Fraction.ts 12 KB

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