cairo/src/cairo-wideint.c

1024 lines
21 KiB
C

/*
* $Id: cairo-wideint.c,v 1.4 2005-01-19 15:07:00 cworth Exp $
*
* Copyright © 2004 Keith Packard
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Keith Packard
*
* Contributor(s):
* Keith R. Packard <keithp@keithp.com>
*/
#include "cairoint.h"
#if !HAVE_UINT64_T || !HAVE_UINT128_T
static const unsigned char top_bit[256] =
{
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
};
#endif
#if HAVE_UINT64_T
#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l))
cairo_uquorem64_t
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
{
cairo_uquorem64_t qr;
qr.quo = num / den;
qr.rem = num % den;
return qr;
}
#else
cairo_uint64_t
_cairo_uint32_to_uint64 (uint32_t i)
{
cairo_uint64_t q;
q.lo = i;
q.hi = 0;
return q;
}
cairo_int64_t
_cairo_int32_to_int64 (int32_t i)
{
cairo_uint64_t q;
q.lo = i;
q.hi = i < 0 ? -1 : 0;
return q;
}
static const cairo_uint64_t
_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l)
{
cairo_uint64_t q;
q.lo = l;
q.hi = h;
return q;
}
cairo_uint64_t
_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b)
{
cairo_uint64_t s;
s.hi = a.hi + b.hi;
s.lo = a.lo + b.lo;
if (s.lo < a.lo)
s.hi++;
return s;
}
cairo_uint64_t
_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b)
{
cairo_uint64_t s;
s.hi = a.hi - b.hi;
s.lo = a.lo - b.lo;
if (s.lo > a.lo)
s.hi--;
return s;
}
#define uint32_lo(i) ((i) & 0xffff)
#define uint32_hi(i) ((i) >> 16)
#define uint32_carry16 ((1) << 16)
cairo_uint64_t
_cairo_uint32x32_64_mul (uint32_t a, uint32_t b)
{
cairo_uint64_t s;
uint16_t ah, al, bh, bl;
uint32_t r0, r1, r2, r3;
al = uint32_lo (a);
ah = uint32_hi (a);
bl = uint32_lo (b);
bh = uint32_hi (b);
r0 = (uint32_t) al * bl;
r1 = (uint32_t) al * bh;
r2 = (uint32_t) ah * bl;
r3 = (uint32_t) ah * bh;
r1 += uint32_hi(r0); /* no carry possible */
r1 += r2; /* but this can carry */
if (r1 < r2) /* check */
r3 += uint32_carry16;
s.hi = r3 + uint32_hi(r1);
s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0);
return s;
}
cairo_int64_t
_cairo_int32x32_64_mul (int32_t a, int32_t b)
{
s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t b));
if (a < 0)
s.hi -= b;
if (b < 0)
s.hi -= a;
return s;
}
cairo_uint64_t
_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b)
{
cairo_uint64_t s;
s = _cairo_uint32x32_64_mul (a.lo, b.lo);
s.hi += a.lo * b.hi + a.hi * b.lo;
return s;
}
cairo_uint64_t
_cairo_uint64_lsl (cairo_uint64_t a, int shift)
{
if (shift >= 32)
{
a.hi = a.lo;
a.lo = 0;
shift -= 32;
}
if (shift)
{
a.hi = a.hi << shift | a.lo >> (32 - shift);
a.lo = a.lo << shift;
}
return a;
}
cairo_uint64_t
_cairo_uint64_rsl (cairo_uint64_t a, int shift)
{
if (shift >= 32)
{
a.lo = a.hi;
a.hi = 0;
shift -= 32;
}
if (shift)
{
a.lo = a.lo >> shift | a.hi << (32 - shift);
a.hi = a.hi >> shift;
}
return a;
}
#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n)))
cairo_int64_t
_cairo_uint64_rsa (cairo_int64_t a, int shift)
{
if (shift >= 32)
{
a.lo = a.hi;
a.hi = _cairo_uint32_rsa (a.hi, 31);
shift -= 32;
}
if (shift)
{
a.lo = a.lo >> shift | a.hi << (32 - shift);
a.hi = _cairo_uint32_rsa (a.hi, shift);
}
return a;
}
int
_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b)
{
return (a.hi < b.hi ||
(a.hi == b.hi && a.lo < b.lo));
}
int
_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b)
{
return a.hi == b.hi && a.lo == b.lo;
}
int
_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b)
{
if (_cairo_int64_negative (a) && !_cairo_int64_negative (b))
return 1;
if (!_cairo_int64_negative (a) && _cairo_int64_negative (b))
return 0;
return _cairo_uint64_lt (a, b);
}
cairo_uint64_t
_cairo_uint64_not (cairo_uint64_t a)
{
a.lo = ~a.lo;
a.hi = ~a.hi;
return a;
}
cairo_uint64_t
_cairo_uint64_negate (cairo_uint64_t a)
{
a.lo = ~a.lo;
a.hi = ~a.hi;
if (++a.lo == 0)
++a.hi;
return a;
}
/*
* The design of this algorithm comes from GCC,
* but the actual implementation is new
*/
static const int
_cairo_leading_zeros32 (uint32_t i)
{
int top;
if (i < 0x100)
top = 0;
else if (i < 0x10000)
top = 8;
else if (i < 0x1000000)
top = 16;
else
top = 24;
top = top + top_bit [i >> top];
return 32 - top;
}
typedef struct _cairo_uquorem32_t {
uint32_t quo;
uint32_t rem;
} cairo_uquorem32_t;
/*
* den >= num.hi
*/
static const cairo_uquorem32_t
_cairo_uint64x32_normalized_divrem (cairo_uint64_t num, uint32_t den)
{
cairo_uquorem32_t qr;
uint32_t q0, q1, r0, r1;
uint16_t d0, d1;
uint32_t t;
d0 = den & 0xffff;
d1 = den >> 16;
q1 = num.hi / d1;
r1 = num.hi % d1;
t = q1 * d0;
r1 = (r1 << 16) | (num.lo >> 16);
if (r1 < t)
{
q1--;
r1 += den;
if (r1 >= den && r1 < t)
{
q1--;
r1 += den;
}
}
r1 -= t;
q0 = r1 / d1;
r0 = r1 % d1;
t = q0 * d0;
r0 = (r0 << 16) | (num.lo & 0xffff);
if (r0 < t)
{
q0--;
r0 += den;
if (r0 >= den && r0 < t)
{
q0--;
r0 += den;
}
}
r0 -= t;
qr.quo = (q1 << 16) | q0;
qr.rem = r0;
return qr;
}
cairo_uquorem64_t
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
{
cairo_uquorem32_t qr32;
cairo_uquorem64_t qr;
int norm;
uint32_t q1, q0, r1, r0;
if (den.hi == 0)
{
if (den.lo > num.hi)
{
/* 0q = nn / 0d */
norm = _cairo_leading_zeros32 (den.lo);
if (norm)
{
den.lo <<= norm;
num = _cairo_uint64_lsl (num, norm);
}
q1 = 0;
}
else
{
/* qq = NN / 0d */
if (den.lo == 0)
den.lo = 1 / den.lo;
norm = _cairo_leading_zeros32 (den.lo);
if (norm)
{
cairo_uint64_t num1;
den.lo <<= norm;
num1 = _cairo_uint64_rsl (num, 32 - norm);
qr32 = _cairo_uint64x32_normalized_divrem (num1, den.lo);
q1 = qr32.quo;
num.hi = qr32.rem;
num.lo <<= norm;
}
else
{
num.hi -= den.lo;
q1 = 1;
}
}
qr32 = _cairo_uint64x32_normalized_divrem (num, den.lo);
q0 = qr32.quo;
r1 = 0;
r0 = qr32.rem >> norm;
}
else
{
if (den.hi > num.hi)
{
/* 00 = nn / DD */
q0 = q1 = 0;
r0 = num.lo;
r1 = num.hi;
}
else
{
/* 0q = NN / dd */
norm = _cairo_leading_zeros32 (den.hi);
if (norm == 0)
{
if (num.hi > den.hi || num.lo >= den.lo)
{
q0 = 1;
num = _cairo_uint64_sub (num, den);
}
else
q0 = 0;
q1 = 0;
r0 = num.lo;
r1 = num.hi;
}
else
{
cairo_uint64_t num1;
cairo_uint64_t part;
num1 = _cairo_uint64_rsl (num, 32 - norm);
den = _cairo_uint64_lsl (den, norm);
qr32 = _cairo_uint64x32_normalized_divrem (num1, den.hi);
part = _cairo_uint32x32_64_mul (qr32.quo, den.lo);
q0 = qr32.quo;
num.lo <<= norm;
num.hi = qr32.rem;
if (_cairo_uint64_gt (part, num))
{
q0--;
part = _cairo_uint64_sub (part, den);
}
q1 = 0;
num = _cairo_uint64_sub (num, part);
num = _cairo_uint64_rsl (num, norm);
r0 = num.lo;
r1 = num.hi;
}
}
}
qr.quo.lo = q0;
qr.quo.hi = q1;
qr.rem.lo = r0;
qr.rem.hi = r1;
return qr;
}
#endif /* !HAVE_UINT64_T */
cairo_quorem64_t
_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den)
{
int num_neg = _cairo_int64_negative (num);
int den_neg = _cairo_int64_negative (den);
cairo_uquorem64_t uqr;
cairo_quorem64_t qr;
if (num_neg)
num = _cairo_int64_negate (num);
if (den_neg)
den = _cairo_int64_negate (den);
uqr = _cairo_uint64_divrem (num, den);
if (num_neg)
qr.rem = _cairo_int64_negate (uqr.rem);
else
qr.rem = uqr.rem;
if (num_neg != den_neg)
qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo);
else
qr.quo = (cairo_int64_t) uqr.quo;
return qr;
}
#if HAVE_UINT128_T
cairo_uquorem128_t
_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
{
cairo_uquorem128_t qr;
qr.quo = num / den;
qr.rem = num % den;
return qr;
}
#else
cairo_uint128_t
_cairo_uint32_to_uint128 (uint32_t i)
{
cairo_uint128_t q;
q.lo = _cairo_uint32_to_uint64 (i);
q.hi = _cairo_uint32_to_uint64 (0);
return q;
}
cairo_int128_t
_cairo_int32_to_int128 (int32_t i)
{
cairo_int128_t q;
q.lo = _cairo_int32_to_int64 (i);
q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0);
return q;
}
cairo_uint128_t
_cairo_uint64_to_uint128 (cairo_uint64_t i)
{
cairo_uint128_t q;
q.lo = i;
q.hi = _cairo_uint32_to_uint64 (0);
return q;
}
cairo_int128_t
_cairo_int64_to_int128 (cairo_int64_t i)
{
cairo_int128_t q;
q.lo = i;
q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0);
return q;
}
cairo_uint128_t
_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t s;
s.hi = _cairo_uint64_add (a.hi, b.hi);
s.lo = _cairo_uint64_add (a.lo, b.lo);
if (_cairo_uint64_lt (s.lo, a.lo))
s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1));
return s;
}
cairo_uint128_t
_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t s;
s.hi = _cairo_uint64_sub (a.hi, b.hi);
s.lo = _cairo_uint64_sub (a.lo, b.lo);
if (_cairo_uint64_gt (s.lo, a.lo))
s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1));
return s;
}
#if HAVE_UINT64_T
#define uint64_lo32(i) ((i) & 0xffffffff)
#define uint64_hi32(i) ((i) >> 32)
#define uint64_lo(i) ((i) & 0xffffffff)
#define uint64_hi(i) ((i) >> 32)
#define uint64_shift32(i) ((i) << 32)
#define uint64_carry32 (((uint64_t) 1) << 32)
#else
#define uint64_lo32(i) ((i).lo)
#define uint64_hi32(i) ((i).hi)
static const cairo_uint64_t
uint64_lo (cairo_uint64_t i)
{
cairo_uint64_t s;
s.lo = i.lo;
s.hi = 0;
return s;
}
static const cairo_uint64_t
uint64_hi (cairo_uint64_t i)
{
cairo_uint64_t s;
s.lo = i.hi;
s.hi = 0;
return s;
}
static const cairo_uint64_t
uint64_shift32 (cairo_uint64_t i)
{
cairo_uint64_t s;
s.lo = 0;
s.hi = i.lo;
return s;
}
static const cairo_uint64_t uint64_carry32 = { 0, 1 };
#endif
cairo_uint128_t
_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b)
{
cairo_uint128_t s;
uint32_t ah, al, bh, bl;
cairo_uint64_t r0, r1, r2, r3;
al = uint64_lo32 (a);
ah = uint64_hi32 (a);
bl = uint64_lo32 (b);
bh = uint64_hi32 (b);
r0 = _cairo_uint32x32_64_mul (al, bl);
r1 = _cairo_uint32x32_64_mul (al, bh);
r2 = _cairo_uint32x32_64_mul (ah, bl);
r3 = _cairo_uint32x32_64_mul (ah, bh);
r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */
r1 = _cairo_uint64_add (r1, r2); /* but this can carry */
if (_cairo_uint64_lt (r1, r2)) /* check */
r3 = _cairo_uint64_add (r3, uint64_carry32);
s.hi = _cairo_uint64_add (r3, uint64_hi(r1));
s.lo = _cairo_uint64_add (uint64_shift32 (r1),
uint64_lo (r0));
return s;
}
cairo_int128_t
_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b)
{
cairo_int128_t s;
s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a),
_cairo_int64_to_uint64(b));
if (_cairo_int64_negative (a))
s.hi = _cairo_uint64_sub (s.hi,
_cairo_int64_to_uint64 (b));
if (_cairo_int64_negative (b))
s.hi = _cairo_uint64_sub (s.hi,
_cairo_int64_to_uint64 (a));
return s;
}
cairo_uint128_t
_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t s;
s = _cairo_uint64x64_128_mul (a.lo, b.lo);
s.hi = _cairo_uint64_add (s.hi,
_cairo_uint64_mul (a.lo, b.hi));
s.hi = _cairo_uint64_add (s.hi,
_cairo_uint64_mul (a.hi, b.lo));
return s;
}
cairo_uint128_t
_cairo_uint128_lsl (cairo_uint128_t a, int shift)
{
if (shift >= 64)
{
a.hi = a.lo;
a.lo = _cairo_uint32_to_uint64 (0);
shift -= 64;
}
if (shift)
{
a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift),
_cairo_uint64_rsl (a.lo, (64 - shift)));
a.lo = _cairo_uint64_lsl (a.lo, shift);
}
return a;
}
cairo_uint128_t
_cairo_uint128_rsl (cairo_uint128_t a, int shift)
{
if (shift >= 64)
{
a.lo = a.hi;
a.hi = _cairo_uint32_to_uint64 (0);
shift -= 64;
}
if (shift)
{
a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
_cairo_uint64_lsl (a.hi, (64 - shift)));
a.hi = _cairo_uint64_rsl (a.hi, shift);
}
return a;
}
cairo_uint128_t
_cairo_uint128_rsa (cairo_int128_t a, int shift)
{
if (shift >= 64)
{
a.lo = a.hi;
a.hi = _cairo_uint64_rsa (a.hi, 64-1);
shift -= 64;
}
if (shift)
{
a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
_cairo_uint64_lsl (a.hi, (64 - shift)));
a.hi = _cairo_uint64_rsa (a.hi, shift);
}
return a;
}
int
_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b)
{
return (_cairo_uint64_lt (a.hi, b.hi) ||
(_cairo_uint64_eq (a.hi, b.hi) &&
_cairo_uint64_lt (a.lo, b.lo)));
}
int
_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b)
{
if (_cairo_int128_negative (a) && !_cairo_int128_negative (b))
return 1;
if (!_cairo_int128_negative (a) && _cairo_int128_negative (b))
return 0;
return _cairo_uint128_lt (a, b);
}
int
_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b)
{
return (_cairo_uint64_eq (a.hi, b.hi) &&
_cairo_uint64_eq (a.lo, b.lo));
}
/*
* The design of this algorithm comes from GCC,
* but the actual implementation is new
*/
/*
* den >= num.hi
*/
static cairo_uquorem64_t
_cairo_uint128x64_normalized_divrem (cairo_uint128_t num, cairo_uint64_t den)
{
cairo_uquorem64_t qr64;
cairo_uquorem64_t qr;
uint32_t q0, q1;
cairo_uint64_t r0, r1;
uint32_t d0, d1;
cairo_uint64_t t;
d0 = uint64_lo32 (den);
d1 = uint64_hi32 (den);
qr64 = _cairo_uint64_divrem (num.hi, _cairo_uint32_to_uint64 (d1));
q1 = _cairo_uint64_to_uint32 (qr64.quo);
r1 = qr64.rem;
t = _cairo_uint32x32_64_mul (q1, d0);
r1 = _cairo_uint64_add (_cairo_uint64_lsl (r1, 32),
_cairo_uint64_rsl (num.lo, 32));
if (_cairo_uint64_lt (r1, t))
{
q1--;
r1 = _cairo_uint64_add (r1, den);
if (_cairo_uint64_ge (r1, den) && _cairo_uint64_lt (r1, t))
{
q1--;
r1 = _cairo_uint64_add (r1, den);
}
}
r1 = _cairo_uint64_sub (r1, t);
qr64 = _cairo_uint64_divrem (r1, _cairo_uint32_to_uint64 (d1));
q0 = _cairo_uint64_to_uint32 (qr64.quo);
r0 = qr64.rem;
t = _cairo_uint32x32_64_mul (q0, d0);
r0 = _cairo_uint64_add (_cairo_uint64_lsl (r0, 32),
_cairo_uint32_to_uint64 (_cairo_uint64_to_uint32 (num.lo)));
if (_cairo_uint64_lt (r0, t))
{
q0--;
r0 = _cairo_uint64_add (r0, den);
if (_cairo_uint64_ge (r0, den) && _cairo_uint64_lt (r0, t))
{
q0--;
r0 = _cairo_uint64_add (r0, den);
}
}
r0 = _cairo_uint64_sub (r0, t);
qr.quo = _cairo_uint32s_to_uint64 (q1, q0);
qr.rem = r0;
return qr;
}
#if HAVE_UINT64_T
static int
_cairo_leading_zeros64 (cairo_uint64_t q)
{
int top = 0;
if (q >= (uint64_t) 0x10000 << 16)
{
top += 32;
q >>= 32;
}
if (q >= (uint64_t) 0x10000)
{
top += 16;
q >>= 16;
}
if (q >= (uint64_t) 0x100)
{
top += 8;
q >>= 8;
}
top += top_bit [q];
return 64 - top;
}
#else
static const int
_cairo_leading_zeros64 (cairo_uint64_t d)
{
if (d.hi)
return _cairo_leading_zeros32 (d.hi);
else
return 32 + _cairo_leading_zeros32 (d.lo);
}
#endif
cairo_uquorem128_t
_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
{
cairo_uquorem64_t qr64;
cairo_uquorem128_t qr;
int norm;
cairo_uint64_t q1, q0, r1, r0;
if (_cairo_uint64_eq (den.hi, _cairo_uint32_to_uint64 (0)))
{
if (_cairo_uint64_gt (den.lo, num.hi))
{
/* 0q = nn / 0d */
norm = _cairo_leading_zeros64 (den.lo);
if (norm)
{
den.lo = _cairo_uint64_lsl (den.lo, norm);
num = _cairo_uint128_lsl (num, norm);
}
q1 = _cairo_uint32_to_uint64 (0);
}
else
{
/* qq = NN / 0d */
if (_cairo_uint64_eq (den.lo, _cairo_uint32_to_uint64 (0)))
den.lo = _cairo_uint64_divrem (_cairo_uint32_to_uint64 (1),
den.lo).quo;
norm = _cairo_leading_zeros64 (den.lo);
if (norm)
{
cairo_uint128_t num1;
den.lo = _cairo_uint64_lsl (den.lo, norm);
num1 = _cairo_uint128_rsl (num, 64 - norm);
qr64 = _cairo_uint128x64_normalized_divrem (num1, den.lo);
q1 = qr64.quo;
num.hi = qr64.rem;
num.lo = _cairo_uint64_lsl (num.lo, norm);
}
else
{
num.hi = _cairo_uint64_sub (num.hi, den.lo);
q1 = _cairo_uint32_to_uint64 (1);
}
}
qr64 = _cairo_uint128x64_normalized_divrem (num, den.lo);
q0 = qr64.quo;
r1 = _cairo_uint32_to_uint64 (0);
r0 = _cairo_uint64_rsl (qr64.rem, norm);
}
else
{
if (_cairo_uint64_gt (den.hi, num.hi))
{
/* 00 = nn / DD */
q0 = q1 = _cairo_uint32_to_uint64 (0);
r0 = num.lo;
r1 = num.hi;
}
else
{
/* 0q = NN / dd */
norm = _cairo_leading_zeros64 (den.hi);
if (norm == 0)
{
if (_cairo_uint64_gt (num.hi, den.hi) ||
_cairo_uint64_ge (num.lo, den.lo))
{
q0 = _cairo_uint32_to_uint64 (1);
num = _cairo_uint128_sub (num, den);
}
else
q0 = _cairo_uint32_to_uint64 (0);
q1 = _cairo_uint32_to_uint64 (0);
r0 = num.lo;
r1 = num.hi;
}
else
{
cairo_uint128_t num1;
cairo_uint128_t part;
num1 = _cairo_uint128_rsl (num, 64 - norm);
den = _cairo_uint128_lsl (den, norm);
qr64 = _cairo_uint128x64_normalized_divrem (num1, den.hi);
part = _cairo_uint64x64_128_mul (qr64.quo, den.lo);
q0 = qr64.quo;
num.lo = _cairo_uint64_lsl (num.lo, norm);
num.hi = qr64.rem;
if (_cairo_uint128_gt (part, num))
{
q0 = _cairo_uint64_sub (q0, _cairo_uint32_to_uint64 (1));
part = _cairo_uint128_sub (part, den);
}
q1 = _cairo_uint32_to_uint64 (0);
num = _cairo_uint128_sub (num, part);
num = _cairo_uint128_rsl (num, norm);
r0 = num.lo;
r1 = num.hi;
}
}
}
qr.quo.lo = q0;
qr.quo.hi = q1;
qr.rem.lo = r0;
qr.rem.hi = r1;
return qr;
}
cairo_int128_t
_cairo_int128_negate (cairo_int128_t a)
{
a.lo = _cairo_uint64_not (a.lo);
a.hi = _cairo_uint64_not (a.hi);
return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1));
}
cairo_int128_t
_cairo_int128_not (cairo_int128_t a)
{
a.lo = _cairo_uint64_not (a.lo);
a.hi = _cairo_uint64_not (a.hi);
return a;
}
#endif /* !HAVE_UINT128_T */
cairo_quorem128_t
_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den)
{
int num_neg = _cairo_int128_negative (num);
int den_neg = _cairo_int128_negative (den);
cairo_uquorem128_t uqr;
cairo_quorem128_t qr;
if (num_neg)
num = _cairo_int128_negate (num);
if (den_neg)
den = _cairo_int128_negate (den);
uqr = _cairo_uint128_divrem (num, den);
if (num_neg)
qr.rem = _cairo_int128_negate (uqr.rem);
else
qr.rem = uqr.rem;
if (num_neg != den_neg)
qr.quo = _cairo_int128_negate (uqr.quo);
else
qr.quo = uqr.quo;
return qr;
}