| 1 |
/**A rational number implementation |
|---|
| 2 |
* By: David Simcha |
|---|
| 3 |
* |
|---|
| 4 |
* License: |
|---|
| 5 |
* Boost Software License - Version 1.0 - August 17th, 2003 |
|---|
| 6 |
* |
|---|
| 7 |
* Permission is hereby granted, free of charge, to any person or organization |
|---|
| 8 |
* obtaining a copy of the software and accompanying documentation covered by |
|---|
| 9 |
* this license (the "Software") to use, reproduce, display, distribute, |
|---|
| 10 |
* execute, and transmit the Software, and to prepare derivative works of the |
|---|
| 11 |
* Software, and to permit third-parties to whom the Software is furnished to |
|---|
| 12 |
* do so, all subject to the following: |
|---|
| 13 |
* |
|---|
| 14 |
* The copyright notices in the Software and this entire statement, including |
|---|
| 15 |
* the above license grant, this restriction and the following disclaimer, |
|---|
| 16 |
* must be included in all copies of the Software, in whole or in part, and |
|---|
| 17 |
* all derivative works of the Software, unless such copies or derivative |
|---|
| 18 |
* works are solely in the form of machine-executable object code generated by |
|---|
| 19 |
* a source language processor. |
|---|
| 20 |
* |
|---|
| 21 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 22 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 23 |
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
|---|
| 24 |
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
|---|
| 25 |
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
|---|
| 26 |
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|---|
| 27 |
* DEALINGS IN THE SOFTWARE. |
|---|
| 28 |
*/ |
|---|
| 29 |
module rational; |
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
import std.algorithm, std.stdio, std.bigint, std.conv, std.math, std.exception, |
|---|
| 33 |
std.conv, std.traits; |
|---|
| 34 |
|
|---|
| 35 |
alias std.math.abs abs; // Allow cross-module overloading. |
|---|
| 36 |
|
|---|
| 37 |
private template isIntegerLike(T) { |
|---|
| 38 |
enum bool isIntegerLike = is(typeof({ |
|---|
| 39 |
T num; |
|---|
| 40 |
num = 2; |
|---|
| 41 |
num <<= 1; |
|---|
| 42 |
num >>= 1; |
|---|
| 43 |
num += num; |
|---|
| 44 |
num *= num; |
|---|
| 45 |
num /= num; |
|---|
| 46 |
num -= num; |
|---|
| 47 |
num %= 2; |
|---|
| 48 |
num %= num; |
|---|
| 49 |
bool foo = num < 2; |
|---|
| 50 |
bool bar = num == 2; |
|---|
| 51 |
|
|---|
| 52 |
return num; |
|---|
| 53 |
})); |
|---|
| 54 |
} |
|---|
| 55 |
|
|---|
| 56 |
unittest { |
|---|
| 57 |
static assert(isIntegerLike!BigInt); |
|---|
| 58 |
static assert(isIntegerLike!int); |
|---|
| 59 |
static assert(isIntegerLike!byte); |
|---|
| 60 |
static assert(!isIntegerLike!real); |
|---|
| 61 |
} |
|---|
| 62 |
|
|---|
| 63 |
private template isRational(T) { |
|---|
| 64 |
enum bool isRational = |
|---|
| 65 |
is(typeof(T.init.denom)) && is(typeof(T.init.num)); |
|---|
| 66 |
} |
|---|
| 67 |
|
|---|
| 68 |
private template CommonRational(R1, R2) { |
|---|
| 69 |
static if(isRational!R1) { |
|---|
| 70 |
alias CommonRational!(typeof(R1.num), R2) CommonRational; |
|---|
| 71 |
} else static if(isRational!R2) { |
|---|
| 72 |
alias CommonRational!(R1, typeof(R2.num)) CommonRational; |
|---|
| 73 |
} else static if(is(CommonInteger!(R1, R2))) { |
|---|
| 74 |
alias Rational!(CommonInteger!(R1, R2)) CommonRational; |
|---|
| 75 |
} |
|---|
| 76 |
} |
|---|
| 77 |
|
|---|
| 78 |
/** |
|---|
| 79 |
Returns $(D true) iff a value of type $(D U) can be assigned to a variable of |
|---|
| 80 |
type $(D T). |
|---|
| 81 |
|
|---|
| 82 |
Examples: |
|---|
| 83 |
--- |
|---|
| 84 |
static assert(isAssignable!(long, int)); |
|---|
| 85 |
static assert(!isAssignable!(int, long)); |
|---|
| 86 |
static assert(isAssignable!(const(char)[], string)); |
|---|
| 87 |
static assert(!isAssignable!(string, char[])); |
|---|
| 88 |
--- |
|---|
| 89 |
*/ |
|---|
| 90 |
template isAssignable(T, U) { |
|---|
| 91 |
enum bool isAssignable = is(typeof({ |
|---|
| 92 |
T t; |
|---|
| 93 |
U u; |
|---|
| 94 |
t = u; |
|---|
| 95 |
return t; |
|---|
| 96 |
})); |
|---|
| 97 |
} |
|---|
| 98 |
|
|---|
| 99 |
unittest { |
|---|
| 100 |
static assert(isAssignable!(long, int)); |
|---|
| 101 |
static assert(!isAssignable!(int, long)); |
|---|
| 102 |
static assert(isAssignable!(const(char)[], string)); |
|---|
| 103 |
static assert(!isAssignable!(string, char[])); |
|---|
| 104 |
} |
|---|
| 105 |
|
|---|
| 106 |
/**Returns a common integral type between I1, I2. This is defined as the type |
|---|
| 107 |
* returned by I1.init * I2.init. |
|---|
| 108 |
*/ |
|---|
| 109 |
template CommonInteger(I1, I2) if(isIntegerLike!I1 && isIntegerLike!I2) { |
|---|
| 110 |
alias typeof(I1.init * I2.init) CommonInteger; |
|---|
| 111 |
} |
|---|
| 112 |
|
|---|
| 113 |
unittest { |
|---|
| 114 |
static assert(is(CommonInteger!(BigInt, int) == BigInt)); |
|---|
| 115 |
static assert(is(CommonInteger!(byte, int) == int)); |
|---|
| 116 |
} |
|---|
| 117 |
|
|---|
| 118 |
/**Implements rational numbers on top of whatever integer type is specified |
|---|
| 119 |
* by the user. If you use a fixed-size int, the onus is on you to make |
|---|
| 120 |
* sure nothing overflows. Neither the denominator nor the numerator may |
|---|
| 121 |
* be bigger than the maximum value of the underlying integer. |
|---|
| 122 |
* |
|---|
| 123 |
* If you use an arbitrary precision type, it must have the relevant operators |
|---|
| 124 |
* overloaded and have value semantics. |
|---|
| 125 |
* |
|---|
| 126 |
* Examples: |
|---|
| 127 |
* --- |
|---|
| 128 |
* auto r1 = rational( BigInt("314159265"), BigInt("27182818")); |
|---|
| 129 |
* auto r2 = rational( BigInt("8675309"), BigInt("362436")); |
|---|
| 130 |
* r1 += r2; |
|---|
| 131 |
* assert(r1 == rational( BigInt("174840986505151"), |
|---|
| 132 |
* BigInt("4926015912324"))); |
|---|
| 133 |
* |
|---|
| 134 |
* // Print result. Prints: |
|---|
| 135 |
* // "174840986505151 / 4926015912324" |
|---|
| 136 |
* writeln(f1); |
|---|
| 137 |
* |
|---|
| 138 |
* // Print result in decimal form. Prints: |
|---|
| 139 |
* // "35.4934" |
|---|
| 140 |
* writeln(cast(real) result); |
|---|
| 141 |
* --- |
|---|
| 142 |
*/ |
|---|
| 143 |
Rational!(CommonInteger!(I1, I2)) rational(I1, I2)(I1 i1, I2 i2) |
|---|
| 144 |
if(isIntegerLike!I1 && isIntegerLike!I2) { |
|---|
| 145 |
static if(is(typeof(typeof(return)(i1, i2)))) { |
|---|
| 146 |
// Avoid initializing and then reassigning. |
|---|
| 147 |
auto ret = typeof(return)(i1, i2); |
|---|
| 148 |
} else { |
|---|
| 149 |
// Don't want to use void initialization b/c BigInts probably use |
|---|
| 150 |
// assignment operator, copy c'tor, etc. |
|---|
| 151 |
typeof(return) ret; |
|---|
| 152 |
ret.numerator = i1; |
|---|
| 153 |
ret.denominator = i2; |
|---|
| 154 |
} |
|---|
| 155 |
ret.simplify(); |
|---|
| 156 |
return ret; |
|---|
| 157 |
} |
|---|
| 158 |
|
|---|
| 159 |
/**Overload for creating rational that initially has an integer value.*/ |
|---|
| 160 |
Rational!(I) rational(I)(I val) |
|---|
| 161 |
if(isIntegerLike!I) { |
|---|
| 162 |
return rational(val, 1); |
|---|
| 163 |
} |
|---|
| 164 |
|
|---|
| 165 |
/// |
|---|
| 166 |
struct Rational(Int) if(isIntegerLike!Int) { |
|---|
| 167 |
public: |
|---|
| 168 |
|
|---|
| 169 |
// ----------------Multiplication operators---------------------------------- |
|---|
| 170 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 171 |
if(op == "*" && is(CommonRational!(Int, Rhs)) && isRational!Rhs) { |
|---|
| 172 |
auto ret = CommonRational!(Int, Rhs)(this.num, this.denom); |
|---|
| 173 |
return ret *= rhs; |
|---|
| 174 |
} |
|---|
| 175 |
|
|---|
| 176 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 177 |
if(op == "*" && is(CommonRational!(Int, Rhs)) && isIntegerLike!Rhs) { |
|---|
| 178 |
auto ret = this; |
|---|
| 179 |
return ret *= rhs; |
|---|
| 180 |
} |
|---|
| 181 |
|
|---|
| 182 |
auto opBinaryRight(string op, Rhs)(Rhs rhs) |
|---|
| 183 |
if(op == "*" && is(CommonRational!(Int, Rhs)) && isIntegerLike!Rhs) { |
|---|
| 184 |
return opBinary!(op, Rhs)(rhs); |
|---|
| 185 |
} |
|---|
| 186 |
|
|---|
| 187 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 188 |
if(op == "*" && isRational!Rhs) { |
|---|
| 189 |
// Cancel common factors first, then multiply. This prevents |
|---|
| 190 |
// overflows and is much more efficient when using BigInts. |
|---|
| 191 |
auto divisor = gcf(this.numerator, rhs.denominator); |
|---|
| 192 |
this.numerator /= divisor; |
|---|
| 193 |
rhs.denominator /= divisor; |
|---|
| 194 |
|
|---|
| 195 |
divisor = gcf(this.denominator, rhs.numerator); |
|---|
| 196 |
this.denominator /= divisor; |
|---|
| 197 |
rhs.numerator /= divisor; |
|---|
| 198 |
|
|---|
| 199 |
this.numerator *= rhs.numerator; |
|---|
| 200 |
this.denominator *= rhs.denominator; |
|---|
| 201 |
|
|---|
| 202 |
// Don't need to simplify. Already cancelled common factors before |
|---|
| 203 |
// multiplying. |
|---|
| 204 |
fixSigns(); |
|---|
| 205 |
return this; |
|---|
| 206 |
} |
|---|
| 207 |
|
|---|
| 208 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 209 |
if(op == "*" && isIntegerLike!Rhs) { |
|---|
| 210 |
auto divisor = gcf(this.denominator, rhs); |
|---|
| 211 |
this.denominator /= divisor; |
|---|
| 212 |
rhs /= divisor; |
|---|
| 213 |
this.numerator *= rhs; |
|---|
| 214 |
|
|---|
| 215 |
// Don't need to simplify. Already cancelled common factors before |
|---|
| 216 |
// multiplying. |
|---|
| 217 |
fixSigns(); |
|---|
| 218 |
return this; |
|---|
| 219 |
} |
|---|
| 220 |
|
|---|
| 221 |
// --------------------Division operators-------------------------------------- |
|---|
| 222 |
|
|---|
| 223 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 224 |
if(op == "/" && is(CommonRational!(Int, Rhs)) && isRational!Rhs) { |
|---|
| 225 |
// Division = multiply by inverse. |
|---|
| 226 |
swap(rhs.numerator, rhs.denominator); |
|---|
| 227 |
return this *= rhs; |
|---|
| 228 |
} |
|---|
| 229 |
|
|---|
| 230 |
typeof(this) opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 231 |
if(op == "/" && is(CommonRational!(Int, Rhs)) && isIntegerLike!(Rhs)) { |
|---|
| 232 |
auto ret = CommonRational!(Int, Rhs)(this.num, this.denom); |
|---|
| 233 |
return ret /= rhs; |
|---|
| 234 |
} |
|---|
| 235 |
|
|---|
| 236 |
typeof(this) opBinaryRight(string op, Rhs)(Rhs rhs) |
|---|
| 237 |
if(op == "/" && is(CommonRational!(Int, Rhs)) && isIntegerLike!Rhs) { |
|---|
| 238 |
auto ret = CommonRational!(Int, Rhs)(this.denom, this.num); |
|---|
| 239 |
return ret *= rhs; |
|---|
| 240 |
} |
|---|
| 241 |
|
|---|
| 242 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 243 |
if(op == "/" && isIntegerLike!Rhs) { |
|---|
| 244 |
auto divisor = gcf(this.numerator, rhs); |
|---|
| 245 |
this.numerator /= divisor; |
|---|
| 246 |
rhs /= divisor; |
|---|
| 247 |
this.denominator *= rhs; |
|---|
| 248 |
|
|---|
| 249 |
// Don't need to simplify. Already cancelled common factors before |
|---|
| 250 |
// multiplying. |
|---|
| 251 |
fixSigns(); |
|---|
| 252 |
return this; |
|---|
| 253 |
} |
|---|
| 254 |
|
|---|
| 255 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 256 |
if(op == "/" && isRational!Rhs) { |
|---|
| 257 |
// Division = multiply by inverse. |
|---|
| 258 |
swap(rhs.numerator, rhs.denominator); |
|---|
| 259 |
return this *= rhs; |
|---|
| 260 |
} |
|---|
| 261 |
|
|---|
| 262 |
// ---------------------Addition operators------------------------------------- |
|---|
| 263 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 264 |
if(op == "+" && (isRational!Rhs || isIntegerLike!Rhs)) { |
|---|
| 265 |
auto ret = CommonRational!(typeof(this), Rhs)(this.num, this.denom); |
|---|
| 266 |
return ret += rhs; |
|---|
| 267 |
} |
|---|
| 268 |
|
|---|
| 269 |
auto opBinaryRight(string op, Rhs)(Rhs rhs) |
|---|
| 270 |
if(op == "+" && is(CommonRational!(Int, Rhs)) && isIntegerLike!Rhs) { |
|---|
| 271 |
return opBinary!(op, Rhs)(rhs); |
|---|
| 272 |
} |
|---|
| 273 |
|
|---|
| 274 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 275 |
if(op == "+" && isRational!Rhs) { |
|---|
| 276 |
if(this.denominator == rhs.denominator) { |
|---|
| 277 |
this.numerator += rhs.numerator; |
|---|
| 278 |
simplify(); |
|---|
| 279 |
return this; |
|---|
| 280 |
} |
|---|
| 281 |
|
|---|
| 282 |
Int commonDenom = lcm(this.denominator, rhs.denominator); |
|---|
| 283 |
this.numerator *= commonDenom / this.denominator; |
|---|
| 284 |
this.numerator += (commonDenom / rhs.denominator) * rhs.numerator; |
|---|
| 285 |
this.denominator = commonDenom; |
|---|
| 286 |
|
|---|
| 287 |
simplify(); |
|---|
| 288 |
return this; |
|---|
| 289 |
} |
|---|
| 290 |
|
|---|
| 291 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 292 |
if(op == "+" && isIntegerLike!Rhs) { |
|---|
| 293 |
this.numerator += rhs * this.denominator; |
|---|
| 294 |
|
|---|
| 295 |
simplify(); |
|---|
| 296 |
return this; |
|---|
| 297 |
} |
|---|
| 298 |
|
|---|
| 299 |
// -----------------------Subtraction operators------------------------------- |
|---|
| 300 |
|
|---|
| 301 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 302 |
if(op == "-" && is(CommonRational!(Int, Rhs))) { |
|---|
| 303 |
auto ret = CommonRational!(typeof(this), Rhs)(this.num, this.denom); |
|---|
| 304 |
return ret -= rhs; |
|---|
| 305 |
} |
|---|
| 306 |
|
|---|
| 307 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 308 |
if(op == "-" && isRational!Rhs) { |
|---|
| 309 |
if(this.denominator == rhs.denominator) { |
|---|
| 310 |
this.numerator -= rhs.numerator; |
|---|
| 311 |
simplify(); |
|---|
| 312 |
return this; |
|---|
| 313 |
} |
|---|
| 314 |
|
|---|
| 315 |
auto commonDenom = lcm(this.denominator, rhs.denominator); |
|---|
| 316 |
this.numerator *= commonDenom / this.denominator; |
|---|
| 317 |
this.numerator -= (commonDenom / rhs.denominator) * rhs.numerator; |
|---|
| 318 |
this.denominator = commonDenom; |
|---|
| 319 |
|
|---|
| 320 |
simplify(); |
|---|
| 321 |
return this; |
|---|
| 322 |
} |
|---|
| 323 |
|
|---|
| 324 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 325 |
if(op == "-" && isIntegerLike!Rhs) { |
|---|
| 326 |
this.numerator -= rhs * this.denominator; |
|---|
| 327 |
|
|---|
| 328 |
simplify(); |
|---|
| 329 |
return this; |
|---|
| 330 |
} |
|---|
| 331 |
|
|---|
| 332 |
typeof(this) opBinaryRight(string op, Rhs)(Rhs rhs) |
|---|
| 333 |
if(op == "-" && is(CommonInteger!(Int, Rhs)) && isIntegerLike!Rhs) { |
|---|
| 334 |
typeof(this) ret; |
|---|
| 335 |
ret.denominator = this.denominator; |
|---|
| 336 |
ret.numerator = (rhs * this.denominator) - this.numerator; |
|---|
| 337 |
|
|---|
| 338 |
simplify(); |
|---|
| 339 |
return ret; |
|---|
| 340 |
} |
|---|
| 341 |
|
|---|
| 342 |
// ----------------------Unary operators--------------------------------------- |
|---|
| 343 |
|
|---|
| 344 |
typeof(this) opUnary(string op)() if(op == "-" || op == "+") { |
|---|
| 345 |
mixin("return typeof(this)(" ~ op ~ "numerator, denominator);"); |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
// ---------------------Exponentiation operator--------------------------------- |
|---|
| 349 |
|
|---|
| 350 |
// Can only handle integer powers if the result has to also be |
|---|
| 351 |
// rational. |
|---|
| 352 |
|
|---|
| 353 |
typeof(this) opOpAssign(string op, Rhs)(Rhs rhs) |
|---|
| 354 |
if(op == "^^" && isIntegerLike!Rhs) { |
|---|
| 355 |
if(rhs < 0) { |
|---|
| 356 |
this.invert(); |
|---|
| 357 |
rhs *= -1; |
|---|
| 358 |
} |
|---|
| 359 |
|
|---|
| 360 |
/* Don't need to simplify here. this is already simplified, meaning |
|---|
| 361 |
the numerator and denominator don't have any common factors. Raising |
|---|
| 362 |
both to a positive integer power won't create any. |
|---|
| 363 |
*/ |
|---|
| 364 |
numerator ^^= rhs; |
|---|
| 365 |
denominator ^^= rhs; |
|---|
| 366 |
return this; |
|---|
| 367 |
} |
|---|
| 368 |
|
|---|
| 369 |
auto opBinary(string op, Rhs)(Rhs rhs) |
|---|
| 370 |
if(op == "^^" && isIntegerLike!Rhs && is(CommonRational!(Int, Rhs))) { |
|---|
| 371 |
auto ret = CommonRational!(Int, Rhs)(this.num, this.denom); |
|---|
| 372 |
ret ^^= rhs; |
|---|
| 373 |
return ret; |
|---|
| 374 |
} |
|---|
| 375 |
|
|---|
| 376 |
// ---------------------Assignment operators------------------------------------ |
|---|
| 377 |
typeof(this) opAssign(Rhs)(Rhs rhs) |
|---|
| 378 |
if(isIntegerLike!Rhs && isAssignable!(Int, Rhs)) { |
|---|
| 379 |
this.numerator = rhs; |
|---|
| 380 |
this.denominator = 1; |
|---|
| 381 |
return this; |
|---|
| 382 |
} |
|---|
| 383 |
|
|---|
| 384 |
typeof(this) opAssign(Rhs)(Rhs rhs) |
|---|
| 385 |
if(isRational!Rhs && isAssignable!(Int, typeof(Rhs.num))) { |
|---|
| 386 |
this.numerator = rhs.num; |
|---|
| 387 |
this.denominator = rhs.denom; |
|---|
| 388 |
return this; |
|---|
| 389 |
} |
|---|
| 390 |
|
|---|
| 391 |
// --------------------Comparison/Equality Operators--------------------------- |
|---|
| 392 |
|
|---|
| 393 |
bool opEquals(Rhs)(Rhs rhs) if(isRational!Rhs || isIntegerLike!Rhs) { |
|---|
| 394 |
static if(isRational!Rhs) { |
|---|
| 395 |
return rhs.numerator == this.numerator && |
|---|
| 396 |
rhs.denominator == this.denominator; |
|---|
| 397 |
} else { |
|---|
| 398 |
static assert(isIntegerLike!Rhs); |
|---|
| 399 |
return rhs == this.numerator && this.denominator == 1; |
|---|
| 400 |
} |
|---|
| 401 |
} |
|---|
| 402 |
|
|---|
| 403 |
int opCmp(Rhs)(Rhs rhs) if(isRational!Rhs) { |
|---|
| 404 |
if( opEquals(rhs)) { |
|---|
| 405 |
return 0; |
|---|
| 406 |
} |
|---|
| 407 |
|
|---|
| 408 |
// Check a few obvious cases first, see if we can avoid having to use a |
|---|
| 409 |
// common denominator. These are basically speed hacks. |
|---|
| 410 |
|
|---|
| 411 |
// Assumption: When simplify() is called, rational will be written in |
|---|
| 412 |
// canonical form, with any negative signs being only in the numerator. |
|---|
| 413 |
if(this.numerator < 0 && rhs.numerator > 0) { |
|---|
| 414 |
return -1; |
|---|
| 415 |
} else if(this.numerator > 0 && rhs.numerator < 0) { |
|---|
| 416 |
return 1; |
|---|
| 417 |
} else if(this.numerator >= rhs.numerator && |
|---|
| 418 |
this.denominator <= rhs.denominator) { |
|---|
| 419 |
// We've already ruled out equality, so this must be > rhs. |
|---|
| 420 |
return 1; |
|---|
| 421 |
} else if(rhs.numerator >= this.numerator && |
|---|
| 422 |
rhs.denominator <= this.denominator) { |
|---|
| 423 |
return -1; |
|---|
| 424 |
} |
|---|
| 425 |
|
|---|
| 426 |
// Can't do it without common denominator. Argh. |
|---|
| 427 |
auto commonDenom = lcm(this.denominator, rhs.denominator); |
|---|
| 428 |
auto lhsNum = this.numerator * (commonDenom / this.denominator); |
|---|
| 429 |
auto rhsNum = rhs.numerator * (commonDenom / rhs.denominator); |
|---|
| 430 |
|
|---|
| 431 |
if(lhsNum > rhsNum) { |
|---|
| 432 |
return 1; |
|---|
| 433 |
} else if(lhsNum < rhsNum) { |
|---|
| 434 |
return -1; |
|---|
| 435 |
} |
|---|
| 436 |
|
|---|
| 437 |
// We've checked for equality already. If we get to this point, |
|---|
| 438 |
// there's clearly something wrong. |
|---|
| 439 |
assert(0); |
|---|
| 440 |
} |
|---|
| 441 |
|
|---|
| 442 |
int opCmp(Rhs)(Rhs rhs) if(isIntegerLike!Rhs) { |
|---|
| 443 |
if( opEquals(rhs)) { |
|---|
| 444 |
return 0; |
|---|
| 445 |
} |
|---|
| 446 |
|
|---|
| 447 |
// Again, check the obvious cases first. |
|---|
| 448 |
if(rhs >= this.numerator) { |
|---|
| 449 |
return -1; |
|---|
| 450 |
} |
|---|
| 451 |
|
|---|
| 452 |
rhs *= this.denominator; |
|---|
| 453 |
if(rhs > this.numerator) { |
|---|
| 454 |
return -1; |
|---|
| 455 |
} else if(rhs < this.numerator) { |
|---|
| 456 |
return 1; |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
// Already checked for equality. If we get here, something's wrong. |
|---|
| 460 |
assert(0); |
|---|
| 461 |
} |
|---|
| 462 |
|
|---|
| 463 |
/////////////////////////////////////////////////////////////////////////////// |
|---|
| 464 |
|
|---|
| 465 |
/**Fast inversion, equivalent to 1 / rational.*/ |
|---|
| 466 |
typeof(this) invert() { |
|---|
| 467 |
swap(numerator, denominator); |
|---|
| 468 |
return this; |
|---|
| 469 |
} |
|---|
| 470 |
|
|---|
| 471 |
/**Convert to floating point representation.*/ |
|---|
| 472 |
F opCast(F)() if(isFloatingPoint!F) { |
|---|
| 473 |
// Do everything in real precision, then convert to F at the end. |
|---|
| 474 |
|
|---|
| 475 |
static if(isIntegral!(Int)) { |
|---|
| 476 |
return cast(real) numerator / denominator; |
|---|
| 477 |
} else { |
|---|
| 478 |
auto temp = this; |
|---|
| 479 |
real expon = 1.0; |
|---|
| 480 |
real ans = 0; |
|---|
| 481 |
byte sign = 1; |
|---|
| 482 |
if(temp.numerator < 0) { |
|---|
| 483 |
temp.numerator *= -1; |
|---|
| 484 |
sign = -1; |
|---|
| 485 |
} |
|---|
| 486 |
|
|---|
| 487 |
while(temp.numerator > 0) { |
|---|
| 488 |
while(temp.numerator < temp.denominator) { |
|---|
| 489 |
|
|---|
| 490 |
assert(temp.denominator > 0); |
|---|
| 491 |
|
|---|
| 492 |
static if(is(typeof(temp.denominator & 1))) { |
|---|
| 493 |
// Try to make numbers smaller instead of bigger. |
|---|
| 494 |
if((temp.denominator & 1) == 0) { |
|---|
| 495 |
temp.denominator >>= 1; |
|---|
| 496 |
} else { |
|---|
| 497 |
temp.numerator <<= 1; |
|---|
| 498 |
} |
|---|
| 499 |
} else { |
|---|
| 500 |
temp.numerator <<= 1; |
|---|
| 501 |
} |
|---|
| 502 |
|
|---|
| 503 |
expon *= 0.5; |
|---|
| 504 |
|
|---|
| 505 |
// This checks for overflow in case we're working with a |
|---|
| 506 |
// user-defined fixed-precision integer. |
|---|
| 507 |
enforce(temp.numerator > 0, text( |
|---|
| 508 |
"Overflow while converting ", typeof(this).stringof, |
|---|
| 509 |
" to ", F.stringof, ".")); |
|---|
| 510 |
|
|---|
| 511 |
} |
|---|
| 512 |
|
|---|
| 513 |
auto intPart = temp.numerator / temp.denominator; |
|---|
| 514 |
|
|---|
| 515 |
static if(is(Int == std.bigint.BigInt)) { |
|---|
| 516 |
// This should really be a cast, but BigInt still has a few |
|---|
| 517 |
// issues. |
|---|
| 518 |
long lIntPart = intPart.toLong(); |
|---|
| 519 |
} else { |
|---|
| 520 |
long lIntPart = cast(long) intPart; |
|---|
| 521 |
} |
|---|
| 522 |
|
|---|
| 523 |
// Test for changes. |
|---|
| 524 |
real oldAns = ans; |
|---|
| 525 |
ans += lIntPart * expon; |
|---|
| 526 |
if(ans == oldAns) { // Smaller than epsilon. |
|---|
| 527 |
return ans * sign; |
|---|
| 528 |
} |
|---|
| 529 |
|
|---|
| 530 |
// Subtract out int part. |
|---|
| 531 |
temp.numerator -= intPart * temp.denominator; |
|---|
| 532 |
} |
|---|
| 533 |
|
|---|
| 534 |
return ans * sign; |
|---|
| 535 |
} |
|---|
| 536 |
} |
|---|
| 537 |
|
|---|
| 538 |
/**Casts this to an integer by truncating the fractional part. Equivalent |
|---|
| 539 |
* to $(D integerPart), and then casting it to type $(D I). |
|---|
| 540 |
*/ |
|---|
| 541 |
I opCast(I)() if(isIntegerLike!I && is(typeof(cast(I) Int.init))) { |
|---|
| 542 |
return cast(I) integerPart; |
|---|
| 543 |
} |
|---|
| 544 |
|
|---|
| 545 |
/**Returns the numerator.*/ |
|---|
| 546 |
@property Int num() { |
|---|
| 547 |
return numerator; |
|---|
| 548 |
} |
|---|
| 549 |
|
|---|
| 550 |
/**Returns the denominator.*/ |
|---|
| 551 |
@property Int denom() { |
|---|
| 552 |
return denominator; |
|---|
| 553 |
} |
|---|
| 554 |
|
|---|
| 555 |
/**Returns the integer part of this rational, with any remainder truncated. |
|---|
| 556 |
*/ |
|---|
| 557 |
@property Int integerPart() { |
|---|
| 558 |
return num / denom; |
|---|
| 559 |
} |
|---|
| 560 |
|
|---|
| 561 |
/**Returns the fractional part of this rational.*/ |
|---|
| 562 |
@property typeof(this) fractionPart() { |
|---|
| 563 |
return this - integerPart; |
|---|
| 564 |
} |
|---|
| 565 |
|
|---|
| 566 |
string toString() { |
|---|
| 567 |
static if(is(Int == std.bigint.BigInt)) { |
|---|
| 568 |
// Special case it for now. This should be fixed later. |
|---|
| 569 |
return toDecimalString(numerator) ~ " / " ~ |
|---|
| 570 |
toDecimalString(denominator); |
|---|
| 571 |
} else { |
|---|
| 572 |
return to!string(numerator) ~ " / " ~ to!string(denominator); |
|---|
| 573 |
} |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
private : |
|---|
| 577 |
Int numerator; |
|---|
| 578 |
Int denominator; |
|---|
| 579 |
|
|---|
| 580 |
void simplify() { |
|---|
| 581 |
if(numerator == 0) { |
|---|
| 582 |
denominator = 1; |
|---|
| 583 |
return; |
|---|
| 584 |
} |
|---|
| 585 |
|
|---|
| 586 |
auto divisor = gcf(numerator, denominator); |
|---|
| 587 |
numerator /= divisor; |
|---|
| 588 |
denominator /= divisor; |
|---|
| 589 |
|
|---|
| 590 |
fixSigns(); |
|---|
| 591 |
} |
|---|
| 592 |
|
|---|
| 593 |
void fixSigns() { |
|---|
| 594 |
static if( !is(Int == ulong) && !is(Int == uint) && |
|---|
| 595 |
!is(Int == ushort) && !is(Int == ubyte)) { |
|---|
| 596 |
// Write in canonical form w.r.t. signs. |
|---|
| 597 |
if(denominator < 0) { |
|---|
| 598 |
denominator *= -1; |
|---|
| 599 |
numerator *= -1; |
|---|
| 600 |
} |
|---|
| 601 |
} |
|---|
| 602 |
} |
|---|
| 603 |
} |
|---|
| 604 |
|
|---|
| 605 |
unittest { |
|---|
| 606 |
// All reference values from the Maxima computer algebra system. |
|---|
| 607 |
|
|---|
| 608 |
// Test c'tor and simplification first. |
|---|
| 609 |
auto num = BigInt("295147905179352825852"); |
|---|
| 610 |
auto den = BigInt("147573952589676412920"); |
|---|
| 611 |
auto simpNum = BigInt("24595658764946068821"); |
|---|
| 612 |
auto simpDen = BigInt("12297829382473034410"); |
|---|
| 613 |
auto f1 = rational(num, den); |
|---|
| 614 |
auto f2 = rational(simpNum, simpDen); |
|---|
| 615 |
assert(f1 == f2); |
|---|
| 616 |
|
|---|
| 617 |
// Test multiplication. |
|---|
| 618 |
assert( rational(8, 42) * rational(cast(byte) 7, cast(byte) 68) |
|---|
| 619 |
== rational(1, 51)); |
|---|
| 620 |
assert(rational(20_000L, 3_486_784_401U) * rational(3_486_784_401U, 1_000U) |
|---|
| 621 |
== rational(20, 1)); |
|---|
| 622 |
auto f3 = rational(7, 57); |
|---|
| 623 |
f3 *= rational(2, 78); |
|---|
| 624 |
assert(f3 == rational(7, 2223)); |
|---|
| 625 |
f3 = 5 * f3; |
|---|
| 626 |
assert(f3 == rational(35, 2223)); |
|---|
| 627 |
assert(f3 * 5UL == 5 * f3); |
|---|
| 628 |
|
|---|
| 629 |
// Test division. Since it's implemented in terms of multiplication, |
|---|
| 630 |
// quick and dirty tests should be good enough. |
|---|
| 631 |
assert( rational(7, 38) / rational(8, 79) == rational(553, 304)); |
|---|
| 632 |
assert( rational(7, 38) / rational(8, 79) == rational(553, 304)); |
|---|
| 633 |
auto f4 = rational(7, 38); |
|---|
| 634 |
f4 /= rational(8UL, 79); |
|---|
| 635 |
assert(f4 == rational(553, 304)); |
|---|
| 636 |
f4 = f4 / 2; |
|---|
| 637 |
assert(f4 == rational(553, 608)); |
|---|
| 638 |
f4 = 2 / f4; |
|---|
| 639 |
assert(f4 == rational(1216, 553)); |
|---|
| 640 |
assert(f4 * 2 == f4 * rational(2)); |
|---|
| 641 |
f4 = 2; |
|---|
| 642 |
assert(f4 == 2); |
|---|
| 643 |
|
|---|
| 644 |
// Test addition. |
|---|
| 645 |
assert( rational(1, 3) + rational(cast(byte) 2, cast(byte) 3) == rational(1, 1)); |
|---|
| 646 |
assert( rational(1, 3) + rational(1, 2L) == rational(5, 6)); |
|---|
| 647 |
auto f5 = rational( BigInt("314159265"), BigInt("27182818")); |
|---|
| 648 |
auto f6 = rational( BigInt("8675309"), BigInt("362436")); |
|---|
| 649 |
f5 += f6; |
|---|
| 650 |
assert(f5 == rational( BigInt("174840986505151"), BigInt("4926015912324"))); |
|---|
| 651 |
assert( rational(1, 3) + 2UL == rational(7, 3)); |
|---|
| 652 |
assert( 5UL + rational(1, 5) == rational(26, 5)); |
|---|
| 653 |
|
|---|
| 654 |
// Test subtraction. |
|---|
| 655 |
assert( rational(2, 3) - rational(1, 3) == rational(1, 3UL)); |
|---|
| 656 |
assert( rational(1UL, 2) - rational(1, 3) == rational(1, 6)); |
|---|
| 657 |
f5 = rational( BigInt("314159265"), BigInt("27182818")); |
|---|
| 658 |
f5 -= f6; |
|---|
| 659 |
assert(f5 == rational( BigInt("-60978359135611"), BigInt("4926015912324"))); |
|---|
| 660 |
assert( rational(4, 3) - 1 == rational(1, 3)); |
|---|
| 661 |
assert(1 - rational(1, 4) == rational(3, 4)); |
|---|
| 662 |
|
|---|
| 663 |
// Test unary operators. |
|---|
| 664 |
auto fExp = rational(2, 5); |
|---|
| 665 |
assert(-fExp == rational(-2, 5)); |
|---|
| 666 |
assert(+fExp == rational(2, 5)); |
|---|
| 667 |
|
|---|
| 668 |
// Test exponentiation. |
|---|
| 669 |
fExp ^^= 3; |
|---|
| 670 |
assert(fExp == rational(8, 125)); |
|---|
| 671 |
fExp = fExp ^^ 2; |
|---|
| 672 |
assert(fExp == rational(64, 125 * 125)); |
|---|
| 673 |
assert(rational(2, 5) ^^ -2 == rational(25, 4)); |
|---|
| 674 |
|
|---|
| 675 |
// Test decimal conversion. |
|---|
| 676 |
assert(approxEqual(cast(real) f5, -12.37883925284411L)); |
|---|
| 677 |
|
|---|
| 678 |
// Test comparison. |
|---|
| 679 |
assert(rational(1UL, 6) < rational(1, 2)); |
|---|
| 680 |
assert(rational(cast(byte) 1, cast(byte) 2) > rational(1, 6)); |
|---|
| 681 |
assert(rational(-1, 7) < rational(7, 2)); |
|---|
| 682 |
assert(rational(7, 2) > rational(-1, 7)); |
|---|
| 683 |
assert(rational(7, 9) > rational(8, 11)); |
|---|
| 684 |
assert(rational(8, 11) < rational(7, 9)); |
|---|
| 685 |
|
|---|
| 686 |
assert(rational(9, 10) < 1UL); |
|---|
| 687 |
assert(1UL > rational(9, 10)); |
|---|
| 688 |
assert(10 > rational(9L, 10)); |
|---|
| 689 |
assert(2 > rational(5, 4)); |
|---|
| 690 |
assert(1 < rational(5U, 4)); |
|---|
| 691 |
|
|---|
| 692 |
// Test creating rationals of value zero. |
|---|
| 693 |
auto zero = rational(0, 8); |
|---|
| 694 |
assert(zero == 0); |
|---|
| 695 |
assert(zero == rational(0, 16)); |
|---|
| 696 |
assert(zero.num == 0); |
|---|
| 697 |
assert(zero.denom == 1); |
|---|
| 698 |
auto one = zero + 1; |
|---|
| 699 |
one -= one; |
|---|
| 700 |
assert(one == zero); |
|---|
| 701 |
|
|---|
| 702 |
// Test integerPart, fraction part. |
|---|
| 703 |
auto intFract = rational(5, 4); |
|---|
| 704 |
assert(intFract.integerPart == 1); |
|---|
| 705 |
assert(intFract.fractionPart == rational(1, 4)); |
|---|
| 706 |
assert(cast(long) intFract == 1); |
|---|
| 707 |
|
|---|
| 708 |
// Test whether CTFE works for primitive types. Doesn't work yet. |
|---|
| 709 |
// enum myRational = (((rational(1, 2) + rational(1, 4)) * 2 - rational(1, 4)) |
|---|
| 710 |
// / 2 + 1 * rational(1, 2) - 1) / rational(2, 5); |
|---|
| 711 |
// writeln(myRational); |
|---|
| 712 |
// static assert(myRational == rational(-15, 32)); |
|---|
| 713 |
} |
|---|
| 714 |
|
|---|
| 715 |
/**Convert a floating point number to a Rational based on integer type Int. |
|---|
| 716 |
* Allows an error tolerance of epsilon. (Default epsilon = 1e-8.) |
|---|
| 717 |
* |
|---|
| 718 |
* epsilon must be greater than 1.0L / long.max. |
|---|
| 719 |
* |
|---|
| 720 |
* Throws: Exception on infinities, NaNs, numbers with absolute value |
|---|
| 721 |
* larger than long.max and epsilons smaller than 1.0L / long.max. |
|---|
| 722 |
* |
|---|
| 723 |
* Examples: |
|---|
| 724 |
* --- |
|---|
| 725 |
* // Prints "22 / 7". |
|---|
| 726 |
* writeln( toRational!int( PI, 1e-1)); |
|---|
| 727 |
* --- |
|---|
| 728 |
*/ |
|---|
| 729 |
Rational!(Int) toRational(Int)(real floatNum, real epsilon = 1e-8) { |
|---|
| 730 |
enforce(floatNum != real.infinity && floatNum != -real.infinity |
|---|
| 731 |
&& !isNaN(floatNum), "Can't convert NaNs and infinities to rational."); |
|---|
| 732 |
enforce(floatNum < long.max && floatNum > -long.max, |
|---|
| 733 |
"Rational conversions of very large numbers not yet implemented."); |
|---|
| 734 |
enforce(1.0L / epsilon < long.max, |
|---|
| 735 |
"Can't handle very small epsilons < long.max in toRational."); |
|---|
| 736 |
|
|---|
| 737 |
// Handle this as a special case to make the rest of the code less |
|---|
| 738 |
// complicated: |
|---|
| 739 |
if( abs(floatNum) < epsilon) { |
|---|
| 740 |
Rational!Int ret; |
|---|
| 741 |
ret.numerator = 0; |
|---|
| 742 |
ret.denominator = 1; |
|---|
| 743 |
return ret; |
|---|
| 744 |
} |
|---|
| 745 |
|
|---|
| 746 |
return toRationalImpl!(Int)(floatNum, epsilon); |
|---|
| 747 |
} |
|---|
| 748 |
|
|---|
| 749 |
private Rational!Int toRationalImpl(Int)(real floatNum, real epsilon) { |
|---|
| 750 |
real actualEpsilon; |
|---|
| 751 |
Rational!Int ret; |
|---|
| 752 |
|
|---|
| 753 |
if( abs(floatNum) < 1) { |
|---|
| 754 |
real invFloatNum = 1.0L / floatNum; |
|---|
| 755 |
long intPart = roundTo!long(invFloatNum); |
|---|
| 756 |
actualEpsilon = floatNum - 1.0L / intPart; |
|---|
| 757 |
|
|---|
| 758 |
static if(isIntegral!(Int)) { |
|---|
| 759 |
ret.denominator = cast(Int) intPart; |
|---|
| 760 |
ret.numerator = cast(Int) 1; |
|---|
| 761 |
} else { |
|---|
| 762 |
ret.denominator = intPart; |
|---|
| 763 |
ret.numerator = 1; |
|---|
| 764 |
} |
|---|
| 765 |
} else { |
|---|
| 766 |
long intPart = roundTo!long(floatNum); |
|---|
| 767 |
actualEpsilon = floatNum - intPart; |
|---|
| 768 |
|
|---|
| 769 |
static if(isIntegral!(Int)) { |
|---|
| 770 |
ret.denominator = cast(Int) 1; |
|---|
| 771 |
ret.numerator = cast(Int) intPart; |
|---|
| 772 |
} else { |
|---|
| 773 |
ret.denominator = 1; |
|---|
| 774 |
ret.numerator = intPart; |
|---|
| 775 |
} |
|---|
| 776 |
} |
|---|
| 777 |
|
|---|
| 778 |
if(abs(actualEpsilon) <= epsilon) { |
|---|
| 779 |
return ret; |
|---|
| 780 |
} |
|---|
| 781 |
|
|---|
| 782 |
// Else get results from downstream recursions, add them to this result. |
|---|
| 783 |
return ret + toRationalImpl!(Int)(actualEpsilon, epsilon); |
|---|
| 784 |
} |
|---|
| 785 |
|
|---|
| 786 |
unittest { |
|---|
| 787 |
// Start with simple cases. |
|---|
| 788 |
assert( toRational!int(0.5) == rational(1, 2)); |
|---|
| 789 |
assert( toRational!BigInt(0.333333333333333L) == |
|---|
| 790 |
rational( BigInt(1), BigInt(3))); |
|---|
| 791 |
assert( toRational!int(2.470588235294118) == |
|---|
| 792 |
rational( cast(int) 42, cast(int) 17)); |
|---|
| 793 |
assert( toRational!long(2.007874015748032) == rational(255L, 127L)); |
|---|
| 794 |
assert( toRational!int( 3.0L / 7.0L) == rational(3, 7)); |
|---|
| 795 |
assert( toRational!int( 7.0L / 3.0L) == rational(7, 3)); |
|---|
| 796 |
|
|---|
| 797 |
// Now for some fun. |
|---|
| 798 |
real myEpsilon = 1e-8; |
|---|
| 799 |
auto piRational = toRational!long(PI, myEpsilon); |
|---|
| 800 |
assert( abs( cast(real) piRational - PI) < myEpsilon); |
|---|
| 801 |
|
|---|
| 802 |
auto eRational = toRational!long(E, myEpsilon); |
|---|
| 803 |
assert( abs( cast(real) eRational - E) < myEpsilon); |
|---|
| 804 |
} |
|---|
| 805 |
|
|---|
| 806 |
|
|---|
| 807 |
/**Find the greatest common factor of num1 and num2 using Euclid's Algorithm.*/ |
|---|
| 808 |
CommonInteger!(I1, I2) gcf(I1, I2)(I1 num1, I2 num2) |
|---|
| 809 |
if(isIntegerLike!I1 && isIntegerLike!I2) { |
|---|
| 810 |
num1 = iAbs(num1); |
|---|
| 811 |
num2 = iAbs(num2); |
|---|
| 812 |
if(num2 > num1) { |
|---|
| 813 |
return gcf(num2, num1); |
|---|
| 814 |
} else if(num2 == num1) { |
|---|
| 815 |
typeof(return) ret = num1; |
|---|
| 816 |
return ret; |
|---|
| 817 |
} |
|---|
| 818 |
|
|---|
| 819 |
// Work around Bug 4742. |
|---|
| 820 |
static if(is(I1 == I2)) { |
|---|
| 821 |
auto remainder = num1 % num2; |
|---|
| 822 |
} else { |
|---|
| 823 |
typeof(return) workaround1 = num1; |
|---|
| 824 |
typeof(return) workaround2 = num2; |
|---|
| 825 |
auto remainder = workaround1 % workaround2; |
|---|
| 826 |
} |
|---|
| 827 |
|
|---|
| 828 |
if(remainder == 0) { |
|---|
| 829 |
typeof(return) ret = num2; |
|---|
| 830 |
return ret; |
|---|
| 831 |
} else { |
|---|
| 832 |
return gcf(num2, remainder); |
|---|
| 833 |
} |
|---|
| 834 |
assert(0); |
|---|
| 835 |
} |
|---|
| 836 |
|
|---|
| 837 |
unittest { |
|---|
| 838 |
// Values from the Maxima computer algebra system. |
|---|
| 839 |
assert(gcf( BigInt(314_156_535UL), BigInt(27_182_818_284UL)) == BigInt(3)); |
|---|
| 840 |
assert(gcf(8675309, 362436) == 1); |
|---|
| 841 |
assert(gcf( BigInt("8589934596"), BigInt("295147905179352825852")) == |
|---|
| 842 |
12); |
|---|
| 843 |
} |
|---|
| 844 |
|
|---|
| 845 |
/**Find the least common multiple of num1, num2.*/ |
|---|
| 846 |
CommonInteger!(I1, I2) lcm(I1, I2)(I1 num1, I2 num2) |
|---|
| 847 |
if(isIntegerLike!I1 && isIntegerLike!I2) { |
|---|
| 848 |
num1 = iAbs(num1); |
|---|
| 849 |
num2 = iAbs(num2); |
|---|
| 850 |
if(num1 == num2) { |
|---|
| 851 |
return num1; |
|---|
| 852 |
} |
|---|
| 853 |
return (num1 / gcf(num1, num2)) * num2; |
|---|
| 854 |
} |
|---|
| 855 |
|
|---|
| 856 |
/**Absolute value function that should gracefully handle any reasonable |
|---|
| 857 |
* BigInt implementation.*/ |
|---|
| 858 |
Int iAbs(Int)(Int num1) |
|---|
| 859 |
if(isIntegerLike!Int) { |
|---|
| 860 |
static if(isUnsigned!Int) { |
|---|
| 861 |
return num1; |
|---|
| 862 |
} else { |
|---|
| 863 |
// For some reason DMD insists that a byte multipled by -1 is an int |
|---|
| 864 |
// not a byte. |
|---|
| 865 |
return cast(Int) ((num1 < 0) ? -1 * num1 : num1); |
|---|
| 866 |
} |
|---|
| 867 |
} |
|---|
| 868 |
|
|---|
| 869 |
/**Returns the largest integer less than or equal to $(D r).*/ |
|---|
| 870 |
Int floor(Int)(Rational!Int r) { |
|---|
| 871 |
auto intPart = r.integerPart; |
|---|
| 872 |
if(r > 0 || intPart == r) { |
|---|
| 873 |
return intPart; |
|---|
| 874 |
} else { |
|---|
| 875 |
intPart -= 1; |
|---|
| 876 |
return intPart; |
|---|
| 877 |
} |
|---|
| 878 |
} |
|---|
| 879 |
|
|---|
| 880 |
unittest { |
|---|
| 881 |
assert(floor(rational(1, 2)) == 0); |
|---|
| 882 |
assert(floor(rational(-1, 2)) == -1); |
|---|
| 883 |
assert(floor(rational(2)) == 2); |
|---|
| 884 |
assert(floor(rational(-2)) == -2); |
|---|
| 885 |
assert(floor(rational(-1, 2)) == -1); |
|---|
| 886 |
} |
|---|
| 887 |
|
|---|
| 888 |
/**Returns the smallest integer greater than or equal to $(D r).*/ |
|---|
| 889 |
Int ceil(Int)(Rational!Int r) { |
|---|
| 890 |
auto intPart = r.integerPart; |
|---|
| 891 |
if(intPart == r || r < 0) { |
|---|
| 892 |
return intPart; |
|---|
| 893 |
} else { |
|---|
| 894 |
intPart += 1; |
|---|
| 895 |
return intPart; |
|---|
| 896 |
} |
|---|
| 897 |
} |
|---|
| 898 |
|
|---|
| 899 |
unittest { |
|---|
| 900 |
assert(ceil(rational(1, 2)) == 1); |
|---|
| 901 |
assert(ceil(rational(0)) == 0); |
|---|
| 902 |
assert(ceil(rational(-1, 2)) == 0); |
|---|
| 903 |
assert(ceil(rational(1)) == 1); |
|---|
| 904 |
assert(ceil(rational(-2)) == -2); |
|---|
| 905 |
} |
|---|
| 906 |
|
|---|
| 907 |
/**Round $(D r) to the nearest integer. If the fractional part is exactly |
|---|
| 908 |
* 1 / 2, $(D r) will be rounded such that the absolute value is increased by |
|---|
| 909 |
* rounding. |
|---|
| 910 |
*/ |
|---|
| 911 |
Int round(Int)(Rational!Int r) { |
|---|
| 912 |
auto intPart = r.integerPart; |
|---|
| 913 |
auto fractPart = r.fractionPart; |
|---|
| 914 |
|
|---|
| 915 |
bool added; |
|---|
| 916 |
if(fractPart >= rational(1, 2)) { |
|---|
| 917 |
added = true; |
|---|
| 918 |
intPart += 1; |
|---|
| 919 |
} |
|---|
| 920 |
|
|---|
| 921 |
static if(!isUnsigned!Int) { |
|---|
| 922 |
if(!added && fractPart <= rational(-1, 2)) { |
|---|
| 923 |
intPart -= 1; |
|---|
| 924 |
} |
|---|
| 925 |
} |
|---|
| 926 |
|
|---|
| 927 |
return intPart; |
|---|
| 928 |
} |
|---|
| 929 |
|
|---|
| 930 |
unittest { |
|---|
| 931 |
assert(round(rational(1, 3)) == 0); |
|---|
| 932 |
assert(round(rational(7, 2)) == 4); |
|---|
| 933 |
assert(round(rational(-3, 4)) == -1); |
|---|
| 934 |
assert(round(rational(8U, 15U)) == 1); |
|---|
| 935 |
} |
|---|
| 936 |
|
|---|
| 937 |
version(unittest) { |
|---|
| 938 |
void main() {} |
|---|
| 939 |
} |
|---|