eigenmath/qpow.cpp

274 lines
4.2 KiB
C++

// Rational power function
#include "stdafx.h"
#include "defs.h"
static void qpowf(void);
static void normalize_angle(void);
static int is_small_integer(U *);
void
qpow()
{
save();
qpowf();
restore();
}
#define BASE p1
#define EXPO p2
static void
qpowf(void)
{
int expo;
unsigned int a, b, *t, *x, *y;
EXPO = pop();
BASE = pop();
// if base is 1 or exponent is 0 then return 1
if (isplusone(BASE) || iszero(EXPO)) {
push_integer(1);
return;
}
// if base is zero then return 0
if (iszero(BASE)) {
if (isnegativenumber(EXPO))
stop("divide by zero");
push(zero);
return;
}
// if exponent is 1 then return base
if (isplusone(EXPO)) {
push(BASE);
return;
}
// if exponent is integer then power
if (isinteger(EXPO)) {
push(EXPO);
expo = pop_integer();
if (expo == (int) 0x80000000) {
// expo greater than 32 bits
push_symbol(POWER);
push(BASE);
push(EXPO);
list(3);
return;
}
x = mpow(BASE->u.q.a, abs(expo));
y = mpow(BASE->u.q.b, abs(expo));
if (expo < 0) {
t = x;
x = y;
y = t;
MSIGN(x) = MSIGN(y);
MSIGN(y) = 1;
}
p3 = alloc();
p3->k = NUM;
p3->u.q.a = x;
p3->u.q.b = y;
push(p3);
return;
}
// from here on out the exponent is NOT an integer
// if base is -1 then normalize polar angle
if (isminusone(BASE)) {
push(EXPO);
if (conjugating)
negate();
normalize_angle();
return;
}
// if base is negative then (-N)^M -> N^M * (-1)^M
if (isnegativenumber(BASE)) {
push(BASE);
negate();
push(EXPO);
power_numbers();
push_integer(-1);
push(EXPO);
power_numbers();
multiply();
return;
}
// if BASE is not an integer then power numerator and denominator
if (!isinteger(BASE)) {
push(BASE);
mp_numerator();
push(EXPO);
power_numbers();
push(BASE);
mp_denominator();
push(EXPO);
negate();
power_numbers();
multiply();
return;
}
// At this point BASE is a positive integer.
// If BASE is small then factor it.
if (is_small_integer(BASE)) {
push(BASE);
push(EXPO);
quickfactor();
return;
}
// At this point BASE is a positive integer and EXPO is not an integer.
if (MLENGTH(EXPO->u.q.a) > 1 || MLENGTH(EXPO->u.q.b) > 1) {
push_symbol(POWER);
push(BASE);
push(EXPO);
list(3);
return;
}
a = EXPO->u.q.a[0];
b = EXPO->u.q.b[0];
x = mroot(BASE->u.q.a, b);
if (x == 0) {
push_symbol(POWER);
push(BASE);
push(EXPO);
list(3);
return;
}
y = mpow(x, a);
mfree(x);
p3 = alloc();
p3->k = NUM;
if (MSIGN(EXPO->u.q.a) == -1) {
p3->u.q.a = mint(1);
p3->u.q.b = y;
} else {
p3->u.q.a = y;
p3->u.q.b = mint(1);
}
push(p3);
}
//-----------------------------------------------------------------------------
//
// Normalize the angle of unit imaginary, i.e. (-1) ^ N
//
// Input: N on stack (must be rational, not float)
//
// Output: Result on stack
//
// Note:
//
// n = q * d + r
//
// Example:
// n d q r
//
// (-1)^(8/3) -> (-1)^(2/3) 8 3 2 2
// (-1)^(7/3) -> (-1)^(1/3) 7 3 2 1
// (-1)^(5/3) -> -(-1)^(2/3) 5 3 1 2
// (-1)^(4/3) -> -(-1)^(1/3) 4 3 1 1
// (-1)^(2/3) -> (-1)^(2/3) 2 3 0 2
// (-1)^(1/3) -> (-1)^(1/3) 1 3 0 1
//
// (-1)^(-1/3) -> -(-1)^(2/3) -1 3 -1 2
// (-1)^(-2/3) -> -(-1)^(1/3) -2 3 -1 1
// (-1)^(-4/3) -> (-1)^(2/3) -4 3 -2 2
// (-1)^(-5/3) -> (-1)^(1/3) -5 3 -2 1
// (-1)^(-7/3) -> -(-1)^(2/3) -7 3 -3 2
// (-1)^(-8/3) -> -(-1)^(1/3) -8 3 -3 1
//
//-----------------------------------------------------------------------------
#define A p1
#define Q p2
#define R p3
static void
normalize_angle(void)
{
save();
A = pop();
// integer exponent?
if (isinteger(A)) {
if (A->u.q.a[0] & 1)
push_integer(-1); // odd exponent
else
push_integer(1); // even exponent
restore();
return;
}
// floor
push(A);
bignum_truncate();
Q = pop();
if (isnegativenumber(A)) {
push(Q);
push_integer(-1);
add();
Q = pop();
}
// remainder (always positive)
push(A);
push(Q);
subtract();
R = pop();
// remainder becomes new angle
push_symbol(POWER);
push_integer(-1);
push(R);
list(3);
// negate if quotient is odd
if (Q->u.q.a[0] & 1)
negate();
restore();
}
static int
is_small_integer(U *p)
{
if (isinteger(p) && MLENGTH(p->u.q.a) == 1 && (p->u.q.a[0] & 0x80000000) == 0)
return 1;
else
return 0;
}