fork download
  1. using System;
  2.  
  3. public class Test
  4. {
  5. public static void Main()
  6. {
  7. // 2,000,000,000/2,000,000,001
  8. Rational r = new Rational(2000000000, 2000000001);
  9. Console.WriteLine(r + r);
  10. }
  11. }
  12.  
  13. //namespace NotSystemAndOthersThingsThatIHaveNoPracticalUseFor
  14. //{
  15. // This is Version 2.
  16. public struct Rational : IComparable, IComparable<Rational>, IEquatable<Rational>
  17. {
  18. // Retained from Version 1.
  19. public int Numerator { get; private set; }
  20. public int Denominator { get; private set; }
  21.  
  22. // These fields bypass Simplify().
  23. public static readonly Rational MinValue = new Rational { Numerator = int.MinValue, Denominator = 1 };
  24. public static readonly Rational MaxValue = new Rational { Numerator = int.MaxValue, Denominator = 1 };
  25. public static readonly Rational Epsilon = new Rational { Numerator = 1, Denominator = int.MaxValue };
  26. public static readonly Rational Undefined = new Rational { Numerator = 0, Denominator = 0 };
  27. public static readonly Rational Zero = new Rational { Numerator = 0, Denominator = 1 };
  28. public static readonly Rational One = new Rational { Numerator = 1, Denominator = 1 };
  29. public static readonly Rational MinusOne = new Rational { Numerator = -1, Denominator = 1 };
  30.  
  31. public Rational(int numerator, int denominator = 1) : this()
  32. {
  33. Numerator = numerator;
  34. Denominator = denominator;
  35. // There is a special case where Simplify() could throw an exception:
  36. //
  37. // new Rational(int.MinValue, certainNegativeIntegers)
  38. //
  39. // In general, having the contructor throw an exception is bad practice.
  40. // However given the extremity of this special case and the fact that Rational
  41. // is an immutable struct where its inputs are ONLY validated DURING
  42. // construction, I allow the exception to be thrown here.
  43. Simplify();
  44. }
  45.  
  46. public static bool TryCreate(int numerator, int denominator, out Rational result)
  47. {
  48. try
  49. {
  50. result = new Rational(numerator, denominator);
  51. return true;
  52. }
  53. catch
  54. {
  55. result = Undefined;
  56. }
  57. return false;
  58. }
  59.  
  60. public static bool TryParse(string s, out Rational result)
  61. {
  62. try
  63. {
  64. result = Rational.Parse(s);
  65. return true;
  66. }
  67. catch
  68. {
  69. result = Undefined;
  70. }
  71. return false;
  72. }
  73.  
  74. public static Rational Parse(string s)
  75. {
  76. // Note that "3 / -4" would return new Rational(-3, 4).
  77. var tokens = s.Split(new char[] { '/' });
  78.  
  79. var numerator = 0;
  80. var denominator = 0;
  81.  
  82. switch (tokens.Length)
  83. {
  84. case 1:
  85. numerator = GetInteger("Numerator", tokens[0]);
  86. denominator = 1;
  87. break;
  88. case 2:
  89. numerator = GetInteger("Numerator", tokens[0]);
  90. denominator = GetInteger("Denominator", tokens[1]);
  91. break;
  92. default:
  93. throw new ArgumentException(string.Format("Invalid input string: '{0}'", s));
  94. }
  95. return new Rational(numerator, denominator);
  96. }
  97.  
  98. // This is only called by Parse.
  99. private static int GetInteger(string desc, string s)
  100. {
  101. if (string.IsNullOrWhiteSpace(s))
  102. {
  103. throw new ArgumentNullException(desc);
  104. }
  105. var result = 0;
  106. // TODO: Decide whether it's good idea to convert " - 4" to "-4".
  107. s = s.Replace(" ", string.Empty);
  108. if (!int.TryParse(s, out result))
  109. {
  110. throw new ArgumentException(string.Format("Invalid value for {0}: '{1}'", desc, s));
  111. }
  112. return result;
  113. }
  114.  
  115. // Ver 2 Change: uses if's instead of switch(Denominator). Should be easier for Sam The Maintainer.
  116. //TODO: consider other overloads of ToString(). Perhaps one to always display a division symbol.
  117. // For example, new Rational(0, 0).ToString() --> "0/0" instead of "Undefined", or
  118. // new Rational(5).ToString() --> "5/1" instead of "5"
  119. public override string ToString()
  120. {
  121. if (IsUndefined) { return "Undefined"; }
  122. if (IsInteger) { return Numerator.ToString(); }
  123. return string.Format("{0}/{1}", Numerator, Denominator);
  124. }
  125.  
  126. public int CompareTo(object other)
  127. {
  128. if (other == null) { return 1; }
  129. if (other is Rational) { return CompareTo((Rational)other); }
  130. throw new ArgumentException("Argument must be Rational");
  131. }
  132.  
  133. public int CompareTo(Rational other)
  134. {
  135. if (IsUndefined)
  136. {
  137. // While IEEE decrees that floating point NaN's are not equal to each other,
  138. // I am not under any decree to adhere to that same specification for Rational.
  139. return other.IsUndefined ? 0 : -1;
  140. }
  141. if (other.IsUndefined) { return 1; }
  142. return ToDouble().CompareTo(other.ToDouble());
  143. }
  144.  
  145. public bool Equals(Rational other)
  146. {
  147. if (IsUndefined) { return other.IsUndefined; }
  148. return (Numerator == other.Numerator) && (Denominator == other.Denominator);
  149. }
  150.  
  151. public override bool Equals(object other)
  152. {
  153. if (other == null) { return false; }
  154. if (other is Rational) { return Equals((Rational)other); }
  155. throw new ArgumentException("Argument must be Rational");
  156. }
  157.  
  158. // Mofified code that was stolen from:
  159. // http://w...content-available-to-author-only...k.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Double@cs/1305376/Double@cs
  160. // The hashcode for a double is the absolute value of the integer representation of that double.
  161. // [System.Security.SecuritySafeCritical] // auto-generated
  162. // public unsafe override int GetHashCode()
  163. // {
  164. // if (Numerator == 0)
  165. // {
  166. // // Ensure that 0 and -0 have the same hash code
  167. // return 0;
  168. // }
  169. // double d = ToDouble();
  170. // long value = *(long*)(&d);
  171. // return unchecked((int)value) ^ ((int)(value >> 32));
  172. // }
  173.  
  174. public static bool operator ==(Rational rat1, Rational rat2)
  175. {
  176. return rat1.Equals(rat2);
  177. }
  178.  
  179. public static bool operator !=(Rational rat1, Rational rat2)
  180. {
  181. return !rat1.Equals(rat2);
  182. }
  183.  
  184. // Version 2 Change for operators { +, -, *, / } :
  185. // Removed goofy call to Simplify() and rely upon constructor.
  186. // I use local variable n and d for better readability for Sam the Maintainer,
  187. // who's failing eyesight may miss a comma here and there.
  188.  
  189. public static Rational operator +(Rational rat1, Rational rat2)
  190. {
  191. if (rat1.IsUndefined || rat2.IsUndefined)
  192. {
  193. return Undefined;
  194. }
  195. var n = (rat1.Numerator * rat2.Denominator) + (rat1.Denominator * rat2.Numerator);
  196. var d = rat1.Denominator * rat2.Denominator;
  197. return new Rational(n, d);
  198. }
  199.  
  200. public static Rational operator -(Rational rat1, Rational rat2)
  201. {
  202. if (rat1.IsUndefined || rat2.IsUndefined)
  203. {
  204. return Undefined;
  205. }
  206. var n = (rat1.Numerator * rat2.Denominator) - (rat1.Denominator * rat2.Numerator);
  207. var d = rat1.Denominator * rat2.Denominator;
  208. return new Rational(n, d);
  209. }
  210.  
  211. public static Rational operator *(Rational rat1, Rational rat2)
  212. {
  213. if (rat1.IsUndefined || rat2.IsUndefined)
  214. {
  215. return Undefined;
  216. }
  217. var n = rat1.Numerator * rat2.Numerator;
  218. var d = rat1.Denominator * rat2.Denominator;
  219. return new Rational(n, d);
  220. }
  221.  
  222. public static Rational operator /(Rational rat1, Rational rat2)
  223. {
  224. if (rat1.IsUndefined || rat2.IsUndefined)
  225. {
  226. return Undefined;
  227. }
  228. // fixed math error from Version 1
  229. var n = rat1.Numerator * rat2.Denominator;
  230. var d = rat1.Denominator * rat2.Numerator;
  231. return new Rational(n, d);
  232. }
  233.  
  234. // Ver 2 Change: now void - no longer returns Rational.
  235. // The simplified Denominator will always be >= 0 for any Rational.
  236. // For a Rational to be negative, the simplified Numerator will be negative.
  237. // Thus a Rational(3, -4) would simplify to Rational(-3, 4).
  238. private void Simplify()
  239. {
  240. // These corner cases are very quick checks that means slightly longer code.
  241. // Yet I feel their explicit handling makes their logic more clear to future maintenance.
  242. // More importantly, it bypasses modulus and division when its not absolutely needed.
  243. if (IsUndefined)
  244. {
  245. Numerator = 0;
  246. return;
  247. }
  248. if (Numerator == 0)
  249. {
  250. Denominator = 1;
  251. return;
  252. }
  253. if (IsInteger)
  254. {
  255. return;
  256. }
  257. if (Numerator == Denominator)
  258. {
  259. Numerator = 1;
  260. Denominator = 1;
  261. return;
  262. }
  263. if (Denominator < 0)
  264. {
  265. // One special corner case when unsimplified Denominator is < 0 and Numerator equals int.MinValue.
  266. if (Numerator == int.MinValue)
  267. {
  268. ReduceOrThrow();
  269. return;
  270. }
  271. // Simpler and faster than mutiplying by -1
  272. Numerator = -Numerator;
  273. Denominator = -Denominator;
  274. }
  275. // We only perform modulus and division if we absolutely must.
  276. Reduce();
  277. }
  278.  
  279. private void Reduce()
  280. {
  281. // Reduce() is never called if Numerator or Denominator equals 0.
  282. var greatestCommonDivisor = GreatestCommonDivisor(Numerator, Denominator);
  283. Numerator /= greatestCommonDivisor;
  284. Denominator /= greatestCommonDivisor;
  285. }
  286.  
  287. // Ver 2 Change: now void - no longer returns Rational.
  288. // Very special one off case: only called when unsimplified Numerater equals int.MinValue and Denominator is negative.
  289. // Some combinations produce a valid Rational, such as Rational(int.MinValue, int.MinValue), equivalent to Rational(1).
  290. // Others are not valid, such as Rational(int.MinValue, -1) because the Numerator would need to be (int.MaxValue + 1).
  291. private void ReduceOrThrow()
  292. {
  293. try
  294. {
  295. Reduce();
  296. }
  297. catch
  298. {
  299. throw new ArgumentException(string.Format("Invalid Rational(int.MinValue, {0})", Denominator));
  300. }
  301. }
  302.  
  303. public bool IsUndefined { get { return (Denominator == 0); } }
  304.  
  305. public bool IsInteger { get { return (Denominator == 1); } }
  306.  
  307. public double ToDouble()
  308. {
  309. if (IsUndefined) { return double.NaN; }
  310. return (double)Numerator / (double)Denominator;
  311. }
  312.  
  313. // http://e...content-available-to-author-only...a.org/wiki/Euclidean_algorithm
  314. private static int GreatestCommonDivisor(int a, int b)
  315. {
  316. return (b == 0) ? a : GreatestCommonDivisor(b, a % b);
  317. }
  318.  
  319. } //end struct
  320.  
  321. //} //end namespace
Success #stdin #stdout 0.04s 24200KB
stdin
Standard input is empty
stdout
139397120/-389294899