Fraction.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. // don't simplify in case of a GraceNote (need it in order to set the right symbol)
  106. if (this.numerator !== 0) {
  107. this.simplify();
  108. }
  109. this.setRealValue();
  110. }
  111. }
  112. public get WholeValue(): number {
  113. return this.wholeValue;
  114. }
  115. public set WholeValue(value: number) {
  116. if (this.wholeValue !== value) {
  117. this.wholeValue = value;
  118. this.setRealValue();
  119. }
  120. }
  121. /**
  122. * Returns the unified numerator where the whole value will be expanded
  123. * with the denominator and added to the existing numerator.
  124. */
  125. public GetExpandedNumerator(): number {
  126. return this.wholeValue * this.denominator + this.numerator;
  127. }
  128. public IsNegative(): boolean {
  129. return this.realValue < 0;
  130. }
  131. public get RealValue(): number {
  132. return this.realValue;
  133. }
  134. public expand(expansionValue: number): void {
  135. this.numerator *= expansionValue;
  136. this.denominator *= expansionValue;
  137. if (this.wholeValue !== 0) {
  138. this.numerator += this.wholeValue * this.denominator;
  139. this.wholeValue = 0;
  140. }
  141. }
  142. // public multiplyDenominatorWithFactor(factor: number): void {
  143. // this.denominator *= factor;
  144. // this.setRealValue();
  145. // }
  146. public Add(fraction: Fraction): void {
  147. // normally should check if denominator or fraction.denominator is 0 but in our case
  148. // a zero denominator doesn't make sense
  149. this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator +
  150. (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
  151. this.denominator = this.denominator * fraction.denominator;
  152. this.wholeValue = 0;
  153. this.simplify();
  154. this.setRealValue();
  155. }
  156. public Sub(fraction: Fraction): void {
  157. // normally should check if denominator or fraction.denominator is 0 but in our case
  158. // a zero denominator doesn't make sense
  159. this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator -
  160. (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
  161. this.denominator = this.denominator * fraction.denominator;
  162. this.wholeValue = 0;
  163. this.simplify();
  164. this.setRealValue();
  165. }
  166. /**
  167. * Brute Force quanization by searching incremental with the numerator until the denominator is
  168. * smaller/equal than the desired one.
  169. * @param maxAllowedDenominator
  170. */
  171. public Quantize(maxAllowedDenominator: number): Fraction {
  172. if (this.denominator <= maxAllowedDenominator) {
  173. return this;
  174. }
  175. const upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator, this.wholeValue);
  176. while (upTestFraction.Denominator > maxAllowedDenominator) {
  177. upTestFraction.Numerator++;
  178. }
  179. if (this.numerator > this.denominator) {
  180. const downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator, this.wholeValue);
  181. while (downTestFraction.Denominator > maxAllowedDenominator) {
  182. downTestFraction.Numerator--;
  183. }
  184. if (downTestFraction.Denominator < upTestFraction.Denominator) {
  185. return downTestFraction;
  186. }
  187. }
  188. return upTestFraction;
  189. }
  190. public Equals(obj: Fraction): boolean {
  191. return this.realValue === obj.realValue;
  192. }
  193. public CompareTo(obj: Fraction): number {
  194. const diff: number = this.realValue - obj.realValue;
  195. // Return the sign of diff
  196. return diff ? diff < 0 ? -1 : 1 : 0;
  197. }
  198. public lt(frac: Fraction): boolean {
  199. return this.realValue < frac.realValue;
  200. }
  201. public lte(frac: Fraction): boolean {
  202. return this.realValue <= frac.realValue;
  203. }
  204. //public Equals(f: Fraction): boolean {
  205. // if (ReferenceEquals(this, f))
  206. // return true;
  207. // if (ReferenceEquals(f, undefined))
  208. // return false;
  209. // return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
  210. //}
  211. private setRealValue(): void {
  212. this.realValue = this.wholeValue + this.numerator / this.denominator;
  213. }
  214. private simplify(): void {
  215. // don't simplify in case of a GraceNote (need it in order to set the right symbol)
  216. if (this.numerator === 0) {
  217. this.denominator = 1;
  218. return;
  219. }
  220. // normally should check if denominator or fraction.denominator is 0 but in our case a zero denominator
  221. // doesn't make sense. Could probably be optimized
  222. const i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
  223. this.numerator /= i;
  224. this.denominator /= i;
  225. const whole: number = Math.floor(this.numerator / this.denominator);
  226. if (whole !== 0) {
  227. this.wholeValue += whole;
  228. this.numerator -= whole * this.denominator;
  229. if (this.numerator === 0) {
  230. this.denominator = 1;
  231. }
  232. }
  233. if (this.denominator > Fraction.maximumAllowedNumber) {
  234. const factor: number = <number>this.denominator / Fraction.maximumAllowedNumber;
  235. this.numerator = <number>Math.round(this.numerator / factor);
  236. this.denominator = <number>Math.round(this.denominator / factor);
  237. }
  238. if (this.numerator > Fraction.maximumAllowedNumber) {
  239. const factor: number = <number>this.numerator / Fraction.maximumAllowedNumber;
  240. this.numerator = <number>Math.round(this.numerator / factor);
  241. this.denominator = <number>Math.round(this.denominator / factor);
  242. }
  243. }
  244. //private static equals(f1: Fraction, f2: Fraction): boolean {
  245. // return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
  246. //}
  247. //
  248. //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
  249. // let n: number = 1;
  250. // let d: number = 1;
  251. // let fraction: number = n / d;
  252. // while (Math.abs(fraction - value) > epsilonForPrecision) {
  253. // if (fraction < value) {
  254. // n++;
  255. // }
  256. // else {
  257. // d++;
  258. // n = <number>Math.round(value * d);
  259. // }
  260. // fraction = n / <number>d;
  261. // }
  262. // return new Fraction(n, d);
  263. //}
  264. //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
  265. // if (m1 < m2)
  266. // return m1;
  267. // else return m2;
  268. //}
  269. //public static getFraction(value: number, denominatorPrecision: number): Fraction {
  270. // let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
  271. // return new Fraction(numerator, denominatorPrecision);
  272. //}
  273. //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
  274. // if (f1 < f2)
  275. // return f1;
  276. // else return f2;
  277. //}
  278. //public static GetMaxValue(): Fraction {
  279. // return new Fraction(Fraction.maximumAllowedNumber, 1);
  280. //}
  281. //public static get MaxAllowedNumerator(): number {
  282. // return Fraction.maximumAllowedNumber;
  283. //}
  284. //public static get MaxAllowedDenominator(): number {
  285. // return Fraction.maximumAllowedNumber;
  286. //}
  287. //public ToFloatingString(): string {
  288. // return this.RealValue.ToString();
  289. //}
  290. //public Compare(x: Fraction, y: Fraction): number {
  291. // if (x > y)
  292. // return 1;
  293. // if (x < y)
  294. // return -1;
  295. // return 0;
  296. //}
  297. //#region operators
  298. //
  299. // // operator overloads must always come in pairs
  300. // // operator overload +
  301. // public static Fraction operator + (Fraction f1, Fraction f2)
  302. //{
  303. // Fraction sum = new Fraction(f1);
  304. // sum.Add(f2);
  305. // return sum;
  306. //}
  307. //
  308. //// operator overload -
  309. //public static Fraction operator - (Fraction f1, Fraction f2)
  310. //{
  311. // Fraction diff = new Fraction(f1);
  312. // diff.Sub(f2);
  313. // return diff;
  314. //}
  315. //
  316. //// operator overloads must always come in pairs
  317. //// operator overload >
  318. //public static bool operator > (Fraction f1, Fraction f2)
  319. //{
  320. // //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
  321. // return f1.RealValue > f2.RealValue;
  322. //}
  323. //
  324. //// operator overload <
  325. //public static bool operator < (Fraction f1, Fraction f2)
  326. //{
  327. // //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
  328. // return f1.RealValue < f2.RealValue;
  329. //}
  330. //
  331. //// operator overload ==
  332. //public static bool operator === (Fraction f1, Fraction f2)
  333. //{
  334. // // code enhanced for performance
  335. // // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
  336. // // and comparisons between booleans are quick
  337. // bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
  338. // bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
  339. //
  340. // // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
  341. // if (f1IsNull !== f2IsNull)
  342. // return false;
  343. //
  344. // if (f1IsNull /*&& f2IsNull*/)
  345. // return true;
  346. //
  347. // return equals(f1, f2);
  348. //}
  349. //
  350. //// operator overload !=
  351. //public static bool operator !== (Fraction f1, Fraction f2)
  352. //{
  353. // return (!(f1 === f2));
  354. //}
  355. //
  356. //// operator overload >=
  357. //public static bool operator >= (Fraction f1, Fraction f2)
  358. //{
  359. // return (!(f1 < f2));
  360. //}
  361. //
  362. //// operator overload <=
  363. //public static bool operator <= (Fraction f1,Fraction f2)
  364. //{
  365. // return (!(f1 > f2));
  366. //}
  367. //
  368. //public static Fraction operator / (Fraction f, int i)
  369. //{
  370. // return new Fraction(f._numerator, f._denominator *= i);
  371. //}
  372. //
  373. //public static Fraction operator / (Fraction f1, Fraction f2)
  374. //{
  375. // let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
  376. // return res.Denominator === 0 ? new Fraction(0, 1) : res;
  377. //}
  378. //
  379. //public static Fraction operator * (Fraction f1, Fraction f2)
  380. //{
  381. // return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
  382. //}
  383. //
  384. //public static Fraction operator % (Fraction f1, Fraction f2)
  385. //{
  386. // let a = f1/f2;
  387. // return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
  388. //}
  389. //
  390. //#endregion operators
  391. }