tigersum/lib/freetiger/php/Uint64.php

721 lines
26 KiB
PHP

<?php
/**
* Copyright (c) 2012 Francisco Blas Izquierdo Riera (klondike)
* The Tiger algorithm was written by Eli Biham and Ross Anderson and is
* available on the official Tiger algorithm page.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* the algorithm authorsip notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* 4. If this license is not appropriate for you please write me at
* klondike ( a t ) klondike ( d o t ) es to negotiate another license.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
/**
* This class provides 64-bit long unsigned integers in PHP taking care of the
* odd way in which PHP takes care of wrapping when overflows happen and the
* lack of unsigned types.
**/
/**
* It currently supports 64 and 32 bit arches, other arches may be supported in
* the future.
* Take in mind that although code for operations like mul and some special
* operations is provided it hasn't been tested, since they are unneeded for
* tiger and is thus commented to prevent complaints.
**/
//This function provides a logical right shift for ints.
//I still wonder why the PHP guys didn't provide one.
//$v integer value to rotate
//$n integer number of positions to rotate $n must be positive!
function lsr($v, $n) {
return ((int)$v >> (int)$n) & ((PHP_INT_MAX >> (int)$n) | (1 << (((PHP_INT_SIZE * 8) - 1) - (int)$n)));
}
//This class is the responsible off doing overflow aware unsigned airthmethic
if (PHP_INT_SIZE == 8) {
class Uint64 {
private $val;
static private function mask($n) {
return (((int)$n) & 0xffffffff);
}
private function glh() {
return self::mask($this->val >> 32);
}
private function grh() {
return self::mask($this->val);
}
private function joinhalves($hl, $hr) {
return ($hl << 32 | self::mask($hr));
}
private function notp(&$rv) {
$rv = ~($this->val);
}
private function xorp(Uint64 $n, &$rv) {
$rv = ($this->val ^ $n->val);
}
private function andp(Uint64 $n, &$rv) {
$rv = ($this->val & $n->val);
}
private function orp(Uint64 $n, &$rv) {
$rv = ($this->val | $n->val);
}
private function shrp(Uint64 $n, &$rv) {
if ($n->val > 63 || $n->val < 0) {
$rvl = 0;
$rvr = 0;
} else {
$this->shrip($n->val,$rvl,$rvr);
}
}
private function shlp(Uint64 $n, &$rv) {
if ($n->val > 63 || $n->val < 0) {
$rvl = 0;
$rvr = 0;
} else {
$this->shlip($n->val,$rvl,$rvr);
}
}
//Comodity for ints
private function shrip($n, &$rv) {
$rv = lsr($this->val,$n);
}
private function shlip($n, &$rv) {
$rv = ($this->val << $n);
}
private function addp(Uint64 $n, &$rv) {
$vr = $this->grh() + $n->grh(); // Never overflows xD
$vl = $this->glh() + $n->glh() + ($vr >> 32); // Add with overflow
//The constructor takes care of masking
$rv = self::joinhalves($vl,$vr);
}
private function subp(Uint64 $n, &$rv) {
$vr = $this->grh() - $n->grh(); // Never overflows xD
$vl = $this->glh() - $n->glh() - (($vr >> 32) & 1); // Add with overflow
//The constructor takes care of masking
$rv = self::joinhalves($vl,$vr);
}
// private function mulp(Uint64 $n, &$rv) {
// // We divide in three parts of around 22 bits each
// $mask = 0x3fffff; // 22 ones
// $tvr = ($this->val & $mask);
// $tvm = (($this->val >> 22) & $mask);
// $tvl = (($this->val >> 44) & $mask);
// $nvr = ($n->val & $mask);
// $nvm = (($n->val >> 22) & $mask);
// $nvl = (($n->val >> 44) & $mask);
// //We multiply them, the results we get will be of double the size (44 bits < 63) thus overflows are impossible
// $v0 = $nvr * $tvr; // The lsb is at pos 0
// $o01 = $nvm * $tvr; // The lsb is at pos 22
// $o02 = $nvl * $tvr; // The lsb is at pos 44
// $o10 = $nvr * $tvm; // The lsb is at pos 22
// $o11 = $nvm * $tvm; // The lsb is at pos 44
// $o20 = $nvr * $tvl; // The lsb is at pos 44
// //We now add the shifted results (propagating the up to 24 bit carries)
// $v1 = $o01 + $o10 + ($v0 >> 22); // The lsb is at pos 22
// $v2 = $o02 + $o11 + $o20 + ($v1 >> 22); // The lsb is at pos 44
// //Mask extra bits
// $v0 &= mask;
// $v1 &= mask;
// //Build the value
// $rv = ($v0 | ($v1 << 22) | ($v2 << 44));
// }
public function gb($n) {
return (($this->val>>($n*8))&0xff);
}
//Standard operations, return a new copy of the object
public function not_() {
$this->notp($ni);
return new Uint64 ($ni);
}
public function xor_(Uint64 $n) {
$this->xorp($n,$ni);
return new Uint64 ($ni);
}
public function and_(Uint64 $n) {
$this->andp($n,$ni);
return new Uint64 ($ni);
}
public function or_(Uint64 $n) {
$this->orp($n,$ni);
return new Uint64 ($ni);
}
public function shr_(Uint64 $n) {
$this->shrp($n,$ni);
return new Uint64 ($ni);
}
public function shl_(Uint64 $n) {
$this->shlp($n,$ni);
return new Uint64 ($ni);
}
//Comodity for ints
public function shri($n) {
$this->shrip($n,$ni);
return new Uint64 ($ni);
}
public function shli($n) {
$this->shlip($n,$ni);
return new Uint64 ($ni);
}
public function add_(Uint64 $n) {
$this->addp($n,$ni);
return new Uint64 ($ni);
}
public function sub_(Uint64 $n) {
$this->subp($n,$ni);
return new Uint64 ($ni);
}
// public function mul_(Uint64 $n) {
// $this->mulp($n,$ni);
// return new Uint64 ($ni);
// }
//Assignment operations, assign the result to this object
public function xore(Uint64 $n) {
$this->xorp($n,$this->val);
return $this;
}
public function ande(Uint64 $n) {
$this->andp($n,$this->val);
return $this;
}
public function ore(Uint64 $n) {
$this->orp($n,$this->val);
return $this;
}
public function shre(Uint64 $n) {
$this->shrp($n,$this->val);
return $this;
}
public function shle(Uint64 $n) {
$this->shlp($n,$this->val);
return $this;
}
//Comodity for ints
public function shrie($n) {
$this->shrip($n,$this->val);
return $this;
}
public function shlie($n) {
$this->shlip($n,$this->val);
return $this;
}
public function adde(Uint64 $n) {
$this->addp($n,$this->val);
return $this;
}
public function sube(Uint64 $n) {
$this->subp($n,$this->val);
return $this;
}
// public function mule(Uint64 $n) {
// $this->mulp($n,$this->val);
// return $this;
// }
//
// //Other useful operations
// //Use when the overflow bit is needed
// public function addo(Uint64 $n, &$of) {
// $vr = $this->grh() + $n->grh(); // Never overflows xD
// $vl = $this->glh() + $n->glh() + ($vr >> 32); // Add with overflow
// //The constructor takes care of masking
// return new Uint64 ($vl,$vr);
// }
//
// //Use when the overflow bit is needed
// public function subo(Uint64 $n, &$of) {
// $vr = $this->grh() - $n->grh(); // Never overflows xD
// $vl = $this->glh() - $n->glh() - (($vr >> 32) & 1); // Add with overflow
// $of = (($vl >> 32)&1);
// //The constructor takes care of masking
// return new Uint64 ($vl,$vr);
// }
//
// // Returns two Uint64 the first one is the least significative the right one is the most
// public function mul2_(Uint64 $n) {
// // We divide in three parts of around 22 bits each
// $mask = 0x3fffff; // 22 ones
// $tvr = ($this->val & $mask);
// $tvm = (($this->val >> 22) & $mask);
// $tvl = (($this->val >> 44) & $mask);
// $nvr = ($n->val & $mask);
// $nvm = (($n->val >> 22) & $mask);
// $nvl = (($n->val >> 44) & $mask);
// //We multiply them, the results we get will be of double the size (44 bits < 63) thus overflows are impossible
// $v0 = $nvr * $tvr; // The lsb is at pos 0
// $o01 = $nvm * $tvr; // The lsb is at pos 22
// $o02 = $nvl * $tvr; // The lsb is at pos 44
// $o10 = $nvr * $tvm; // The lsb is at pos 22
// $o11 = $nvm * $tvm; // The lsb is at pos 44
// $o12 = $nvl * $tvm; // The lsb is at pos 66
// $o20 = $nvr * $tvl; // The lsb is at pos 44
// $o21 = $nvm * $tvl; // The lsb is at pos 66
// $o22 = $nvl * $tvl; // The lsb is at pos 88
// //We now add the shifted results (propagating the up to 24 bit carries)
// $v1 = $o01 + $o10 + ($v0 >> 22); // The lsb is at pos 22
// $v2 = $o02 + $o11 + $o20 + ($v1 >> 22); // The lsb is at pos 44
// $v3 = $o01 + $o10 + ($v2 >> 22); // The lsb is at pos 66
// $v4 = $o00 + ($v3 >> 22); // The lsb is at pos 88
// //Mask extra bits
// $v0 &= mask;
// $v1 &= mask;
// $v2 &= mask;
// $v3 &= mask;
// //Build the two values now
// $rr = $v0 | ($v1 << 22) | ($v2 << 44);
// $rl = ($v2 >> 20) | ($v2 << 2) | ($v3 << 24);
// return array (new Uint64 ($rr), new Uint64 ($rl));
// }
private function __construct($a) {
$this->val = (int)$a;
}
static public function from_hex($a) {
$hc=array_filter(str_split($a),"ctype_xdigit");
$reduce = function (array $a) {
return array_reduce($a,function($v, $w) {
return (($v << 4) | hexdec($w));
},0);
};
return new Uint64 ($reduce(array_slice($hc,-16,16)));
}
//Little endian string read the bytes with the lsB first
static public function from_les($a) {
return self::from_bes(strrev($a));
}
//Big endian string read the bytes with the msB first
static public function from_bes($a) {
$hc=str_split($a);
$reduce = function (array $a) {
return array_reduce($a,function($v, $w) {
return (($v << 8) | ord($w));
},0);
};
return new Uint64 ($reduce($hc));
}
//Similar to from_les but it assumes each 8 bytes form a different number
static public function arr_from_les($a) {
return array_map('self::from_les',str_split($a,8));
}
//Similar to from_bes but it assumes each 8 bytes form a different number
static public function arr_from_bes($a) {
return array_map('self::from_les',str_split($a,8));
}
//This creates a new Uint64 from an integer setting any unused bits to zero
static public function from_int($a) {
return new Uint64 ($a);
}
}
} else if (PHP_INT_SIZE == 4) {
class Uint64 {
private $vall;
private $valr;
static private function mask($n) {
return (((int)$n) & 0xffff);
}
//Get the MS 16 bits
private function g0() {
return self::mask($this->vall >> 16);
}
//Get the next 16 bits
private function g1() {
return self::mask($this->vall);
}
//Get the next 16 bits
private function g2() {
return self::mask($this->valr >> 16);
}
//Get the LS 16 bits
private function g3() {
return self::mask($this->valr);
}
private function joinhalves($hl, $hr) {
return ($hl << 16 | self::mask($hr));
}
private function notp(&$rvl, &$rvr) {
$rvl = ~($this->vall);
$rvr = ~($this->valr);
}
private function xorp(Uint64 $n, &$rvl, &$rvr) {
$rvl = ($this->vall ^ $n->vall);
$rvr = ($this->valr ^ $n->valr);
}
private function andp(Uint64 $n, &$rvl, &$rvr) {
$rvl = ($this->vall & $n->vall);
$rvr = ($this->valr & $n->valr);
}
private function orp(Uint64 $n, &$rvl, &$rvr) {
$rvl = ($this->vall | $n->vall);
$rvr = ($this->valr | $n->valr);
}
private function shrp(Uint64 $n, &$rvl, &$rvr) {
if ($n->vall || $n->valr > 63 || $n->valr < 0) {
$rvl = 0;
$rvr = 0;
} else {
$this->shrip($n->valr,$rvl,$rvr);
}
}
private function shlp(Uint64 $n, &$rvl, &$rvr) {
if ($n->vall || $n->valr > 63 || $n->valr < 0) {
$rvl = 0;
$rvr = 0;
} else {
$this->shlip($n->valr,$rvl,$rvr);
}
}
//HACK: order here is quite important since we could overwrite our inputs
//Comodity for ints
private function shrip($n, &$rvl, &$rvr) {
$rvr = lsr($this->valr,$n) | ($this->vall << (32 - $n));
$rvl = lsr($this->vall,$n);
}
private function shlip($n, &$rvl, &$rvr) {
$rvl = ($this->vall << $n) | lsr($this->valr,32 - $n);
$rvr = ($this->valr << $n);
}
private function addp(Uint64 $n, &$rvl, &$rvr) {
$v3 = $this->g3() + $n->g3(); // Never overflows xD
$v2 = $this->g2() + $n->g2() + ($v3 >> 16); // Add with overflow
$v1 = $this->g1() + $n->g1() + ($v2 >> 16); // Add with overflow
$v0 = $this->g0() + $n->g0() + ($v1 >> 16); // Add with overflow
//The constructor takes care of masking
$rvl = self::joinhalves($v0,$v1);
$rvr = self::joinhalves($v2,$v3);
}
private function subp(Uint64 $n, &$rvl, &$rvr) {
$v3 = $this->g3() - $n->g3(); // Never overflows xD
$v2 = $this->g2() - $n->g2() - (($v3 >> 16) & 1); // Add with overflow
$v1 = $this->g1() - $n->g1() - (($v2 >> 16) & 1); // Add with overflow
$v0 = $this->g0() - $n->g0() - (($v1 >> 16) & 1); // Add with overflow
//The constructor takes care of masking
$rvl = self::joinhalves($v0,$v1);
$rvr = self::joinhalves($v2,$v3);
}
// private function mulp(Uint64 $n, &$rvl, &$rvr) {
// // We divide in three parts of around 22 bits each
// $mask = 0x3fffff; // 22 ones
// $tvr = ($this->val & $mask);
// $tvm = (($this->val >> 22) & $mask);
// $tvl = (($this->val >> 44) & $mask);
// $nvr = ($n->val & $mask);
// $nvm = (($n->val >> 22) & $mask);
// $nvl = (($n->val >> 44) & $mask);
// //We multiply them, the results we get will be of double the size (44 bits < 63) thus overflows are impossible
// $v0 = $nvr * $tvr; // The lsb is at pos 0
// $o01 = $nvm * $tvr; // The lsb is at pos 22
// $o02 = $nvl * $tvr; // The lsb is at pos 44
// $o10 = $nvr * $tvm; // The lsb is at pos 22
// $o11 = $nvm * $tvm; // The lsb is at pos 44
// $o20 = $nvr * $tvl; // The lsb is at pos 44
// //We now add the shifted results (propagating the up to 24 bit carries)
// $v1 = $o01 + $o10 + ($v0 >> 22); // The lsb is at pos 22
// $v2 = $o02 + $o11 + $o20 + ($v1 >> 22); // The lsb is at pos 44
// //Mask extra bits
// $v0 &= mask;
// $v1 &= mask;
// //Build the value
// $rv = ($v0 | ($v1 << 22) | ($v2 << 44));
// }
public function gb($n) {
if ($n > 3)
return (($this->vall>>(($n-4)*8))&0xff);
else
return (($this->valr>>($n*8))&0xff);
}
//Standard operations, return a new copy of the object
public function not_() {
$this->notp($ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function xor_(Uint64 $n) {
$this->xorp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function and_(Uint64 $n) {
$this->andp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function or_(Uint64 $n) {
$this->orp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function shr_(Uint64 $n) {
$this->shrp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function shl_(Uint64 $n) {
$this->shlp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
//Comodity for ints
public function shri($n) {
$this->shrip($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function shli($n) {
$this->shlip($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function add_(Uint64 $n) {
$this->addp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
public function sub_(Uint64 $n) {
$this->subp($n,$ni1, $ni2);
return new Uint64 ($ni1, $ni2);
}
// public function mul_(Uint64 $n) {
// $this->mulp($n,$ni1, $ni2);
// return new Uint64 ($ni1, $ni2);
// }
//Assignment operations, assign the result to this object
public function xore(Uint64 $n) {
$this->xorp($n,$this->vall, $this->valr);
return $this;
}
public function ande(Uint64 $n) {
$this->andp($n,$this->vall, $this->valr);
return $this;
}
public function ore(Uint64 $n) {
$this->orp($n,$this->vall, $this->valr);
return $this;
}
public function shre(Uint64 $n) {
$this->shrp($n,$this->vall, $this->valr);
return $this;
}
public function shle(Uint64 $n) {
$this->shlp($n,$this->vall, $this->valr);
return $this;
}
//Comodity for ints
public function shrie($n) {
$this->shrip($n,$this->vall, $this->valr);
return $this;
}
public function shlie($n) {
$this->shlip($n,$this->vall, $this->valr);
return $this;
}
public function adde(Uint64 $n) {
$this->addp($n,$this->vall, $this->valr);
return $this;
}
public function sube(Uint64 $n) {
$this->subp($n,$this->vall, $this->valr);
return $this;
}
// public function mule(Uint64 $n) {
// $this->mulp($n,$this->vall, $this->valr);
// return $this;
// }
// //Other useful operations
// //Use when the overflow bit is needed
// public function addo(Uint64 $n, &$of) {
// $vr = $this->grh() + $n->grh(); // Never overflows xD
// $vl = $this->glh() + $n->glh() + ($vr >> 32); // Add with overflow
// //The constructor takes care of masking
// return new Uint64 ($vl,$vr);
// }
//
// //Use when the overflow bit is needed
// public function subo(Uint64 $n, &$of) {
// $vr = $this->grh() - $n->grh(); // Never overflows xD
// $vl = $this->glh() - $n->glh() - (($vr >> 32) & 1); // Add with overflow
// $of = (($vl >> 32)&1);
// //The constructor takes care of masking
// return new Uint64 ($vl,$vr);
// }
//
// // Returns two Uint64 the first one is the least significative the right one is the most
// public function mul2_(Uint64 $n) {
// // We divide in three parts of around 22 bits each
// $mask = 0x3fffff; // 22 ones
// $tvr = ($this->val & $mask);
// $tvm = (($this->val >> 22) & $mask);
// $tvl = (($this->val >> 44) & $mask);
// $nvr = ($n->val & $mask);
// $nvm = (($n->val >> 22) & $mask);
// $nvl = (($n->val >> 44) & $mask);
// //We multiply them, the results we get will be of double the size (44 bits < 63) thus overflows are impossible
// $v0 = $nvr * $tvr; // The lsb is at pos 0
// $o01 = $nvm * $tvr; // The lsb is at pos 22
// $o02 = $nvl * $tvr; // The lsb is at pos 44
// $o10 = $nvr * $tvm; // The lsb is at pos 22
// $o11 = $nvm * $tvm; // The lsb is at pos 44
// $o12 = $nvl * $tvm; // The lsb is at pos 66
// $o20 = $nvr * $tvl; // The lsb is at pos 44
// $o21 = $nvm * $tvl; // The lsb is at pos 66
// $o22 = $nvl * $tvl; // The lsb is at pos 88
// //We now add the shifted results (propagating the up to 24 bit carries)
// $v1 = $o01 + $o10 + ($v0 >> 22); // The lsb is at pos 22
// $v2 = $o02 + $o11 + $o20 + ($v1 >> 22); // The lsb is at pos 44
// $v3 = $o01 + $o10 + ($v2 >> 22); // The lsb is at pos 66
// $v4 = $o00 + ($v3 >> 22); // The lsb is at pos 88
// //Mask extra bits
// $v0 &= mask;
// $v1 &= mask;
// $v2 &= mask;
// $v3 &= mask;
// //Build the two values now
// $rr = $v0 | ($v1 << 22) | ($v2 << 44);
// $rl = ($v2 >> 20) | ($v2 << 2) | ($v3 << 24);
// return array (new Uint64 ($rr), new Uint64 ($rl));
// }
private function __construct($lv, $rv) {
$this->vall = (int)$lv;
$this->valr = (int)$rv;
}
static public function from_hex($a) {
$hc=array_filter(str_split($a),"ctype_xdigit");
$reduce = function (array $a) {
return array_reduce($a,function($v, $w) {
return (($v << 4) | hexdec($w));
},0);
};
return new Uint64 ($reduce(array_slice($hc,-16,8)),$reduce(array_slice($hc,-8,8)));
}
//Little endian string read the bytes with the lsB first
static public function from_les($a) {
return self::from_bes(strrev($a));
}
//Big endian string read the bytes with the msB first
static public function from_bes($a) {
$hc=str_split($a);
$reduce = function (array $a) {
return array_reduce($a,function($v, $w) {
return (($v << 8) | ord($w));
},0);
};
return new Uint64 ($reduce(array_slice($hc,-8,4)),$reduce(array_slice($hc,-4,4)));
}
//Similar to from_les but it assumes each 8 bytes form a different number
static public function arr_from_les($a) {
return array_map('self::from_les',str_split($a,8));
}
//Similar to from_bes but it assumes each 8 bytes form a different number
static public function arr_from_bes($a) {
return array_map('self::from_les',str_split($a,8));
}
//This creates a new Uint64 from an integer setting any unused bits to zero
static public function from_int($a) {
return new Uint64 (0,$a);
}
}
}
?>