/* CSharp Assignment 1, Part 2: All about numbers You will find that the contents of this file has been pre-compiled into csasn2.dll. You may not change this file and recompile. Your program must work with the given .dll. A number can be an integer, rational (fraction), real, or complex. These are all subclasses of the abstract class `number`. Your task is to write a series of functions concerning numbers. Fortunately, you won't have to add another type of number. In all functions, you must observe the requirement that there is to be no loss of precision as much as possible. That is, do not convert a rational 1/3 into a double, which would lose precision, unless you're adding it to a real or complex number. DO NOT EDIT THIS FILE. Instead, start a new program and compile it with mcs yourprogram.cs /r:csasn2.dll /r:option.dll The .dll files are found in the assignment zip folder. They can also be recreated from the source file with mcs /t:library csasn2.cs Run the program with (mono) ./yourprogram.exe The functions you'll be writing will all be static, so they can appear in any class, including the one that contains Main(). 1. Write a function public static bool equals(number a, number b) that returns true if two "numbers" are equal. For example: a rational(1,5) is "equal" to a real(0.2). However, when making calculations avoid loss of precision as much as possible: do not convert the rational to a double. Instead, just cross-multiply: 1/5 = 0.2 because 1.0 == 0.2*5. Your function must work for all cases of number. You may try to use some of the functions already defined in the abstract class, or not ... /////// Basic math facts: rules of rational arithmetic: a/b + c/d = (a*d+c*b)/(b*d) a/b * c/d = (a*c)/(b*d) a/b==c/d if a*d==b*c an integer n can be cast to a rational as n/1 rules of complex arithmetic: (a+bi) + (c+di) = (a+c) + (b+d)i (a+bi) * (c+di) = (ac-bd) + (bc+ad)i a+bi == c+di if a==c and b==d. Complex inequalities don't exit. (So -1+0i * c+di = -c + -1*i = -c-i) A real number r can be cast to a complex r+0i. Note that a complex number can be "equal" to another type of number only if its imaginary part is zero. 2. Write functions to add and multiply any two numbers, losing precision only when necessary (a rational plus a rational should be a rational, not a real): 2a. public static number add(number a, number b) 2b. public static number multiply(number a, number b) 3. Write a function to add up an array of numbers, returning the sum The sum of the empty array is the integer 0. public static number sum(number[] A) //(A has length A.Length) Part 2B. (I may ask 1-3 to be completed first) 4. Study the Option class (https://cs.hofstra.edu/~cscccl/csc123/option.cs). This class offers an alternative to error-handling using exceptions. Exceptions are dynamically *scoped* and hard to encapsulate. Write a function, using match, and other functions provided on the numbers abstract class, to return the multiplicative inverse of a number: public static Option inverse(number n); The multiplicative inverse of an integer n is the rational 1/n if n is not zero, otherwise, the inverse doesn't exist. The inverse of a rational n/d is the rational d/n if n is not zero, otherwise, it doesn't exist, The inverse of a real r is the real 1.0/r if r is non-zero, else it doesn't exist. The inverse of a complex number a + bi exists only if not both a and b are zero (at least one is non-zero). In that case the inverse is the complex number (a/s)-(b/s)i where s = a*a + b*b. Your function should either return a new Some(x) or a Option.Nothing (or new None()). You should also return a None if the argument n is null. You may optionally define inverse as an "extension method" in a static class: public static Option inverse(this number n). 5. Using the multiplicative inverse, define division between numbers. ( a/b = a*inverse(b), but the inverse may not exist! ) public static Option divide(number a, number b) Be sure to test your divide function (in Main): number pi = new real(3.1415927); number i = new integer(2); number f = new rational(0,1); divide(pi,i) // Some(pi/2) .map(x => multiply(x,new rational(2,3))) // Some(pi/3) .and_then(x => divide(x,f)) // None .match(some: v => {Console.WriteLine("the result is "+v);}, none: () => {Console.WriteLine("no result");}); // note that .map is called on functions number -> number // whereas .and_then is called on functions number -> Option Formulate other tests (including ones that do return a result). Your function may never throw/catch exceptions. You will lose ONE MILLION POINTS if an exception is thrown. And if you will lose ONE BILLION POINTS if it ever throws a null pointer exception. */ using System; public abstract class number { public abstract number negate(); //implemented in subclasses public abstract number upcast(); public abstract number downcast(); public abstract T match( Func I, Func F, Func R, Func C); public override string ToString() { // overrides object.ToString return this.match( I: i => ""+i.val, F: f => f.n+"/"+f.d, R: r => ""+r.val, C: c => c.r + "+" + c.i + "i"); } // setting up "multiple dispatch", assumes functions are commutative public T match2_commute(number N, Func II, Func IF, Func IR, Func IC, Func FF, Func FR, Func FC, Func RR, Func RC, Func CC) { return match( I: i => N.match(I: k => II(i,k), F: f => IF(i,f), R: r => IR(i,r), C: c => IC(i,c)), F: f => N.match(I: i => IF(i,f), // assume commutative F: g => FF(f,g), R: r => FR(f,r), C: c => FC(f,c)), R: r => N.match(I: i => IR(i,r), F: f => FR(f,r), R: s => RR(r,s), C: c => RC(r,c)), C: c => N.match(I: i => IC(i,c), F: f => FC(f,c), R: r => RC(r,c), C: d => CC(c,d))); }//match2 // non-commutative match2, with assist from upcast public T match2_upcast(number N, Func FF, Func FC, Func CF, Func CC) => match( I: i => N.match(I: k => FF((rational)i.upcast(),(rational)k.upcast()), F: f => FF((rational)i.upcast(),f), R: r => FC((rational)i.upcast(),(complex)r.upcast()), C: c => FC((rational)i.upcast(),c)), F: f => N.match(I: i => FF(f,(rational)i.upcast()), F: g => FF(f,g), R: r => FC(f,(complex)r.upcast()), C: c => FC(f,c)), R: r => N.match(I: i => CF((complex)r.upcast(),(rational)i.upcast()), F: f => CF((complex)r.upcast(),f), R: s => CC((complex)r.upcast(),(complex)s.upcast()), C: c => CC((complex)r.upcast(),c)), C: c => N.match(I: i => CF(c,(rational)i.upcast()), F: f => CF(c,f), R: r => CC(c,(complex)r.upcast()), C: d => CC(c,d))); }//abstract class number public class integer : number { public int val; public integer(int x) {this.val=x;} public override number negate() { return new integer(-1*val); } public override number upcast() { return new rational(val,1); } public override number downcast() { return this; } public override T match( Func I, Func F, Func R, Func C) => I(this); }//integer public class rational : number { public int n; //numerator public int d=1; //denominator public rational(int n, int d) { this.n=n; if (d!=0) this.d = d; else Console.Error.WriteLine("warnign: zero denominator avoided"); } public override number negate() { return new rational(-1*n,d); } public override number upcast() { return this; } //can't lose precision public override number downcast() { if (n%d==0) return new integer(n/d); else return this; } public override T match( Func I, Func F, Func R, Func C) => F(this); }//rational public class real : number { public double val; public real(double r) {val=r;} public override number negate() { return new real(-1*val); } public override number upcast() { return new complex(val,0.0);} public override number downcast() { return this; } //can't lose precision public override T match( Func I, Func F, Func R, Func C) => R(this); }//real public class complex : number { public double r; // real part public double i; // imaginary part public complex(double a, double b) {r=a; i=b;} public override number negate() { return new complex(-1*r,-1*i); } public override number upcast() { return this; } public override number downcast() { if (i==0) return new real(r); else return this; } public override T match( Func I, Func F, Func R, Func C) => C(this); }//complex public static class about_numbers { public static number Randnum() { // generates a random number Random rnd = new Random(); int r = rnd.Next(0,4); switch (r) { case 0: return new integer(rnd.Next(-10,10)); case 1: return new rational(rnd.Next(-10,10),rnd.Next(1,10)); case 2: return new real(rnd.Next(0,200)/100.0); default: return new complex(rnd.Next(0,200)/100.0, rnd.Next(0,5)*1.0); }//switch } public static void test() { number[] NUMS = new number[16]; for(int i=0;i<16;i++) NUMS[i] = Randnum(); foreach(var n in NUMS) Console.WriteLine(n); }//main } // static class /* public class csasn2 { public static void Main() { about_numbers.test(); } }// uncomment to test */