Fraction.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. // TODO: Check the operators' names
  2. // TODO: This class should probably be immutable?
  3. /**
  4. * A class representing mathematical fractions, which have a numerator and a denominator.
  5. */
  6. export class Fraction {
  7. private static maximumAllowedNumber: number = 46340; // sqrt(int.Max) --> signed int with 4 bytes (2^31)
  8. private numerator: number = 0;
  9. private denominator: number = 1;
  10. private wholeValue: number = 0;
  11. private realValue: number;
  12. /**
  13. * Returns the maximum of two fractions (does not clone)
  14. * @param f1
  15. * @param f2
  16. * @returns {Fraction}
  17. */
  18. public static max(f1: Fraction, f2: Fraction): Fraction {
  19. if (f1.RealValue > f2.RealValue) {
  20. return f1;
  21. } else {
  22. return f2;
  23. }
  24. }
  25. public static Equal(f1: Fraction, f2: Fraction): boolean {
  26. return f1.wholeValue === f2.wholeValue && f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
  27. }
  28. /**
  29. * The same as Fraction.clone
  30. * @param fraction
  31. * @returns {Fraction}
  32. */
  33. public static createFromFraction(fraction: Fraction): Fraction {
  34. return new Fraction(fraction.numerator, fraction.denominator, fraction.wholeValue, false);
  35. }
  36. public static plus(f1: Fraction, f2: Fraction): Fraction {
  37. const sum: Fraction = f1.clone();
  38. sum.Add(f2);
  39. return sum;
  40. }
  41. public static minus(f1: Fraction, f2: Fraction): Fraction {
  42. const sum: Fraction = f1.clone();
  43. sum.Sub(f2);
  44. return sum;
  45. }
  46. public static multiply (f1: Fraction, f2: Fraction): Fraction {
  47. return new Fraction ( (f1.wholeValue * f1.denominator + f1.numerator) * (f2.wholeValue * f2.denominator + f2.numerator),
  48. f1.denominator * f2.denominator);
  49. }
  50. private static greatestCommonDenominator(a: number, b: number): number {
  51. if (a === 0) {
  52. return b;
  53. }
  54. if (b === 1) {
  55. return 1;
  56. }
  57. while (b !== 0) {
  58. if (a > b) {
  59. a -= b;
  60. } else {
  61. b -= a;
  62. }
  63. }
  64. return a;
  65. }
  66. /**
  67. *
  68. * @param numerator
  69. * @param denominator
  70. * @param wholeValue - the integer number, needed for values greater than 1
  71. * @param simplify - If simplify is true, then the fraction is simplified
  72. * to make both the numerator and denominator coprime, and less than maximumAllowedNumber.
  73. */
  74. constructor(numerator: number = 0, denominator: number = 1, wholeValue: number = 0, simplify: boolean = true) {
  75. this.numerator = numerator;
  76. this.denominator = denominator;
  77. this.wholeValue = wholeValue;
  78. if (simplify) {
  79. this.simplify();
  80. }
  81. this.setRealValue();
  82. }
  83. public toString(): string {
  84. let result: string = this.numerator + "/" + this.denominator;
  85. if (this.wholeValue !== 0) {
  86. result = this.wholeValue + " " + result;
  87. }
  88. return result;
  89. }
  90. public clone(): Fraction {
  91. return new Fraction(this.numerator, this.denominator, this.wholeValue, false);
  92. }
  93. public get Numerator(): number {
  94. return this.numerator;
  95. }
  96. public set Numerator(value: number) {
  97. if (this.numerator !== value) {
  98. this.numerator = value;
  99. this.simplify();
  100. this.setRealValue();
  101. }
  102. }
  103. public get Denominator(): number {
  104. return this.denominator;
  105. }
  106. public set Denominator(value: number) {
  107. if (this.denominator !== value) {
  108. this.denominator = value;
  109. if (this.numerator !== 0) {
  110. this.simplify();
  111. }
  112. this.setRealValue();
  113. }
  114. }
  115. public get WholeValue(): number {
  116. return this.wholeValue;
  117. }
  118. public set WholeValue(value: number) {
  119. if (this.wholeValue !== value) {
  120. this.wholeValue = value;
  121. this.setRealValue();
  122. }
  123. }
  124. public GetExpandedNumerator(): number {
  125. return this.wholeValue * this.denominator + this.numerator;
  126. }
  127. public IsNegative(): boolean {
  128. return this.realValue < 0;
  129. }
  130. public get RealValue(): number {
  131. return this.realValue;
  132. }
  133. public expand(expansionValue: number): void {
  134. this.numerator *= expansionValue;
  135. this.denominator *= expansionValue;
  136. if (this.wholeValue !== 0) {
  137. this.numerator += this.wholeValue * this.denominator;
  138. this.wholeValue = 0;
  139. }
  140. }
  141. // public multiplyDenominatorWithFactor(factor: number): void {
  142. // this.denominator *= factor;
  143. // this.setRealValue();
  144. // }
  145. public Add(fraction: Fraction): void {
  146. this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator +
  147. (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
  148. this.denominator = this.denominator * fraction.denominator;
  149. this.wholeValue = 0;
  150. this.simplify();
  151. this.setRealValue();
  152. }
  153. public Sub(fraction: Fraction): void {
  154. this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator -
  155. (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
  156. this.denominator = this.denominator * fraction.denominator;
  157. this.wholeValue = 0;
  158. this.simplify();
  159. this.setRealValue();
  160. }
  161. public Quantize(maxAllowedDenominator: number): Fraction {
  162. if (this.denominator <= maxAllowedDenominator) {
  163. return this;
  164. }
  165. const upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator, this.wholeValue);
  166. while (upTestFraction.Denominator > maxAllowedDenominator) {
  167. upTestFraction.Numerator++;
  168. }
  169. if (this.numerator > this.denominator) {
  170. const downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator, this.wholeValue);
  171. while (downTestFraction.Denominator > maxAllowedDenominator) {
  172. downTestFraction.Numerator--;
  173. }
  174. if (downTestFraction.Denominator < upTestFraction.Denominator) {
  175. return downTestFraction;
  176. }
  177. }
  178. return upTestFraction;
  179. }
  180. public Equals(obj: Fraction): boolean {
  181. return this.realValue === obj.realValue;
  182. }
  183. public CompareTo(obj: Fraction): number {
  184. const diff: number = this.realValue - obj.realValue;
  185. // Return the sign of diff
  186. return diff ? diff < 0 ? -1 : 1 : 0;
  187. }
  188. public lt(frac: Fraction): boolean {
  189. return this.realValue < frac.realValue;
  190. }
  191. public lte(frac: Fraction): boolean {
  192. return this.realValue <= frac.realValue;
  193. }
  194. //public Equals(f: Fraction): boolean {
  195. // if (ReferenceEquals(this, f))
  196. // return true;
  197. // if (ReferenceEquals(f, undefined))
  198. // return false;
  199. // return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
  200. //}
  201. private setRealValue(): void {
  202. this.realValue = this.wholeValue + this.numerator / this.denominator;
  203. }
  204. private simplify(): void {
  205. if (this.numerator === 0) {
  206. this.denominator = 1;
  207. return;
  208. }
  209. const i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
  210. this.numerator /= i;
  211. this.denominator /= i;
  212. const whole: number = Math.floor(this.numerator / this.denominator);
  213. if (whole !== 0) {
  214. this.wholeValue += whole;
  215. this.numerator -= whole * this.denominator;
  216. if (this.numerator === 0) {
  217. this.denominator = 1;
  218. }
  219. }
  220. if (this.denominator > Fraction.maximumAllowedNumber) {
  221. const factor: number = <number>this.denominator / Fraction.maximumAllowedNumber;
  222. this.numerator = <number>Math.round(this.numerator / factor);
  223. this.denominator = <number>Math.round(this.denominator / factor);
  224. }
  225. if (this.numerator > Fraction.maximumAllowedNumber) {
  226. const factor: number = <number>this.numerator / Fraction.maximumAllowedNumber;
  227. this.numerator = <number>Math.round(this.numerator / factor);
  228. this.denominator = <number>Math.round(this.denominator / factor);
  229. }
  230. }
  231. //private static equals(f1: Fraction, f2: Fraction): boolean {
  232. // return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
  233. //}
  234. //
  235. //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
  236. // let n: number = 1;
  237. // let d: number = 1;
  238. // let fraction: number = n / d;
  239. // while (Math.abs(fraction - value) > epsilonForPrecision) {
  240. // if (fraction < value) {
  241. // n++;
  242. // }
  243. // else {
  244. // d++;
  245. // n = <number>Math.round(value * d);
  246. // }
  247. // fraction = n / <number>d;
  248. // }
  249. // return new Fraction(n, d);
  250. //}
  251. //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
  252. // if (m1 < m2)
  253. // return m1;
  254. // else return m2;
  255. //}
  256. //public static getFraction(value: number, denominatorPrecision: number): Fraction {
  257. // let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
  258. // return new Fraction(numerator, denominatorPrecision);
  259. //}
  260. //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
  261. // if (f1 < f2)
  262. // return f1;
  263. // else return f2;
  264. //}
  265. //public static GetMaxValue(): Fraction {
  266. // return new Fraction(Fraction.maximumAllowedNumber, 1);
  267. //}
  268. //public static get MaxAllowedNumerator(): number {
  269. // return Fraction.maximumAllowedNumber;
  270. //}
  271. //public static get MaxAllowedDenominator(): number {
  272. // return Fraction.maximumAllowedNumber;
  273. //}
  274. //public ToFloatingString(): string {
  275. // return this.RealValue.ToString();
  276. //}
  277. //public Compare(x: Fraction, y: Fraction): number {
  278. // if (x > y)
  279. // return 1;
  280. // if (x < y)
  281. // return -1;
  282. // return 0;
  283. //}
  284. //#region operators
  285. //
  286. // // operator overloads must always come in pairs
  287. // // operator overload +
  288. // public static Fraction operator + (Fraction f1, Fraction f2)
  289. //{
  290. // Fraction sum = new Fraction(f1);
  291. // sum.Add(f2);
  292. // return sum;
  293. //}
  294. //
  295. //// operator overload -
  296. //public static Fraction operator - (Fraction f1, Fraction f2)
  297. //{
  298. // Fraction diff = new Fraction(f1);
  299. // diff.Sub(f2);
  300. // return diff;
  301. //}
  302. //
  303. //// operator overloads must always come in pairs
  304. //// operator overload >
  305. //public static bool operator > (Fraction f1, Fraction f2)
  306. //{
  307. // //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
  308. // return f1.RealValue > f2.RealValue;
  309. //}
  310. //
  311. //// operator overload <
  312. //public static bool operator < (Fraction f1, Fraction f2)
  313. //{
  314. // //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
  315. // return f1.RealValue < f2.RealValue;
  316. //}
  317. //
  318. //// operator overload ==
  319. //public static bool operator === (Fraction f1, Fraction f2)
  320. //{
  321. // // code enhanced for performance
  322. // // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
  323. // // and comparisons between booleans are quick
  324. // bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
  325. // bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
  326. //
  327. // // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
  328. // if (f1IsNull !== f2IsNull)
  329. // return false;
  330. //
  331. // if (f1IsNull /*&& f2IsNull*/)
  332. // return true;
  333. //
  334. // return equals(f1, f2);
  335. //}
  336. //
  337. //// operator overload !=
  338. //public static bool operator !== (Fraction f1, Fraction f2)
  339. //{
  340. // return (!(f1 === f2));
  341. //}
  342. //
  343. //// operator overload >=
  344. //public static bool operator >= (Fraction f1, Fraction f2)
  345. //{
  346. // return (!(f1 < f2));
  347. //}
  348. //
  349. //// operator overload <=
  350. //public static bool operator <= (Fraction f1,Fraction f2)
  351. //{
  352. // return (!(f1 > f2));
  353. //}
  354. //
  355. //public static Fraction operator / (Fraction f, int i)
  356. //{
  357. // return new Fraction(f._numerator, f._denominator *= i);
  358. //}
  359. //
  360. //public static Fraction operator / (Fraction f1, Fraction f2)
  361. //{
  362. // let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
  363. // return res.Denominator === 0 ? new Fraction(0, 1) : res;
  364. //}
  365. //
  366. //public static Fraction operator * (Fraction f1, Fraction f2)
  367. //{
  368. // return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
  369. //}
  370. //
  371. //public static Fraction operator % (Fraction f1, Fraction f2)
  372. //{
  373. // let a = f1/f2;
  374. // return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
  375. //}
  376. //
  377. //#endregion operators
  378. }