1using System;
  2
  3namespace Studentlitteratur.CSharp
  4{
  5	/// <remarks>
  6	/// Klassen <c>Rational</c> implementerar rationella tal som utgör kvoten av två heltal,
  7	/// där nämnaren är skild från noll. Är alltid normaliserade.
  8	/// </remarks>
  9	public class Rational : ICloneable, IComparable
 10	{
 11		private int numerator, denominator;
 12
 13		/// <summary>
 14		/// Defaultkonstruktor, sätter täljaren till 0 och nämnaren till 1
 15		/// </summary>
 16		public Rational() : this(0, 1) {}
 17
 18		/// <summary>
 19		/// Konstruktor för täljare. Nämnaren sätts till 1.
 20		/// </summary>
 21		/// <param name="numerator">Täljaren</param>
 22		public Rational(int numerator) : this(numerator, 1) {}
 23
 24		/// <summary>
 25		/// Konstruktor med flyttalsparameter. Bråkets precision motsvarar tre decimaler.
 26		/// </summary>
 27		/// <param name="val">Flyttal, max 3 decimaler används</param>
 28		public Rational(double val) : this(checked((int)(val * 1000)), 1000) {}
 29
 30		/// <summary>
 31		/// Konstruktor för täljare och nämnare
 32		/// </summary>
 33		/// <param name="numerator">Täljare</param>
 34		/// <param name="denominator">Nämnare</param>
 35		/// <exception cref="ArgumentException">Undantag för nämnaren 0</exception>
 36		public Rational(int numerator, int denominator)
 37		{
 38			if (denominator == 0)
 39				throw new ArgumentException();
 40			this.numerator = numerator;
 41			this.denominator = denominator;
 42			Normalize();
 43		}
 44
 45
 46		/// <summary>
 47		/// Metod som normaliserar objektet
 48		/// </summary>
 49		private void Normalize()
 50		{
 51			// hitta största gemensamma divisor
 52			int m = Math.Abs(numerator), n = Math.Abs(denominator);
 53			int r = m % n;
 54			while (r != 0)
 55			{
 56				m = n;
 57				n = r;
 58				r = m % n;
 59			}	
 60			// reducera
 61			numerator /= n;
 62			denominator /= n;
 63			// noll ska ha nämnaren 1
 64			if (numerator == 0)
 65				denominator = 1;
 66			// nämnaren ska alltid vara positiv
 67			if (denominator < 0)
 68			{
 69				denominator = -denominator;
 70				numerator = -numerator;
 71			}
 72		}
 73
 74		/// <summary>
 75		/// Metod som inverterar bråktalet
 76		/// </summary>
 77		public void Invert()
 78		{
 79			int temp = numerator;
 80			numerator = denominator;
 81			denominator = temp;
 82		}
 83
 84		/// <summary>
 85		/// Statisk egenskap som returnerar ett objekt med värdet 0/1
 86		/// </summary>
 87		public static Rational Zero
 88		{
 89			get { return new Rational(0, 1); }
 90		}
 91
 92		/// <summary>
 93		/// Egenskap för att läsa täljaren
 94		/// </summary>
 95		public int Numerator
 96		{
 97			get { return numerator; }
 98		}
 99
100		/// <summary>
101		/// Egenskap för att läsa nämnaren
102		/// </summary>
103		public int Denominator
104		{
105			get { return denominator; }
106		}
107		
108		/// <summary>
109		/// Omdefinierad från Object
110		/// </summary>
111		/// <returns>Strng med täljare/nämnare</returns>
112		public override string ToString()
113		{
114			return numerator.ToString() + "/" + denominator.ToString();
115		}
116
117		/// <summary>
118		/// Omdefinierad från Object
119		/// </summary>
120		/// <param name="obj">Objekt att jämföra med</param>
121		/// <returns>True eller false för likhet</returns>
122		public override bool Equals(object obj)
123		{
124			if (obj == null)
125				return false;
126			if (obj.GetType() != this.GetType())
127				return false;
128			Rational r = (Rational)obj;
129			return (r.numerator == this.numerator && r.denominator == this.denominator);
130		}
131
132		/// <summary>
133		/// Omdefinierad från Object
134		/// </summary>
135		/// <returns>Hashvärde baserat på bråkets värde</returns>
136		public override int GetHashCode()
137		{
138			return (numerator << 16) | denominator;
139		}
140
141		/// <summary>
142		/// Implementerar ICloneable.Clone. För objektkopiering.
143		/// </summary>
144		/// <returns>Kopia av objektet</returns>
145		public object Clone()
146		{
147			return MemberwiseClone();
148		}
149
150		/// <summary>
151		/// Implementerar IComparable.CompareTo
152		/// </summary>
153		/// <param name="obj">Objektet att jämfra med</param>
154		/// <returns>-1 för detta objekt mindre, 0 för lika, 1 för detta objekt större</returns>
155		/// <exception cref="ArgumentException">Undantag för fel typ på paramatern</exception>
156		public int CompareTo(object obj)
157		{
158			if (obj.GetType() != this.GetType())
159				throw new ArgumentException();
160			Rational r = (Rational)obj;
161			if (this.numerator * r.denominator < this.denominator * r.numerator)
162				return -1;
163			else if (this.Equals(r))
164				return 0;
165			else
166				return 1;
167		}
168
169		/// <summary>
170		/// Additionsoperator
171		/// </summary>
172		/// <param name="left">Vänster operand</param>
173		/// <param name="right">Höger operand</param>
174		/// <returns>Nytt objekt utgörande summan</returns>
175		public static Rational operator+(Rational left, Rational right)
176		{
177			Rational r = new Rational(
178				left.numerator * right.denominator + left.denominator * right.numerator,
179				left.denominator * right.denominator);
180			r.Normalize();
181			return r;
182		}
183
184		/// <summary>
185		/// Subtraktionsoperator
186		/// </summary>
187		/// <param name="left">Vänster operand</param>
188		/// <param name="right">Höger operand</param>
189		/// <returns>Nytt objekt utgörande skillnaden</returns>
190		public static Rational operator-(Rational left, Rational right)
191		{
192			Rational r = new Rational(
193				left.numerator * right.denominator - left.denominator * right.numerator,
194				left.denominator * right.denominator);
195			r.Normalize();
196			return r;
197		}
198
199		/// <summary>
200		/// Multiplikationsoperator
201		/// </summary>
202		/// <param name="left">Vänster operand</param>
203		/// <param name="right">Höger operand</param>
204		/// <returns>Nytt objekt utgörande produkten</returns>
205		public static Rational operator*(Rational left, Rational right)
206		{
207			Rational r = new Rational(
208				left.numerator * right.numerator,
209				left.denominator * right.denominator);
210			r.Normalize();
211			return r;
212		}
213
214		/// <summary>
215		/// Divisionsoperator
216		/// </summary>
217		/// <param name="left">Vänster operand</param>
218		/// <param name="right">Höger operand</param>
219		/// <returns>Nytt objekt utgörande kvoten</returns>
220		public static Rational operator/(Rational left, Rational right)
221		{
222			Rational r = new Rational(
223				left.numerator * right.denominator,
224				left.denominator * right.numerator);
225			r.Normalize();
226			return r;
227		}
228
229		/// <summary>
230		/// Likamed-operator
231		/// </summary>
232		/// <param name="left">Vänster operand</param>
233		/// <param name="right">Höger operand</param>
234		/// <returns>True eller false för likhet</returns>
235		public static bool operator==(Rational left, Rational right)
236		{
237			return Equals(left, right);
238		}
239
240		/// <summary>
241		/// Icke-likamed-operator
242		/// </summary>
243		/// <param name="left">Vänster operand</param>
244		/// <param name="right">Höger operand</param>
245		/// <returns>True eller false för olikhet</returns>
246		public static bool operator!=(Rational left, Rational right)
247		{
248			return !Equals(left, right);
249		}
250
251		/// <summary>
252		/// Mindre-än-operator
253		/// </summary>
254		/// <param name="left">Vänster operand</param>
255		/// <param name="right">Höger operand</param>
256		/// <returns>True eller false för mindre-än</returns>
257		public static bool operator<(Rational left, Rational right)
258		{
259			return left.CompareTo(right) < 0;
260		}
261
262		/// <summary>
263		/// Strre-än-operator
264		/// </summary>
265		/// <param name="left">Vänster operand</param>
266		/// <param name="right">Höger operand</param>
267		/// <returns>True eller false för strre-än</returns>
268		public static bool operator>(Rational left, Rational right)
269		{
270			return left.CompareTo(right) > 0;
271		}
272
273		/// <summary>
274		/// Mindre-än-eller-likamed-operator
275		/// </summary>
276		/// <param name="left">Vänster operand</param>
277		/// <param name="right">Höger operand</param>
278		/// <returns>True eller false för mindre-än-eller-likamed</returns>
279		public static bool operator<=(Rational left, Rational right)
280		{
281			return left.CompareTo(right) <= 0;
282		}
283
284		/// <summary>
285		/// Strre-än-eller-likamed-operator
286		/// </summary>
287		/// <param name="left">Vänster operand</param>
288		/// <param name="right">Höger operand</param>
289		/// <returns>True eller false för större-än-eller-likamed</returns>
290		public static bool operator>=(Rational left, Rational right)
291		{
292			return left.CompareTo(right) >= 0;
293		}
294
295		/// <summary>
296		/// Unär minusoperator
297		/// </summary>
298		/// <param name="obj">Operanden</param>
299		/// <returns>Nytt objekt utgörande negationen</returns>
300		public static Rational operator-(Rational obj)
301		{
302			return new Rational(-obj.numerator, obj.denominator);
303		}
304
305		/// <summary>
306		/// Typomvandlare till <c>double</c>
307		/// </summary>
308		/// <param name="obj">Objektet som utgör värdet</param>
309		/// <returns>Objektets värde som flyttal</returns>
310		public static implicit operator double(Rational obj)
311		{
312			return (double)obj.numerator/obj.denominator;
313		}
314
315		/// <summary>
316		/// Typomvandlare från <c>int</c>
317		/// </summary>
318		/// <param name="i">Heltalet som utgör värdet</param>
319		/// <returns>Nytt objekt representerande värdet</returns>
320		public static implicit operator Rational(int i)
321		{
322			return new Rational(i);
323		}
324
325		/// <summary>
326		/// Typomvandlare från <c>double</c>
327		/// </summary>
328		/// <param name="d">Flyttalet som utgör värdet</param>
329		/// <returns>Nytt objekt representerande värdet</returns>
330		public static implicit operator Rational(double d)
331		{
332			return new Rational(d);
333		}
334		
335		
336		/// <summary>
337		/// Main för test av klassen
338		/// </summary>
339		static void Main()
340		{
341			Rational r1 = new Rational(); //eller:
342			r1 = Rational.Zero; 
343			Rational r2 = new Rational(5);
344			Rational r3 = new Rational(18, -45);
345			Rational r4 = new Rational(3.75);
346			Rational r5 = r3 + r4;
347			Rational r6 = r3 - r4;
348			Rational r7 = r3 * r4;
349			Rational r8 = r3 / r4;
350			Rational r9 = -r4;
351			Console.WriteLine(r1.ToString());
352			Console.WriteLine(r2.ToString());
353			Console.WriteLine(r3.ToString());
354			Console.WriteLine(r4.ToString());
355			Console.WriteLine(r5.ToString());
356			Console.WriteLine(r6.ToString());
357			Console.WriteLine(r7.ToString());
358			Console.WriteLine(r8.ToString());
359			Console.WriteLine(r9.ToString());	
360		}
361	}
362}
363