171 lines
4.9 KiB
D
171 lines
4.9 KiB
D
import std.bigint, std.traits, std.conv;
|
|
|
|
T gcd(T)(/*in*/ T a, /*in*/ T b) /*pure nothrow*/ {
|
|
// std.numeric.gcd doesn't work with BigInt.
|
|
return (b != 0) ? gcd(b, a % b) : (a < 0) ? -a : a;
|
|
}
|
|
|
|
T lcm(T)(/*in*/ T a, /*in*/ T b) {
|
|
return a / gcd(a, b) * b;
|
|
}
|
|
|
|
struct RationalT(T) {
|
|
/*const*/ private T num, den; // Numerator & denominator.
|
|
|
|
private enum Type { NegINF = -2,
|
|
NegDEN = -1,
|
|
NaRAT = 0,
|
|
NORMAL = 1,
|
|
PosINF = 2 };
|
|
|
|
this(U : RationalT)(U n) pure nothrow {
|
|
num = n.num;
|
|
den = n.den;
|
|
}
|
|
|
|
this(U)(in U n) pure nothrow if (isIntegral!U) {
|
|
num = toT(n);
|
|
den = 1UL;
|
|
}
|
|
|
|
this(U, V)(/*in*/ U n, /*in*/ V d) /*pure nothrow*/ {
|
|
num = toT(n);
|
|
den = toT(d);
|
|
/*const*/ T common = gcd(num, den);
|
|
if (common != 0) {
|
|
num /= common;
|
|
den /= common;
|
|
} else { // infinite or NOT a Number
|
|
num = (num == 0) ? 0 : (num < 0) ? -1 : 1;
|
|
den = 0;
|
|
}
|
|
if (den < 0) { // Assure den is non-negative.
|
|
num = -num;
|
|
den = -den;
|
|
}
|
|
}
|
|
|
|
static T toT(U)(/*in*/ ref U n) pure nothrow if (is(U == T)) {
|
|
return n;
|
|
}
|
|
|
|
static T toT(U)(in ref U n) pure nothrow if (!is(U == T)) {
|
|
T result = n;
|
|
return result;
|
|
}
|
|
|
|
T nomerator() /*const*/ pure nothrow @property {
|
|
return num;
|
|
}
|
|
|
|
T denominator() /*const*/ pure nothrow @property {
|
|
return den;
|
|
}
|
|
|
|
string toString() /*const*/ {
|
|
if (den == 0) {
|
|
if (num == 0)
|
|
return "NaRat";
|
|
else
|
|
return ((num < 0) ? "-" : "+") ~ "infRat";
|
|
}
|
|
return text(num) ~ (den == 1 ? "" : ("/" ~ text(den)));
|
|
}
|
|
|
|
RationalT opBinary(string op)(/*in*/ RationalT r)
|
|
/*const pure nothrow*/ if (op == "+" || op == "-") {
|
|
T common = lcm(den, r.den);
|
|
T n = mixin("common / den * num" ~ op ~
|
|
"common / r.den * r.num" );
|
|
return RationalT(n, common);
|
|
}
|
|
|
|
RationalT opBinary(string op)(/*in*/ RationalT r)
|
|
/*const pure nothrow*/ if (op == "*") {
|
|
return RationalT(num * r.num, den * r.den);
|
|
}
|
|
|
|
RationalT opBinary(string op)(/*in*/ RationalT r)
|
|
/*const pure nothrow*/ if (op == "/") {
|
|
return RationalT(num * r.den, den * r.num);
|
|
}
|
|
|
|
RationalT opBinary(string op, U)(in U r)
|
|
/*const pure nothrow*/ if (isIntegral!U && (op == "+" ||
|
|
op == "-" || op == "*" || op == "/")) {
|
|
return opBinary!op(RationalT(r));
|
|
}
|
|
|
|
RationalT opBinary(string op)(in size_t p)
|
|
/*const pure nothrow*/ if (op == "^^") {
|
|
return RationalT(num ^^ p, den ^^ p);
|
|
}
|
|
|
|
RationalT opBinaryRight(string op, U)(in U l)
|
|
/*const pure nothrow*/ if (isIntegral!U) {
|
|
return RationalT(l).opBinary!op(RationalT(num, den));
|
|
}
|
|
|
|
RationalT opOpAssign(string op, U)(in U l)
|
|
/*const pure nothrow*/ {
|
|
mixin("this = this " ~ op ~ "l;");
|
|
return this;
|
|
}
|
|
|
|
RationalT opUnary(string op)()
|
|
/*const pure nothrow*/ if (op == "+" || op == "-") {
|
|
return RationalT(mixin(op ~ "num"), den);
|
|
}
|
|
|
|
bool opCast(U)() const if (is(U == bool)) {
|
|
return num != 0;
|
|
}
|
|
|
|
int opEquals(U)(/*in*/ U r) /*const pure nothrow*/ {
|
|
RationalT rhs = RationalT(r);
|
|
if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
|
|
return false;
|
|
return num == rhs.num && den == rhs.den;
|
|
}
|
|
|
|
int opCmp(U)(/*in*/ U r) /*const pure nothrow*/ {
|
|
auto rhs = RationalT(r);
|
|
if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
|
|
throw new Exception("Compare involve a NaRAT.");
|
|
if (type() != Type.NORMAL ||
|
|
rhs.type() != Type.NORMAL) // for infinite
|
|
return (type() == rhs.type()) ? 0 :
|
|
((type() < rhs.type()) ? -1 : 1);
|
|
auto diff = num * rhs.den - den * rhs.num;
|
|
return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
|
|
}
|
|
|
|
Type type() /*const pure nothrow*/ {
|
|
if (den > 0) return Type.NORMAL;
|
|
if (den < 0) return Type.NegDEN;
|
|
if (num > 0) return Type.PosINF;
|
|
if (num < 0) return Type.NegINF;
|
|
return Type.NaRAT;
|
|
}
|
|
}
|
|
|
|
alias Rational = RationalT!BigInt;
|
|
|
|
version (arithmetic_rational_main) { // Test.
|
|
void main() {
|
|
import std.stdio, std.math;
|
|
alias RatL = RationalT!long;
|
|
|
|
foreach (immutable p; 2 .. 2 ^^ 19) {
|
|
auto sum = RatL(1, p);
|
|
immutable limit = 1 + cast(uint)sqrt(cast(real)p);
|
|
foreach (immutable factor; 2 .. limit)
|
|
if (p % factor == 0)
|
|
sum += RatL(1, factor) + RatL(factor, p);
|
|
if (sum.denominator == 1)
|
|
writefln("Sum of recipr. factors of %6s = %s exactly%s",
|
|
p, sum, (sum == 1) ? ", perfect." : ".");
|
|
}
|
|
}
|
|
}
|