<?php
namespace Api\PhpUtils\Common;
/**
 * Class BaseConvertUtil 10进制数和62进制(包含)以内进制数的互转
 * 对比PHP自带base_convert函数的测试, 对10000个随机数进行进制转换(10进制到36进制, 36进制到10进制)的耗时(单位s):
 * decToOther(10 => 36): 3.4464011192322
 * base_convert(10 => 36): 0.3307638168335
 * otherToDec(36 => 10): 8.2434189319611
 * base_convert(36 => 10): 0.3491358757019
 * 从测试结果来看, 36进制内的转换请尽量使用PHP自带base_convert函数
 */
class BaseConvert
{
    private static $dict = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 十进制数转换成其它进制: 可以转换成2-62任何进制
     *
     * @param int $num
     * @param int $to
     * @return string 转换成功返回转换后的字符串, convertStatus转换失败返回false转换失败返回false
     */
    public static function decToOther($num, $to = 62, &$convertStatus = true)
    {
        if ($to == 10)
        {
            return strval($num);
        }

        if ($to > 62 || $to < 2)
        {
            $convertStatus = false;
            return '';
        }

        $ret = '';
        do
        {
            $mod = bcmod($num . '', $to . '');
            $ret = self::$dict[intval($mod)] . $ret;
            $num = bcdiv($num, $to);
        } while ($num > 0);
        return $ret;
    }

    /**
     * 其它进制数转换成十进制数: 适用2-62的任何进制
     *
     * @param string $num
     * @param int $from
     * @return int|bool 转换成功返回转换后的数值, convertStatus转换失败返回false
     */
    public static function otherToDec($num, $from = 62, &$convertStatus = true)
    {
        if ($from == 10)
        {
            return $num;
        }

        if ($from > 62 || $from < 2)
        {
            $convertStatus = false;
            return 0;
        }

        $num = strval($num);
        $len = strlen($num);
        $dec = 0;
        for ($i = 0; $i < $len; $i++)
        {
            $pos = strpos(self::$dict, $num[$i]);
            if ($pos >= $from)
            {
                $convertStatus = false;// 如果出现非法字符, 比如16进制中出现w、x、y、z等, 直接返回false
                return 0;
            }
            $dec = bcadd(bcmul(bcpow($from, $len - $i - 1), $pos), $dec);
        }
        return $dec;
    }
}
