RosettaCodeData/Task/Arithmetic-Rational/D/arithmetic-rational.d

185 lines
5.2 KiB
D
Raw Permalink Normal View History

2013-04-10 16:57:12 -07:00
import std.bigint, std.traits, std.conv;
2014-01-17 05:32:22 +00:00
// std.numeric.gcd doesn't work with BigInt.
2015-02-20 00:35:01 -05:00
T gcd(T)(in T a, in T b) pure nothrow {
2013-04-10 16:57:12 -07:00
return (b != 0) ? gcd(b, a % b) : (a < 0) ? -a : a;
}
2015-02-20 00:35:01 -05:00
T lcm(T)(in T a, in T b) pure nothrow {
2013-04-10 16:57:12 -07:00
return a / gcd(a, b) * b;
}
2014-04-02 16:56:35 +00:00
struct RationalT(T) if (!isUnsigned!T) {
2014-01-17 05:32:22 +00:00
private T num, den; // Numerator & denominator.
2013-04-10 16:57:12 -07:00
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;
}
2015-02-20 00:35:01 -05:00
this(U, V)(in U n, in V d) pure nothrow {
2013-04-10 16:57:12 -07:00
num = toT(n);
den = toT(d);
2014-01-17 05:32:22 +00:00
const common = gcd(num, den);
2013-04-10 16:57:12 -07:00
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;
}
}
2014-01-17 05:32:22 +00:00
static T toT(U)(in ref U n) pure nothrow if (is(U == T)) {
2013-04-10 16:57:12 -07:00
return n;
}
static T toT(U)(in ref U n) pure nothrow if (!is(U == T)) {
T result = n;
return result;
}
2014-04-02 16:56:35 +00:00
T numerator() const pure nothrow @property {
2013-04-10 16:57:12 -07:00
return num;
}
2014-01-17 05:32:22 +00:00
T denominator() const pure nothrow @property {
2013-04-10 16:57:12 -07:00
return den;
}
2014-01-17 05:32:22 +00:00
string toString() const /*pure nothrow*/ {
2013-10-27 22:24:23 +00:00
if (den != 0)
return num.text ~ (den == 1 ? "" : "/" ~ den.text);
if (num == 0)
return "NaRat";
else
return ((num < 0) ? "-" : "+") ~ "infRat";
}
2015-02-20 00:35:01 -05:00
real toReal() pure const nothrow {
2014-01-17 05:32:22 +00:00
static if (is(T == BigInt))
2014-04-02 16:56:35 +00:00
return num.toLong / real(den.toLong);
2014-01-17 05:32:22 +00:00
else
2014-04-02 16:56:35 +00:00
return num / real(den);
2013-04-10 16:57:12 -07:00
}
2014-01-17 05:32:22 +00:00
RationalT opBinary(string op)(in RationalT r)
2015-02-20 00:35:01 -05:00
const pure nothrow if (op == "+" || op == "-") {
2013-04-10 16:57:12 -07:00
T common = lcm(den, r.den);
T n = mixin("common / den * num" ~ op ~
"common / r.den * r.num" );
return RationalT(n, common);
}
2014-01-17 05:32:22 +00:00
RationalT opBinary(string op)(in RationalT r)
2015-02-20 00:35:01 -05:00
const pure nothrow if (op == "*") {
2013-04-10 16:57:12 -07:00
return RationalT(num * r.num, den * r.den);
}
2014-01-17 05:32:22 +00:00
RationalT opBinary(string op)(in RationalT r)
2015-02-20 00:35:01 -05:00
const pure nothrow if (op == "/") {
2013-04-10 16:57:12 -07:00
return RationalT(num * r.den, den * r.num);
}
RationalT opBinary(string op, U)(in U r)
2015-02-20 00:35:01 -05:00
const pure nothrow if (isIntegral!U && (op == "+" ||
2014-01-17 05:32:22 +00:00
op == "-" || op == "*" || op == "/")) {
2013-04-10 16:57:12 -07:00
return opBinary!op(RationalT(r));
}
RationalT opBinary(string op)(in size_t p)
2015-02-20 00:35:01 -05:00
const pure nothrow if (op == "^^") {
2013-04-10 16:57:12 -07:00
return RationalT(num ^^ p, den ^^ p);
}
RationalT opBinaryRight(string op, U)(in U l)
2015-02-20 00:35:01 -05:00
const pure nothrow if (isIntegral!U) {
2013-04-10 16:57:12 -07:00
return RationalT(l).opBinary!op(RationalT(num, den));
}
2014-01-17 05:32:22 +00:00
RationalT opOpAssign(string op, U)(in U l) pure /*nothrow*/ {
2013-04-10 16:57:12 -07:00
mixin("this = this " ~ op ~ "l;");
return this;
}
RationalT opUnary(string op)()
2015-02-20 00:35:01 -05:00
const pure nothrow if (op == "+" || op == "-") {
2013-04-10 16:57:12 -07:00
return RationalT(mixin(op ~ "num"), den);
}
bool opCast(U)() const if (is(U == bool)) {
return num != 0;
}
2014-04-02 16:56:35 +00:00
bool opEquals(U)(in U r) const pure nothrow {
2013-04-10 16:57:12 -07:00
RationalT rhs = RationalT(r);
if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
return false;
return num == rhs.num && den == rhs.den;
}
2015-02-20 00:35:01 -05:00
int opCmp(U)(in U r) const pure nothrow {
2013-04-10 16:57:12 -07:00
auto rhs = RationalT(r);
if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
2015-02-20 00:35:01 -05:00
throw new Error("Compare involve a NaRAT.");
2013-04-10 16:57:12 -07:00
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);
}
2014-01-17 05:32:22 +00:00
Type type() const pure nothrow {
2013-04-10 16:57:12 -07:00
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;
}
}
2015-02-20 00:35:01 -05:00
RationalT!U rational(U)(in U n) pure nothrow {
2014-04-02 16:56:35 +00:00
return typeof(return)(n);
}
RationalT!(CommonType!(U1, U2))
2015-02-20 00:35:01 -05:00
rational(U1, U2)(in U1 n, in U2 d) pure nothrow {
2014-04-02 16:56:35 +00:00
return typeof(return)(n, d);
}
2013-04-10 16:57:12 -07:00
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);
2014-04-02 16:56:35 +00:00
immutable limit = 1 + cast(uint)real(p).sqrt;
2013-04-10 16:57:12 -07:00
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." : ".");
}
}
}