@ -266,3 +266,132 @@ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow)
* plow = * plow < < shift ;
}
}
/*
* Unsigned 256 - by - 128 division .
* Returns the remainder via r .
* Returns lower 128 bit of quotient .
* Needs a normalized divisor ( most significant bit set to 1 ) .
*
* Adapted from include / qemu / host - utils . h udiv_qrnnd ,
* from the GNU Multi Precision Library - longlong . h __udiv_qrnnd
* ( https : //gmplib.org/repo/gmp/file/tip/longlong.h)
*
* Licensed under the GPLv2 / LGPLv3
*/
static Int128 udiv256_qrnnd ( Int128 * r , Int128 n1 , Int128 n0 , Int128 d )
{
Int128 d0 , d1 , q0 , q1 , r1 , r0 , m ;
uint64_t mp0 , mp1 ;
d0 = int128_make64 ( int128_getlo ( d ) ) ;
d1 = int128_make64 ( int128_gethi ( d ) ) ;
r1 = int128_remu ( n1 , d1 ) ;
q1 = int128_divu ( n1 , d1 ) ;
mp0 = int128_getlo ( q1 ) ;
mp1 = int128_gethi ( q1 ) ;
mulu128 ( & mp0 , & mp1 , int128_getlo ( d0 ) ) ;
m = int128_make128 ( mp0 , mp1 ) ;
r1 = int128_make128 ( int128_gethi ( n0 ) , int128_getlo ( r1 ) ) ;
if ( int128_ult ( r1 , m ) ) {
q1 = int128_sub ( q1 , int128_one ( ) ) ;
r1 = int128_add ( r1 , d ) ;
if ( int128_uge ( r1 , d ) ) {
if ( int128_ult ( r1 , m ) ) {
q1 = int128_sub ( q1 , int128_one ( ) ) ;
r1 = int128_add ( r1 , d ) ;
}
}
}
r1 = int128_sub ( r1 , m ) ;
r0 = int128_remu ( r1 , d1 ) ;
q0 = int128_divu ( r1 , d1 ) ;
mp0 = int128_getlo ( q0 ) ;
mp1 = int128_gethi ( q0 ) ;
mulu128 ( & mp0 , & mp1 , int128_getlo ( d0 ) ) ;
m = int128_make128 ( mp0 , mp1 ) ;
r0 = int128_make128 ( int128_getlo ( n0 ) , int128_getlo ( r0 ) ) ;
if ( int128_ult ( r0 , m ) ) {
q0 = int128_sub ( q0 , int128_one ( ) ) ;
r0 = int128_add ( r0 , d ) ;
if ( int128_uge ( r0 , d ) ) {
if ( int128_ult ( r0 , m ) ) {
q0 = int128_sub ( q0 , int128_one ( ) ) ;
r0 = int128_add ( r0 , d ) ;
}
}
}
r0 = int128_sub ( r0 , m ) ;
* r = r0 ;
return int128_or ( int128_lshift ( q1 , 64 ) , q0 ) ;
}
/*
* Unsigned 256 - by - 128 division .
* Returns the remainder .
* Returns quotient via plow and phigh .
* Also returns the remainder via the function return value .
*/
Int128 divu256 ( Int128 * plow , Int128 * phigh , Int128 divisor )
{
Int128 dhi = * phigh ;
Int128 dlo = * plow ;
Int128 rem , dhighest ;
int sh ;
if ( ! int128_nz ( divisor ) | | ! int128_nz ( dhi ) ) {
* plow = int128_divu ( dlo , divisor ) ;
* phigh = int128_zero ( ) ;
return int128_remu ( dlo , divisor ) ;
} else {
sh = clz128 ( divisor ) ;
if ( int128_ult ( dhi , divisor ) ) {
if ( sh ! = 0 ) {
/* normalize the divisor, shifting the dividend accordingly */
divisor = int128_lshift ( divisor , sh ) ;
dhi = int128_or ( int128_lshift ( dhi , sh ) ,
int128_urshift ( dlo , ( 128 - sh ) ) ) ;
dlo = int128_lshift ( dlo , sh ) ;
}
* phigh = int128_zero ( ) ;
* plow = udiv256_qrnnd ( & rem , dhi , dlo , divisor ) ;
} else {
if ( sh ! = 0 ) {
/* normalize the divisor, shifting the dividend accordingly */
divisor = int128_lshift ( divisor , sh ) ;
dhighest = int128_rshift ( dhi , ( 128 - sh ) ) ;
dhi = int128_or ( int128_lshift ( dhi , sh ) ,
int128_urshift ( dlo , ( 128 - sh ) ) ) ;
dlo = int128_lshift ( dlo , sh ) ;
* phigh = udiv256_qrnnd ( & dhi , dhighest , dhi , divisor ) ;
} else {
/*
* dhi > = divisor
* Since the MSB of divisor is set ( sh = = 0 ) ,
* ( dhi - divisor ) < divisor
*
* Thus , the high part of the quotient is 1 , and we can
* calculate the low part with a single call to udiv_qrnnd
* after subtracting divisor from dhi
*/
dhi = int128_sub ( dhi , divisor ) ;
* phigh = int128_one ( ) ;
}
* plow = udiv256_qrnnd ( & rem , dhi , dlo , divisor ) ;
}
/*
* since the dividend / divisor might have been normalized ,
* the remainder might also have to be shifted back
*/
rem = int128_urshift ( rem , sh ) ;
return rem ;
}
}