<?php
/*! ------------------------------------------------------------------------ *
 *  Functii pentru operatii binare asupra numerelor INT
 *  
 *  @Author: Dumitru Uzun (DUzun)
 *  @Web   : http://duzun.teologie.net/
 *  @Date  : 06.03.2010 
 *      
 * 
 *  ------------------------------------------------------------------------ */
    define('WORD_HI_MASK'     ,  0xFF00                       );
    define('BYTE_MASK'        ,  0xFF                         );
    define('BYTE_SIGN'        ,  0x80                         );
    define('NULL_CHAR'        , "\x00"                        );

    define('INT_SIZE'         ,  PHP_INT_SIZE                 ); // processor word size in bytes
    define('INT_MAX'          ,  PHP_INT_MAX                  ); // processor word size in bytes
    define('INT_BIT_SIZE'     ,  INT_SIZE << 3                ); // processor word size in bits
    define('INT_HALF_BIT_SIZE',  INT_BIT_SIZE >> 1            );
    define('INT_SIGN'         ,  1 << INT_BIT_SIZE-1          );
    define('INT_HALF_SIGN'    ,  1 << INT_HALF_BIT_SIZE-1     );
    define('INT_MASK'         , -1                            );
    define('INT_HI_MASK'      ,  INT_MASK << INT_HALF_BIT_SIZE);
    define('INT_LO_MASK'      , ~INT_HI_MASK                  );
    define('INT_HO_MASK'      ,  BYTE_MASK << INT_BIT_SIZE-8  ); // mask for INT's highest byte

//     for($int_size_shls = 0, $i = INT_SIZE; ~$i&1; $i>>=1, $int_size_shls++) ;
    define('INT_SIZE_SHLS', min_log2(INT_SIZE));

/*------------------------------------------------------------------------*/
  /*!
   * Aduna doua numere intregi cu transport.
   * @return: $ia + $ib; $carry - carry flag (0|1)
   */
  function add_carry($ia, $ib, &$carry)
  {
      $carry += ($ia & INT_LO_MASK) + ($ib & INT_LO_MASK);  // 1/2 din operatie
      $v  = $carry & INT_LO_MASK;   // rezultatul adunarii fara transport

      $carry >>= INT_HALF_BIT_SIZE; // transportul
      $ia    >>= INT_HALF_BIT_SIZE;
      $ib    >>= INT_HALF_BIT_SIZE;

      $carry += ($ia & INT_LO_MASK) + ($ib & INT_LO_MASK);  // 2/2 din operatie
      $v |= $carry << INT_HALF_BIT_SIZE;

      $carry  >>= INT_HALF_BIT_SIZE;    // transportul
      return $v;
  }
/*------------------------------------------------------------------------*/
    /*! Obtine valoarea binara a numarului $n reprezentata pe $isz octeti */
    function int2bs($n, $isz = INT_SIZE)
    {
       //return pack("i", $n);
      switch($isz)
      {
        case 2: return chr($n & BYTE_MASK) . chr(($n >> 8) & BYTE_MASK);
        case 4: return chr($n & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) ;
        case 8: return chr($n & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) .
                       chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK) . chr(($n >>= 8) & BYTE_MASK);
        case 1: return chr($n & BYTE_MASK);
        default:
          $m = $n >> INT_BIT_SIZE-1; // 0 sau -1, in dependenta de semnul $n
          $r = str_repeat(chr($m & BYTE_MASK), $isz);
          $i = 0;
          while($n != $m)
          {
             $r[$i++] = chr($n & BYTE_MASK);
             $n >>= 8;
          }
          return $r;
      }
    }

    /*! Obtine INT din valoarea binara a $str, pastrand semnul. 
     *  $str poate fi mai mic decat un INT. 
     *  Ultimul octet totdeauna indica semnul.
     */
    function bs2int($str)
    {
        $l = strlen($str);
        if(!$l) return 0;
        $r = $l < INT_SIZE && (ord($str[$l-1]) & BYTE_SIGN) ? -1 : 0;
        $l--;
        do {
           $r <<= 8;
           $r |= ord($str[$l]);
        } while($l--);
        return $r;
    }

/*------------------------------------------------------------------------*/
    /*! Obtine valoarea binara a numarului $n si o scrie in $str pe pozitai $pos */
    function int2binstr(&$str, $pos=0, $n, $isz = INT_SIZE)
    {
       //return pack("i", $n);
       $l = strlen($str);
       if($l <= $pos+$isz) $str .= str_repeat(NULL_CHAR, $pos+$isz-$l);
      switch($isz)
      {
        case 2: $str[$pos++] = chr($n & BYTE_MASK); $str[$pos++] = chr(($n >> 8) & BYTE_MASK); break;

        case 4: $str[$pos++] = chr($n & BYTE_MASK);         $str[$pos++] = chr(($n >>= 8) & BYTE_MASK);
                $str[$pos++] = chr(($n >>= 8) & BYTE_MASK); $str[$pos++] = chr(($n >>= 8) & BYTE_MASK); break;

        case 8: $str[$pos++] = chr($n & BYTE_MASK);         $str[$pos++] = chr(($n >>= 8) & BYTE_MASK);
                $str[$pos++] = chr(($n >>= 8) & BYTE_MASK); $str[$pos++] = chr(($n >>= 8) & BYTE_MASK);
                $str[$pos++] = chr(($n >>= 8) & BYTE_MASK); $str[$pos++] = chr(($n >>= 8) & BYTE_MASK);
                $str[$pos++] = chr(($n >>= 8) & BYTE_MASK); $str[$pos++] = chr(($n >>= 8) & BYTE_MASK);
        case 1: $str[$pos] = chr($n & BYTE_MASK); break;

        default:
          $m = $n >> INT_BIT_SIZE-1; // 0 sau -1, in dependenta de semnul $n
          $l = $pos+$isz;
          while($n != $m)
          {
             $str[$pos++] = chr($n & BYTE_MASK);
             $n >>= 8;
          }
          while($pos < $l) $str[$pos++] = $m;
      }
    }

    /*! Obtine INT din valoarea binara de pe pozitia $pos si de lungimea $isz din $str */
    function binstr2int(&$str, $pos=0, $isz=INT_SIZE)
    {
        //$r = unpack("i", substr($str, $pos, INT_SIZE)); return reset($r);
       switch($isz)
       {
         case 2: return ord($str[$pos++]) | ord($str[$pos]) << 8 ;
         case 4: return ord($str[$pos++]) | ord($str[$pos++]) << 8 | ord($str[$pos++]) << 16 | ord($str[$pos]) << 24;
         case 8: return ord($str[$pos++]) | ord($str[$pos++]) << 8 | ord($str[$pos++]) << 16 | ord($str[$pos++]) << 24 |
                        ord($str[$pos++]) << 32 | ord($str[$pos++]) << 40 | ord($str[$pos++]) << 48 | ord($str[$pos]) << 56;
         case 1: return ord($str[$pos]);
         default:
           $r = 0;
           while($isz--)
           {
              $r <<= 8;
              $r |= ord($str[$pos+$isz]);
           }
           return $r;
       }
    }
/*------------------------------------------------------------------------*/
    /*! Obtine numar INT din reprezentarea binara (textuala) */
    function bin2int($str, $sz=INT_BIT_SIZE)
    {
       $i = strlen($str);
       if($i>$sz) $i = $sz;
       $r = 0;
       while($i--) { $r <<= 1; $r |= $str[$i] != '0'; }
       return $r;
    }

    /*! Obtine reprezentarea binara (textuala) din numarul INT */
    function int2bin($n, $sz=INT_BIT_SIZE)
    {
       $r = '';
       while($sz--) { $r .= 1 & $n; $n >>= 1; }
       return $r;
    }

    function bin2byte($s) { return bin2int($s, 8); }
    function byte2bin($b) { return int2bin($b, 8); }
/*------------------------------------------------------------------------*/
    /// Analogic cu bin2hex, dor pentru baza 8
    function bin2oct($str)
    {
       return sprintf("%04o", ord($str));
    }
/*------------------------------------------------------------------------*/
  /// Verifica daca numarul INT $i este o putere a lui 2
  function is_pow2($i)
  {
     return !( $i & ($i - 1) );
  }
  
  /// max{n}: 2^n <= i
  function max_log2($i)
  {
     if($i == 0) return false; // caz foarte rar intalnit
	 for($n=0; ~$i&1; $i>>=1, $n++) ;
     return $n;
  }

  /// min{n}: 2^n >= i
  function min_log2($i)
  {
     if($i < 0) return false; // caz foarte rar intalnit
     for($n=0; $i; $i>>=1, $n++) ;
     return $n;
  }

/*------------------------------------------------------------------------*/
/*!
 * Returneaza un string, reprezentand numarul $nr in forma binara.
 * $fill este separatorul semioctetilor.
 * $oct, daca e specificat, indica nr de octeti de reprezentat $nr.
 * Daca $oct nu e specificat ($oct=0), se reprezinta pe min semiocteti posibil.   
 */ 
function num_bits($nr, $fill=' ', $oct=PHP_INT_SIZE)
{
   $r = ''; // pentru return
   $l = 0;  // lungimea numarului in baza 2
   $m = $nr >= 0 ? 0 : ~0; // maska - conditie de iesire
   $oct <<= 3; // un octet are 8 bitsi
   do {
      $r = ($nr&1) . $r;       // adaugarea cifrelor la sirul raspuns
      $nr >>= 1;
      $l++;               // 
      if( !($l&3) ) $r = "{$fill}$r"; // $fill este caracterul de separare a semioctetilor     
   }
   while($nr ^ $m && $l != $oct) ;

   $m &= 1;
   $n = $oct ? $oct : $l;
   $n = (($n-1) | 3) + 1;         // alinierea din 4 in 4 biti
   while($n > $l++) {
      $r = "$m{$r}"; // adaugarea de '0' pentru aliniere
      if( !($l&3) ) $r = "{$fill}$r"; // $fill este caracterul de separare a semioctetilor     
   }
   return $r;
   return substr($r, $l - $n);
}
/*------------------------------------------------------------------------*/
  /*! Obtine cifra pentru valoarea $nm (in baza 36) */
  function val2digi($nm) { return chr((($r=$nm+0x30)>0x39)?$r+0x07:$r) ; }
  /* Pentru baza 16:  function val2digi($nm) { return chr((($r=$nm|0x30)>0x39)?$r+0x07:$r) ; } */
  
  /*! Obtine valoarea cifrei $ch (in baza 36) */
  function digi2val($ch)
  {
    $ch = ord($ch[0]);
    if(0x2F<$ch && $ch<0x3A) return $ch-0x30; // cifrele zecimale  
    $ch |= 0x20; // fortam minuscula
    if(0x60<$ch && $ch<0x7B) return $ch-0x57; // alfabetul latin  
    return false; // nu exista asa cifra
  }  
  
/*------------------------------------------------------------------------*/
  /*! Nr de cifre ale numarului $val in baza $radix */
  function nr_cif($val, $radix=10)
  {
     return floor(log((int)$val, (int)$radix)+1);
  }
  
/*------------------------------------------------------------------------*/
  /*! Cate cifre are PHP_INT_MAX in baza $radix. */
  function int_nr_max_digits($radix)
  {
     return floor(log(PHP_INT_MAX, (int)$radix)+1) ;
     return floor(INT_BIT_SIZE * log(2, (int)$radix)) ;
  }
  /*! Analogic cu precedenta, insa e buna pentru numere mai mari ca 210 */
  function int_nr_max_digits_($radix)
  {
     if($radix < 2) return INF;
     $i = PHP_INT_MAX;
     $n = 0;
     do { $i = floor($i/$radix); $n++; } while($i);
     return $n;
  }   
  function int_nr_max_digits__($radix)
  {
     return strlen(int_to_base(PHP_INT_MAX, $radix));
  }
  
/*------------------------------------------------------------------------*/
  /*!
   * Converteste un INT in STRING in baza $radix.
   *   2 <= $radix <= 36
   *   // base_convert
   */
  function int_to_base($n, $radix) 
  {
     if($radix < 2 || 36 < $radix) return false;
     if($radix == 10) return $n.'';
     $s = $n < 0 ? '-' : '';
     $r = '';
     do {
        $o = $n % $radix;
        $r = val2digi(abs($o)) . $r;
        $n = floor(($n-$o) / $radix);
     } while($n);
     return $s.$r;
  }
  
  /*!
   * Converteste un numar STRING in baza $radix in INT
   *   2 <= $radix <= 36
   */
  function base_to_int($str, $radix)
  {
     $l = strlen($str);
     if(!$l) return 0;
     $i = 0;
     $s = 1;
     if($str[$i] == '-')
     {
        $i++;
        $s = -1;
     } elseif($str[$i]=='+') $i++;
     $r = 0;
     for(; $i<$l; $i++)
     {
        $r *= $radix;
        $r += digi2val($str[$i]);
     }
     return $s * $r;
  }
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
?>
