439 lines
9.5 KiB
C++
439 lines
9.5 KiB
C++
|
// Released into the public-domain by Robert Bridson, 2009.
|
||
|
// Modified by Tyson Brochu, 2011.
|
||
|
|
||
|
#include "expansion.h"
|
||
|
|
||
|
#include <cassert>
|
||
|
#include <cmath>
|
||
|
#include <iostream>
|
||
|
|
||
|
namespace CCD
|
||
|
{
|
||
|
namespace {
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
two_sum(double a,
|
||
|
double b,
|
||
|
double& x,
|
||
|
double& y)
|
||
|
{
|
||
|
x=a+b;
|
||
|
double z=x-a;
|
||
|
y=(a-(x-z))+(b-z);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
// requires that |a|>=|b|
|
||
|
void
|
||
|
fast_two_sum(double a,
|
||
|
double b,
|
||
|
double& x,
|
||
|
double& y)
|
||
|
{
|
||
|
assert( a == a && b == b );
|
||
|
assert(std::fabs(a)>=std::fabs(b));
|
||
|
x=a+b;
|
||
|
y=(a-x)+b;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
split(double a,
|
||
|
double& x,
|
||
|
double& y)
|
||
|
{
|
||
|
double c=134217729*a;
|
||
|
x=c-(c-a);
|
||
|
y=a-x;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
two_product(double a,
|
||
|
double b,
|
||
|
double& x,
|
||
|
double& y)
|
||
|
{
|
||
|
x=a*b;
|
||
|
double a1, a2, b1, b2;
|
||
|
split(a, a1, a2);
|
||
|
split(b, b1, b2);
|
||
|
y=a2*b2-(((x-a1*b1)-a2*b1)-a1*b2);
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
bool
|
||
|
is_zero( const expansion& a )
|
||
|
{
|
||
|
return ( a.v.size() == 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
int
|
||
|
sign( const expansion& a )
|
||
|
{
|
||
|
if ( a.v.size() == 0 ) { return 0; }
|
||
|
|
||
|
// REVIEW: I'm assuming we can get the sign of the expansion by the sign of its leading term (i.e. the sum of all other terms < leading term )
|
||
|
// This true if the expansion if increasing and nonoverlapping
|
||
|
if ( a.v.back() > 0 )
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
add(double a, double b, expansion& sum)
|
||
|
{
|
||
|
sum.resize(2);
|
||
|
two_sum(a, b, sum.v[1], sum.v[0]);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
// a and sum may be aliased to the same expansion for in-place addition
|
||
|
void
|
||
|
add(const expansion& a, double b, expansion& sum)
|
||
|
{
|
||
|
size_t m=a.v.size();
|
||
|
sum.v.reserve(m+1);
|
||
|
double s;
|
||
|
for(size_t i=0; i<m; ++i){
|
||
|
two_sum(b, a.v[i], b, s);
|
||
|
if(s) sum.v.push_back(s);
|
||
|
}
|
||
|
sum.v.push_back(b);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
// aliasing a, b and sum is safe
|
||
|
void
|
||
|
add(const expansion& a, const expansion& b, expansion& sum)
|
||
|
{
|
||
|
|
||
|
if(a.v.empty())
|
||
|
{
|
||
|
sum=b;
|
||
|
return;
|
||
|
}else if(b.v.empty())
|
||
|
{
|
||
|
sum=a;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Shewchuk's fast-expansion-sum
|
||
|
expansion merge(a.v.size()+b.v.size(), 0);
|
||
|
unsigned int i=0, j=0, k=0;
|
||
|
for(;;){
|
||
|
if(std::fabs(a.v[i])<std::fabs(b.v[j])){
|
||
|
merge.v[k++]=a.v[i++];
|
||
|
if(i==a.v.size()){
|
||
|
while(j<b.v.size()) merge.v[k++]=b.v[j++];
|
||
|
break;
|
||
|
}
|
||
|
}else{
|
||
|
merge.v[k++]=b.v[j++];
|
||
|
if(j==b.v.size()){
|
||
|
while(i<a.v.size()) merge.v[k++]=a.v[i++];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sum.v.reserve(merge.v.size());
|
||
|
sum.v.resize(0);
|
||
|
double q, r;
|
||
|
fast_two_sum(merge.v[1], merge.v[0], q, r);
|
||
|
if(r) sum.v.push_back(r);
|
||
|
for(i=2; i<merge.v.size(); ++i){
|
||
|
two_sum(q, merge.v[i], q, r);
|
||
|
if(r) sum.v.push_back(r);
|
||
|
}
|
||
|
if(q) sum.v.push_back(q);
|
||
|
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
subtract( const double& a, const double& b, expansion& difference)
|
||
|
{
|
||
|
add( a, -b, difference );
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
subtract(const expansion& a, const expansion& b, expansion& difference)
|
||
|
{
|
||
|
// could improve this a bit!
|
||
|
expansion c;
|
||
|
negative(b, c);
|
||
|
add(a, c, difference);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
negative(const expansion& input, expansion& output)
|
||
|
{
|
||
|
output.resize(input.v.size());
|
||
|
for(unsigned int i=0; i<input.v.size(); ++i)
|
||
|
output.v[i]=-input.v[i];
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
multiply(double a, double b, expansion& product)
|
||
|
{
|
||
|
product.resize(2);
|
||
|
two_product(a, b, product.v[1], product.v[0]);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
multiply(double a, double b, double c, expansion& product)
|
||
|
{
|
||
|
expansion ab;
|
||
|
multiply(a, b, ab);
|
||
|
multiply(ab, c, product);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
multiply(double a, double b, double c, double d, expansion& product)
|
||
|
{
|
||
|
multiply(a, b, product);
|
||
|
expansion abc;
|
||
|
multiply(product, c, abc);
|
||
|
multiply(abc, d, product);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
multiply(const expansion& a, double b, expansion& product)
|
||
|
{
|
||
|
|
||
|
// basic idea:
|
||
|
// multiply each entry in a by b (producing two new entries), then
|
||
|
// two_sum them in such a way to guarantee increasing/non-overlapping output
|
||
|
product.resize(2*a.v.size());
|
||
|
if(a.v.empty()) return;
|
||
|
two_product(a.v[0], b, product.v[1], product.v[0]); // finalize product[0]
|
||
|
double x, y, z;
|
||
|
for(unsigned int i=1; i<a.v.size(); ++i){
|
||
|
two_product(a.v[i], b, x, y);
|
||
|
// finalize product[2*i-1]
|
||
|
two_sum(product.v[2*i-1], y, z, product.v[2*i-1]);
|
||
|
// finalize product[2*i], could be fast_two_sum instead
|
||
|
fast_two_sum(x, z, product.v[2*i+1], product.v[2*i]);
|
||
|
}
|
||
|
// multiplication is a prime candidate for producing spurious zeros, so
|
||
|
// remove them by default
|
||
|
remove_zeros(product);
|
||
|
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
multiply(const expansion& a, const expansion& b, expansion& product)
|
||
|
{
|
||
|
// most stupid way of doing it:
|
||
|
// multiply a by each entry in b, add each to product
|
||
|
product.resize(0);
|
||
|
expansion term;
|
||
|
for(unsigned int i=0; i<b.v.size(); ++i){
|
||
|
multiply(a, b.v[i], term);
|
||
|
add(product, term, product);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
|
||
|
|
||
|
void compress( const expansion& e, expansion& h )
|
||
|
{
|
||
|
if ( is_zero( e ) )
|
||
|
{
|
||
|
make_zero( h );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
expansion g( e.v.size(), 0 );
|
||
|
|
||
|
size_t bottom = e.v.size() - 1;
|
||
|
double q = e.v[bottom];
|
||
|
|
||
|
for ( ssize_t i = e.v.size() - 2; i >= 0; --i )
|
||
|
{
|
||
|
double new_q, small_q;
|
||
|
fast_two_sum( q, e.v[i], new_q, small_q );
|
||
|
if ( small_q != 0 )
|
||
|
{
|
||
|
g.v[bottom--] = new_q;
|
||
|
q = small_q;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
q = new_q;
|
||
|
}
|
||
|
}
|
||
|
g.v[bottom] = q;
|
||
|
|
||
|
h.v.resize( e.v.size(), 0 );
|
||
|
|
||
|
unsigned int top = 0;
|
||
|
|
||
|
for ( size_t i = bottom+1; i < e.v.size(); ++i )
|
||
|
{
|
||
|
double new_q, small_q;
|
||
|
fast_two_sum( g.v[i], q, new_q, small_q );
|
||
|
if ( small_q != 0 )
|
||
|
{
|
||
|
h.v[top++] = small_q;
|
||
|
}
|
||
|
q = new_q;
|
||
|
}
|
||
|
h.v[top] = q;
|
||
|
h.resize( top+1 );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
|
||
|
bool divide( const expansion& x, const expansion& y, expansion& q )
|
||
|
{
|
||
|
|
||
|
assert( !is_zero( y ) );
|
||
|
|
||
|
if ( is_zero( x ) )
|
||
|
{
|
||
|
// 0 / y = 0
|
||
|
make_expansion( 0, q );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const double divisor = estimate(y);
|
||
|
|
||
|
// q is the quotient, built by repeatedly dividing the remainder
|
||
|
// Initially, q = estimate(x) / estimate(y)
|
||
|
|
||
|
make_expansion( estimate(x) / divisor, q );
|
||
|
|
||
|
expansion qy;
|
||
|
multiply( q, y, qy );
|
||
|
expansion r;
|
||
|
subtract( x, qy, r );
|
||
|
|
||
|
while ( !is_zero(r) )
|
||
|
{
|
||
|
// s is the next term in the quotient q:
|
||
|
// s = estimate(r) / estimate(y)
|
||
|
expansion s;
|
||
|
make_expansion( estimate(r) / divisor, s );
|
||
|
|
||
|
if ( is_zero(s) )
|
||
|
{
|
||
|
assert ( !is_zero(y) );
|
||
|
std::cout << "underflow, s == 0" << std::endl;
|
||
|
std::cout << "divisor: " << divisor << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// q += s
|
||
|
add( q, s, q );
|
||
|
|
||
|
// r -= s*y
|
||
|
expansion sy;
|
||
|
multiply( s, y, sy );
|
||
|
|
||
|
// underflow, quotient not representable by an expansion
|
||
|
if ( is_zero(sy) )
|
||
|
{
|
||
|
assert ( !is_zero(s) && !is_zero(y) );
|
||
|
std::cout << "underflow, sy == 0" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
subtract( r, sy, r );
|
||
|
|
||
|
expansion compressed_r;
|
||
|
compress( r, compressed_r );
|
||
|
r = compressed_r;
|
||
|
|
||
|
}
|
||
|
|
||
|
remove_zeros( q );
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void
|
||
|
remove_zeros(expansion& a)
|
||
|
{
|
||
|
|
||
|
unsigned int i, j;
|
||
|
|
||
|
for ( i = 0, j = 0; i < a.v.size(); ++i )
|
||
|
{
|
||
|
if ( a.v[i] )
|
||
|
{
|
||
|
a.v[j++] = a.v[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
a.resize(j);
|
||
|
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
double
|
||
|
estimate(const expansion& a)
|
||
|
{
|
||
|
double x=0;
|
||
|
for(unsigned int i=0; i<a.v.size(); ++i)
|
||
|
x+=a.v[i];
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
|
||
|
bool equals( const expansion& a, const expansion& b )
|
||
|
{
|
||
|
bool same = (a.v.size() == b.v.size());
|
||
|
|
||
|
if (!same) { return false; }
|
||
|
|
||
|
for ( unsigned int i = 0; i < a.v.size(); ++i )
|
||
|
{
|
||
|
same &= (a.v[i] == b.v[i]);
|
||
|
}
|
||
|
|
||
|
return same;
|
||
|
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
|
||
|
void print_full( const expansion& e )
|
||
|
{
|
||
|
if ( e.v.size() == 0 )
|
||
|
{
|
||
|
std::cout << "0" << std::endl;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for ( unsigned int j = 0; j < e.v.size(); ++j )
|
||
|
{
|
||
|
std::cout << e.v[j] << " ";
|
||
|
}
|
||
|
std::cout << std::endl;
|
||
|
}
|
||
|
}
|