288 lines
7.1 KiB
C++
288 lines
7.1 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Package Title ratpak
|
|
// File rat.c
|
|
// Copyright (C) 1995-96 Microsoft
|
|
// Date 01-16-95
|
|
//
|
|
//
|
|
// Description
|
|
//
|
|
// Contains mul, div, add, and other support functions for rationals.
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "ratpak.h"
|
|
|
|
using namespace std;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: gcdrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational.
|
|
//
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Divides p and q in rational by the G.C.D.
|
|
// of both. It was hoped this would speed up some
|
|
// calculations, and until the above trimming was done it
|
|
// did, but after trimming gcdratting, only slows things
|
|
// down.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void gcdrat(_Inout_ PRAT* pa, int32_t precision)
|
|
|
|
{
|
|
PNUMBER pgcd = nullptr;
|
|
PRAT a = nullptr;
|
|
|
|
a = *pa;
|
|
pgcd = gcd(a->pp, a->pq);
|
|
|
|
if (!zernum(pgcd))
|
|
{
|
|
divnumx(&(a->pp), pgcd, precision);
|
|
divnumx(&(a->pq), pgcd, precision);
|
|
}
|
|
|
|
destroynum(pgcd);
|
|
*pa = a;
|
|
|
|
RENORMALIZE(*pa);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: fracrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational a second rational.
|
|
//
|
|
// RETURN: None, changes pointer.
|
|
//
|
|
// DESCRIPTION: Does the rational equivalent of frac(*pa);
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void fracrat(_Inout_ PRAT* pa, uint32_t radix, int32_t precision)
|
|
{
|
|
// Only do the flatrat operation if number is nonzero.
|
|
// and only if the bottom part is not one.
|
|
if (!zernum((*pa)->pp) && !equnum((*pa)->pq, num_one))
|
|
{
|
|
flatrat(*pa, radix, precision);
|
|
}
|
|
|
|
remnum(&((*pa)->pp), (*pa)->pq, BASEX);
|
|
|
|
// Get *pa back in the integer over integer form.
|
|
RENORMALIZE(*pa);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: mulrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational a second rational.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the rational equivalent of *pa *= b.
|
|
// Assumes radix is the radix of both numbers.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void mulrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision)
|
|
|
|
{
|
|
// Only do the multiply if it isn't zero.
|
|
if (!zernum((*pa)->pp))
|
|
{
|
|
mulnumx(&((*pa)->pp), b->pp);
|
|
mulnumx(&((*pa)->pq), b->pq);
|
|
trimit(pa, precision);
|
|
}
|
|
else
|
|
{
|
|
// If it is zero, blast a one in the denominator.
|
|
DUPNUM(((*pa)->pq), num_one);
|
|
}
|
|
|
|
#ifdef MULGCD
|
|
gcdrat(pa);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: divrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational a second rational.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the rational equivalent of *pa /= b.
|
|
// Assumes radix is the radix of both numbers.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void divrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision)
|
|
|
|
{
|
|
if (!zernum((*pa)->pp))
|
|
{
|
|
// Only do the divide if the top isn't zero.
|
|
mulnumx(&((*pa)->pp), b->pq);
|
|
mulnumx(&((*pa)->pq), b->pp);
|
|
|
|
if (zernum((*pa)->pq))
|
|
{
|
|
// raise an exception if the bottom is 0.
|
|
throw(CALC_E_DIVIDEBYZERO);
|
|
}
|
|
trimit(pa, precision);
|
|
}
|
|
else
|
|
{
|
|
// Top is zero.
|
|
if (zerrat(b))
|
|
{
|
|
// If bottom is zero
|
|
// 0 / 0 is indefinite, raise an exception.
|
|
throw(CALC_E_INDEFINITE);
|
|
}
|
|
else
|
|
{
|
|
// 0/x make a unique 0.
|
|
DUPNUM(((*pa)->pq), num_one);
|
|
}
|
|
}
|
|
|
|
#ifdef DIVGCD
|
|
gcdrat(pa);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: subrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational a second rational.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the rational equivalent of *pa += b.
|
|
// Assumes base is internal throughout.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void subrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision)
|
|
|
|
{
|
|
b->pp->sign *= -1;
|
|
addrat(pa, b, precision);
|
|
b->pp->sign *= -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: addrat
|
|
//
|
|
// ARGUMENTS: pointer to a rational a second rational.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the rational equivalent of *pa += b.
|
|
// Assumes base is internal throughout.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void addrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision)
|
|
|
|
{
|
|
PNUMBER bot = nullptr;
|
|
|
|
if (equnum((*pa)->pq, b->pq))
|
|
{
|
|
// Very special case, q's match.,
|
|
// make sure signs are involved in the calculation
|
|
// we have to do this since the optimization here is only
|
|
// working with the top half of the rationals.
|
|
(*pa)->pp->sign *= (*pa)->pq->sign;
|
|
(*pa)->pq->sign = 1;
|
|
b->pp->sign *= b->pq->sign;
|
|
b->pq->sign = 1;
|
|
addnum(&((*pa)->pp), b->pp, BASEX);
|
|
}
|
|
else
|
|
{
|
|
// Usual case q's aren't the same.
|
|
DUPNUM(bot, (*pa)->pq);
|
|
mulnumx(&bot, b->pq);
|
|
mulnumx(&((*pa)->pp), b->pq);
|
|
mulnumx(&((*pa)->pq), b->pp);
|
|
addnum(&((*pa)->pp), (*pa)->pq, BASEX);
|
|
destroynum((*pa)->pq);
|
|
(*pa)->pq = bot;
|
|
trimit(pa, precision);
|
|
|
|
// Get rid of negative zeros here.
|
|
(*pa)->pp->sign *= (*pa)->pq->sign;
|
|
(*pa)->pq->sign = 1;
|
|
}
|
|
|
|
#ifdef ADDGCD
|
|
gcdrat(pa);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: rootrat
|
|
//
|
|
// PARAMETERS: y prat representation of number to take the root of
|
|
// n prat representation of the root to take.
|
|
//
|
|
// RETURN: bth root of a in rat form.
|
|
//
|
|
// EXPLANATION: This is now a stub function to powrat().
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void rootrat(_Inout_ PRAT* py, _In_ PRAT n, uint32_t radix, int32_t precision)
|
|
{
|
|
// Initialize 1/n
|
|
PRAT oneovern = nullptr;
|
|
DUPRAT(oneovern, rat_one);
|
|
divrat(&oneovern, n, precision);
|
|
|
|
powrat(py, oneovern, radix, precision);
|
|
|
|
destroyrat(oneovern);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: zerrat
|
|
//
|
|
// ARGUMENTS: Rational number.
|
|
//
|
|
// RETURN: Boolean
|
|
//
|
|
// DESCRIPTION: Returns true if input is zero.
|
|
// False otherwise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool zerrat(_In_ PRAT a)
|
|
|
|
{
|
|
return (zernum(a->pp));
|
|
}
|