unit-testing/interval-exercise/IntervalWithError.cs - A slightly revised version of the Interval struct - with an error. | Lecture 14 - slide 29 : 35 Program 1 |
/* There is a deliberately placed error in this program */ using System; using System.Collections; /* An interval with orientation */ public class IntervalAccessException: ApplicationException{ } public struct Interval{ private readonly int from, to; private bool empty; public Interval(int from, int to){ this.empty = false; this.from = from; this.to = to; } /* Constructs an empty interval. This cannot be done elegantly by a constructor, because parameterless constructors cannot be used for structs */ public static Interval MakeEmptyInterval (){ Interval res = new Interval(); res.empty = true; return res; } /* Assumed as a precondition that the interval is non-empty */ public int From{ get {if (this.Empty) throw new IntervalAccessException(); else return from;} } /* Assumed as a precondition that the interval is non-empty */ public int To{ get {if (this.Empty) throw new IntervalAccessException(); else return to;} } public bool Empty{ get {return empty;} } /* Return the orientation of the interval. Returns -1 in case from is larger than to, 1 if from is small than to, and 0 is the interval is empty or singular. */ private int Orientation { get{ int res; if (empty) res = 0; else if (from < to) res = 1; else if (to < from) res = -1; else res = 0; return res; } } public int Length{ get {return empty? 0: Math.Abs(to - from) + 1;} } /* Return element i of the interval, relative to its From edge. Like for arrays, the indexing is zero-bound */ public int this[int i]{ get {if (empty) throw new IndexOutOfRangeException("Error"); else if (from <= to){ if (i >= 0 && i <= Math.Abs(from-to)) return from + i; else throw new IndexOutOfRangeException("Error"); } else if (from > to){ if (i >= 0 && i <= Math.Abs(from-to)) return from - i; else throw new IndexOutOfRangeException("Error"); } else throw new Exception("Should not happen"); } } /* The current interval determines the orientation of the resulting interval */ public Interval OverlapWith (Interval other){ // Enforce positively oriented intervals: Interval thisPI = (this.Orientation < 0) ? !this : this, otherPI = (other.Orientation < 0) ? !other : other, res; /* In the if-else chain we work with positively oriented intervals In such intervals From <= To: */ if (thisPI.Empty || otherPI.Empty) // both empty res = MakeEmptyInterval(); else if (thisPI.From > otherPI.To || thisPI.To < otherPI.From) // disjoint res = MakeEmptyInterval(); else if (thisPI.From < otherPI.From && otherPI.To < thisPI.To) // other inside this res = thisPI; else if (otherPI.From <= thisPI.From && thisPI.To <= otherPI.To) // this inside other res = otherPI; else if (thisPI.From <= otherPI.From && otherPI.From <= thisPI.To) // this overlaps left border of other res = new Interval(otherPI.From, thisPI.To); else if (otherPI.From <= thisPI.From && thisPI.From <= otherPI.To) // other overlaps left border of this res = new Interval(thisPI.From, otherPI.To); else throw new Exception("Should not happen"); // Reintroduce orientation: return (this.Orientation < 0) ? !res : res; } public static Interval operator +(Interval i, int j){ return i.empty ? MakeEmptyInterval() : new Interval(i.From + j, i.To + j); } public static Interval operator +(int j, Interval i){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From + j, i.To + j); } public static Interval operator >>(Interval i, int j){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From, i.To + j); } public static Interval operator <<(Interval i, int j){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From + j, i.To); } public static Interval operator *(Interval i, int j){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From * j, i.To * j); } public static Interval operator *(int j, Interval i){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From * j, i.To * j); } public static Interval operator -(Interval i, int j){ return i.Empty ? MakeEmptyInterval() : new Interval(i.From - j, i.To - j); } public static Interval operator !(Interval i){ return i.Empty ? MakeEmptyInterval() : new Interval(i.To, i.From); } public static explicit operator int[] (Interval i){ int[] res = new int[i.Length]; for (int j = 0; j < i.Length; j++) res[j] = i[j]; return res; } private class IntervalEnumerator: IEnumerator{ private readonly Interval interval; private int idx; public IntervalEnumerator (Interval i){ this.interval = i; idx = -1; // position enumerator outside range } public Object Current{ get {return (interval.From < interval.To) ? interval.From + idx : interval.From - idx;} } public bool MoveNext (){ if (interval.Empty) return false; else if ( idx < Math.Abs(interval.To - interval.From)) {idx++; return true;} else {return false;} } public void Reset(){ idx = -1; } } public IEnumerator GetEnumerator (){ return new IntervalEnumerator(this); } }