blob: 40325500566b1272ff4ee96fb00b3aa1eface4f6 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go.h"
//
// return the significant
// words of the argument
//
static int
mplen(Mpint *a)
{
int i, n;
long *a1;
n = -1;
a1 = &a->a[0];
for(i=0; i<Mpprec; i++) {
if(*a1++ != 0)
n = i;
}
return n+1;
}
//
// left shift mpint by one
// ignores sign and overflow
//
static void
mplsh(Mpint *a)
{
long *a1, x;
int i, c;
c = 0;
a1 = &a->a[0];
for(i=0; i<Mpprec; i++) {
x = (*a1 << 1) + c;
c = 0;
if(x >= Mpbase) {
x -= Mpbase;
c = 1;
}
*a1++ = x;
}
}
//
// left shift mpint by Mpscale
// ignores sign and overflow
//
static void
mplshw(Mpint *a)
{
long *a1;
int i;
a1 = &a->a[Mpprec-1];
for(i=1; i<Mpprec; i++) {
a1[0] = a1[-1];
a1--;
}
a1[0] = 0;
}
//
// right shift mpint by one
// ignores sign and overflow
//
static void
mprsh(Mpint *a)
{
long *a1, x, lo;
int i, c;
c = 0;
lo = a->a[0] & 1;
a1 = &a->a[Mpprec];
for(i=0; i<Mpprec; i++) {
x = *--a1;
*a1 = (x + c) >> 1;
c = 0;
if(x & 1)
c = Mpbase;
}
if(a->neg && lo != 0)
mpaddcfix(a, -1);
}
//
// right shift mpint by Mpscale
// ignores sign and overflow
//
static void
mprshw(Mpint *a)
{
long *a1, lo;
int i;
lo = a->a[0];
a1 = &a->a[0];
for(i=1; i<Mpprec; i++) {
a1[0] = a1[1];
a1++;
}
a1[0] = 0;
if(a->neg && lo != 0)
mpaddcfix(a, -1);
}
//
// return the sign of (abs(a)-abs(b))
//
static int
mpcmp(Mpint *a, Mpint *b)
{
long x, *a1, *b1;
int i;
if(a->ovf || b->ovf) {
yyerror("ovf in cmp");
return 0;
}
a1 = &a->a[0] + Mpprec;
b1 = &b->a[0] + Mpprec;
for(i=0; i<Mpprec; i++) {
x = *--a1 - *--b1;
if(x > 0)
return +1;
if(x < 0)
return -1;
}
return 0;
}
//
// negate a
// ignore sign and ovf
//
static void
mpneg(Mpint *a)
{
long x, *a1;
int i, c;
a1 = &a->a[0];
c = 0;
for(i=0; i<Mpprec; i++) {
x = -*a1 -c;
c = 0;
if(x < 0) {
x += Mpbase;
c = 1;
}
*a1++ = x;
}
}
// shift left by s (or right by -s)
void
mpshiftfix(Mpint *a, int s)
{
if(s >= 0) {
while(s >= Mpscale) {
mplshw(a);
s -= Mpscale;
}
while(s > 0) {
mplsh(a);
s--;
}
} else {
s = -s;
while(s >= Mpscale) {
mprshw(a);
s -= Mpscale;
}
while(s > 0) {
mprsh(a);
s--;
}
}
}
/// implements fix arihmetic
void
mpaddfixfix(Mpint *a, Mpint *b)
{
int i, c;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
yyerror("ovf in mpaddxx");
a->ovf = 1;
return;
}
c = 0;
a1 = &a->a[0];
b1 = &b->a[0];
if(a->neg != b->neg)
goto sub;
// perform a+b
for(i=0; i<Mpprec; i++) {
x = *a1 + *b1++ + c;
c = 0;
if(x >= Mpbase) {
x -= Mpbase;
c = 1;
}
*a1++ = x;
}
a->ovf = c;
if(a->ovf)
yyerror("set ovf in mpaddxx");
return;
sub:
// perform a-b
switch(mpcmp(a, b)) {
case 0:
mpmovecfix(a, 0);
break;
case 1:
for(i=0; i<Mpprec; i++) {
x = *a1 - *b1++ - c;
c = 0;
if(x < 0) {
x += Mpbase;
c = 1;
}
*a1++ = x;
}
break;
case -1:
a->neg ^= 1;
for(i=0; i<Mpprec; i++) {
x = *b1++ - *a1 - c;
c = 0;
if(x < 0) {
x += Mpbase;
c = 1;
}
*a1++ = x;
}
break;
}
}
void
mpmulfixfix(Mpint *a, Mpint *b)
{
int i, j, na, nb;
long *a1, x;
Mpint s, q;
if(a->ovf || b->ovf) {
yyerror("ovf in mpmulfixfix");
a->ovf = 1;
return;
}
// pick the smaller
// to test for bits
na = mplen(a);
nb = mplen(b);
if(na > nb) {
mpmovefixfix(&s, a);
a1 = &b->a[0];
na = nb;
} else {
mpmovefixfix(&s, b);
a1 = &a->a[0];
}
s.neg = 0;
mpmovecfix(&q, 0);
for(i=0; i<na; i++) {
x = *a1++;
for(j=0; j<Mpscale; j++) {
if(x & 1)
mpaddfixfix(&q, &s);
mplsh(&s);
x >>= 1;
}
}
q.neg = a->neg ^ b->neg;
mpmovefixfix(a, &q);
if(a->ovf)
yyerror("set ovf in mpmulfixfix");
}
void
mpmulfract(Mpint *a, Mpint *b)
{
int i, j;
long *a1, x;
Mpint s, q;
if(a->ovf || b->ovf) {
yyerror("ovf in mpmulflt");
a->ovf = 1;
return;
}
mpmovefixfix(&s, b);
a1 = &a->a[Mpprec];
s.neg = 0;
mpmovecfix(&q, 0);
x = *--a1;
if(x != 0)
yyerror("mpmulfract not normal");
for(i=0; i<Mpprec-1; i++) {
x = *--a1;
if(x == 0) {
mprshw(&s);
continue;
}
for(j=0; j<Mpscale; j++) {
x <<= 1;
if(x & Mpbase)
mpaddfixfix(&q, &s);
mprsh(&s);
}
}
q.neg = a->neg ^ b->neg;
mpmovefixfix(a, &q);
if(a->ovf)
yyerror("set ovf in mpmulflt");
}
void
mporfixfix(Mpint *a, Mpint *b)
{
int i;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
if(a->neg) {
a->neg = 0;
mpneg(a);
}
if(b->neg)
mpneg(b);
a1 = &a->a[0];
b1 = &b->a[0];
for(i=0; i<Mpprec; i++) {
x = *a1 | *b1++;
*a1++ = x;
}
if(b->neg)
mpneg(b);
if(x & Mpsign) {
a->neg = 1;
mpneg(a);
}
}
void
mpandfixfix(Mpint *a, Mpint *b)
{
int i;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
yyerror("ovf in mpandfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
if(a->neg) {
a->neg = 0;
mpneg(a);
}
if(b->neg)
mpneg(b);
a1 = &a->a[0];
b1 = &b->a[0];
for(i=0; i<Mpprec; i++) {
x = *a1 & *b1++;
*a1++ = x;
}
if(b->neg)
mpneg(b);
if(x & Mpsign) {
a->neg = 1;
mpneg(a);
}
}
void
mpandnotfixfix(Mpint *a, Mpint *b)
{
int i;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
yyerror("ovf in mpandnotfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
if(a->neg) {
a->neg = 0;
mpneg(a);
}
if(b->neg)
mpneg(b);
a1 = &a->a[0];
b1 = &b->a[0];
for(i=0; i<Mpprec; i++) {
x = *a1 & ~*b1++;
*a1++ = x;
}
if(b->neg)
mpneg(b);
if(x & Mpsign) {
a->neg = 1;
mpneg(a);
}
}
void
mpxorfixfix(Mpint *a, Mpint *b)
{
int i;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
if(a->neg) {
a->neg = 0;
mpneg(a);
}
if(b->neg)
mpneg(b);
a1 = &a->a[0];
b1 = &b->a[0];
for(i=0; i<Mpprec; i++) {
x = *a1 ^ *b1++;
*a1++ = x;
}
if(b->neg)
mpneg(b);
if(x & Mpsign) {
a->neg = 1;
mpneg(a);
}
}
void
mplshfixfix(Mpint *a, Mpint *b)
{
vlong s;
if(a->ovf || b->ovf) {
yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
s = mpgetfix(b);
if(s < 0 || s >= Mpprec*Mpscale) {
yyerror("stupid shift: %lld", s);
mpmovecfix(a, 0);
return;
}
mpshiftfix(a, s);
}
void
mprshfixfix(Mpint *a, Mpint *b)
{
vlong s;
if(a->ovf || b->ovf) {
yyerror("ovf in mprshfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
}
s = mpgetfix(b);
if(s < 0 || s >= Mpprec*Mpscale) {
yyerror("stupid shift: %lld", s);
if(a->neg)
mpmovecfix(a, -1);
else
mpmovecfix(a, 0);
return;
}
mpshiftfix(a, -s);
}
void
mpnegfix(Mpint *a)
{
a->neg ^= 1;
}
vlong
mpgetfix(Mpint *a)
{
vlong v;
if(a->ovf) {
yyerror("constant overflow");
return 0;
}
v = (vlong)a->a[0];
v |= (vlong)a->a[1] << Mpscale;
v |= (vlong)a->a[2] << (Mpscale+Mpscale);
if(a->neg)
v = -v;
return v;
}
void
mpmovecfix(Mpint *a, vlong c)
{
int i;
long *a1;
vlong x;
a->neg = 0;
a->ovf = 0;
x = c;
if(x < 0) {
a->neg = 1;
x = -x;
}
a1 = &a->a[0];
for(i=0; i<Mpprec; i++) {
*a1++ = x&Mpmask;
x >>= Mpscale;
}
}
void
mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
{
int i, ns, ds;
ns = n->neg;
ds = d->neg;
n->neg = 0;
d->neg = 0;
mpmovefixfix(r, n);
mpmovecfix(q, 0);
// shift denominator until it
// is larger than numerator
for(i=0; i<Mpprec*Mpscale; i++) {
if(mpcmp(d, r) > 0)
break;
mplsh(d);
}
// if it never happens
// denominator is probably zero
if(i >= Mpprec*Mpscale) {
q->ovf = 1;
r->ovf = 1;
n->neg = ns;
d->neg = ds;
yyerror("set ovf in mpdivmodfixfix");
return;
}
// shift denominator back creating
// quotient a bit at a time
// when done the remaining numerator
// will be the remainder
for(; i>0; i--) {
mplsh(q);
mprsh(d);
if(mpcmp(d, r) <= 0) {
mpaddcfix(q, 1);
mpsubfixfix(r, d);
}
}
n->neg = ns;
d->neg = ds;
r->neg = ns;
q->neg = ns^ds;
}
static int
iszero(Mpint *a)
{
long *a1;
int i;
a1 = &a->a[0] + Mpprec;
for(i=0; i<Mpprec; i++) {
if(*--a1 != 0)
return 0;
}
return 1;
}
void
mpdivfract(Mpint *a, Mpint *b)
{
Mpint n, d;
int i, j, neg;
long *a1, x;
mpmovefixfix(&n, a); // numerator
mpmovefixfix(&d, b); // denominator
a1 = &a->a[Mpprec]; // quotient
neg = n.neg ^ d.neg;
n.neg = 0;
d.neg = 0;
for(i=0; i<Mpprec; i++) {
x = 0;
for(j=0; j<Mpscale; j++) {
x <<= 1;
if(mpcmp(&d, &n) <= 0) {
if(!iszero(&d))
x |= 1;
mpsubfixfix(&n, &d);
}
mprsh(&d);
}
*--a1 = x;
}
a->neg = neg;
}
int
mptestfix(Mpint *a)
{
Mpint b;
int r;
mpmovecfix(&b, 0);
r = mpcmp(a, &b);
if(a->neg) {
if(r > 0)
return -1;
if(r < 0)
return +1;
}
return r;
}