<?php
/*
 * PHP Number Base Conversion Functions
 * Version 1.0 - February 2004
 * Version 2.0 - January 2005 - converted to using bcmath
 * Version 2.1 - September 2005 - added decimal point conversion ability
 * Version 2.2 - November 2009 - added base63
 * Version 2.3 - June 2019 - added negative number handling
 * Version 2.4 - April 2020 - fixed some PHP deprecated behaviour
 * (c) 2004-2020 Paul Gregg <pgregg@pgregg.com>
 * http://www.pgregg.com
 *
 * Function: Arbitrary Number Base conversion from base 2 - 64
 * This file should be included by other php scripts
 * For normal base 2 - 36 conversion use the built in base_convert function
 *
 * Open Source Code:   If you use this code on your site for public
 * access (i.e. on the Internet) then you must attribute the author and
 * source web site: http://www.pgregg.com/projects/
 * You must also make this original source code available for download
 * unmodified or provide a link to the source.  Additionally you must provide
 * the source to any modified or translated versions or derivatives.
 *
 */

Function base_dec2base($iNum$iBase$iScale=0) { // cope with base 2..64
  
$LDEBUG FALSE;
  
$sChars '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
  
$sResult ''// Store the result
  
$isNeg FALSE;

  
// special case for Base64 encoding
  
if ($iBase == 64)
   
$sChars 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

  if (
$iBase == 256) {
    
$sChars '';
    for(
$i=0$i<256$i++) {
      
$sChars .= chr($i);
    }
  }

  if (
$LDEBUG) { print "<br>base_dec2base($iNum$iBase$iScale) called.\n"; }

  
$sNum is_integer($iNum) ? "$iNum: (string)$iNum;
  
$iBase intval($iBase); // incase it is a string or some weird decimal

  // Is the input negative?
  
if (strlen($sNum) && $sNum[0] == '-') {
    
$isNeg TRUE;
    
$sNum substr($sNum1);
  }

  
// Check to see if we are an integer or real number
  
if (strpos($sNum'.') !== FALSE) {
    list (
$sNum$sReal) = explode('.'$sNum2);
    
$sReal '0.' $sReal;
  } else
    
$sReal '0';

  if (
$sNum == 0) {
    
$sResult $sChars[0];
  } else {
    while (
bccomp($sNum0$iScale) != 0) { // still data to process
      
$sRem bcmod($sNum$iBase); // calc the remainder
      
$sNum bcdivbcsub($sNum$sRem$iScale), $iBase$iScale );
      
$sResult $sChars[$sRem] . $sResult;
    }
  }
  if (
$sReal != '0') {
    
$sResult .= '.';
    
$fraciScale $iScale;
    while(
$fraciScale-- && bccomp($sReal0$iScale) != 0) { // still data to process
      
if ($LDEBUG) print "<br> -> $sReal * $iBase = ";
      
$sReal bcmul($sReal$iBase$iScale); // multiple the float part with the base
      
if ($LDEBUG) print "$sReal  => ";
      
$sFrac 0;
      if (
bccomp($sReal ,1$iScale) > -1)
        list(
$sFrac$dummy) = explode('.'$sReal2); // get the intval
      
if ($LDEBUG) print "$sFrac\n";
      
$sResult .= $sChars[$sFrac];
      
$sReal bcsub($sReal$sFrac$iScale);
    }
  }

  if (
$isNeg)
    
$sResult '-' $sResult;

  if (
$LDEBUG) { print "<br>base_dec2base($iNum$iBase$iScale) = $sResult\n"; }
  return 
$sResult;
}


Function 
base_base2dec($sNum$iBase=0$iScale=0) {
  
$LDEBUG FALSE;
  
$sChars '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
  
$sResult '';

  if (
$LDEBUG) { print "<br>base_base2dec($sNum$iBase$iScale) called.\n"; }

  
$iBase intval($iBase); // incase it is a string or some weird decimal

  // special case for Base64 encoding
  
if ($iBase == 64)
   
$sChars 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

  
// clean up the input string if it uses particular input formats
  
switch ($iBase) {
    case 
16// remove 0x from start of string
      
if (strtolower(substr($sNum02)) == '0x'$sNum substr($sNum2);
      break;
    case 
8// remove the 0 from the start if it exists - not really required
      
if (strpos($sNum'0')===0$sNum substr($sNum1);
      break;
    case 
2// remove an 0b from the start if it exists
      
if (strtolower(substr($sNum02)) == '0b'$sNum substr($sNum2);
      break;
    case 
64// remove padding chars: =
      
$sNum str_replace('='''$sNum);
      break;
    default: 
// Look for numbers in the format base#number,
             // if so split it up and use the base from it
      
if (strpos($sNum'#') !== false) {
        list (
$sBase$sNum) = explode('#'$sNum2);
        
$iBase intval($sBase);  // take the new base
      
}
      if (
$iBase == 0) {
        print(
"base_base2dec called without a base value and not in base#number format");
        return 
'';
      }
      break;
  }

  
// Convert string to upper case since base36 or less is case insensitive
  
if ($iBase 37$sNum strtoupper($sNum);

  if (
$iBase == 256) {
    
$sChars '';
    for(
$i=0$i<256$i++) {
      
$sChars .= chr($i);
    }
  }

  
// Is the input negative?
  
if ($sNum[0] == '-') {
    
$isNeg TRUE;
    
$sNum substr($sNum1);
  }

  
// Check to see if we are an integer or real number
  
if (strpos($sNum'.') !== FALSE) {
    list (
$sNum$sReal) = explode('.'$sNum2);
    
$sReal '0.' $sReal;
  } else
    
$sReal '0';


  
// By now we know we have a correct base and number
  
$iLen strlen($sNum);
  
  
// Now loop through each digit in the number
  
for ($i=$iLen-1$i>=0$i--) {
    
$sChar $sNum[$i]; // extract the last char from the number
    
$iValue strpos($sChars$sChar); // get the decimal value
    
if ($iValue $iBase) {
      print(
"base_base2dec: $sNum is not a valid base $iBase number");
      return 
'';
    }
    
// Now convert the value+position to decimal
    
$sResult bcadd($sResultbcmul$iValuebcpow($iBase, ($iLen-$i-1))) );
  }

  
// Now append the real part
  
if (strcmp($sReal'0') != 0) {
    
$sReal substr($sReal2); // Chop off the '0.' characters
    
$iLen strlen($sReal);
    for (
$i=0$i<$iLen$i++) {
      
$sChar $sReal[$i]; // extract the first, second, third, etc char
      
$iValue strpos($sChars$sChar); // get the decimal value
      
if ($iValue $iBase) {
        print(
"base_base2dec: $sNum is not a valid base $iBase number");
        return 
'';
      }
      
$sResult bcadd($sResultbcdiv($iValuebcpow($iBase, ($i+1)), $iScale), $iScale);
    }
  }

  if (
$isNeg)
    
$sResult '-' $sResult;

  if (
$LDEBUG) { print "<br>base_base2dec($sNum$iBase$iScale) = $sResult.\n"; }
  return 
$sResult;
}
    
Function 
base_base2base($iNum$iBase$oBase$iScale=0) {

  if (
$iBase != 10$oNum base_base2dec($iNum$iBase$iScale);
  else 
$oNum $iNum;
  
$oNum base_dec2base($oNum$oBase$iScale);
  return 
$oNum;

}

?>