5 // Alexander Chebaturkin (chebaturkin@gmail.com)
7 // Copyright (C) 2012 Alexander Chebaturkin
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
34 using Mono.CodeContracts.Static.AST;
35 using Mono.CodeContracts.Static.DataStructures;
36 using Mono.CodeContracts.Static.Lattices;
37 using Mono.CodeContracts.Static.Providers;
39 namespace Mono.CodeContracts.Static.Analysis.Numerical {
41 /// Represents a closed interval of <see cref="Rational"/> values.
43 public class Interval : IntervalBase<Interval, Rational>, IEquatable<Interval> {
44 static Interval cached_top_value;
45 static Interval cached_bottom_value;
47 readonly bool is_bottom;
50 Interval (Rational lowerBound, Rational upperBound)
51 : base (lowerBound, upperBound)
53 if (lowerBound.IsMinusInfinity && upperBound.IsPlusInfinity ||
54 lowerBound.IsMinusInfinity && upperBound.IsMinusInfinity ||
55 lowerBound.IsPlusInfinity && upperBound.IsPlusInfinity) {
56 LowerBound = Rational.MinusInfinity;
57 UpperBound = Rational.PlusInfinity;
61 is_bottom = LowerBound > UpperBound;
64 public static Interval TopValue
68 if (ReferenceEquals (cached_top_value, null))
69 cached_top_value = new Interval (Rational.MinusInfinity, Rational.PlusInfinity);
70 return cached_top_value;
74 public static Interval BottomValue
78 if (ReferenceEquals (cached_bottom_value, null))
79 cached_bottom_value = new Interval (Rational.PlusInfinity,
80 Rational.MinusInfinity);
81 return cached_bottom_value;
85 public override Interval Top { get { return TopValue; } }
86 public override Interval Bottom { get { return BottomValue; } }
88 public override bool IsTop { get { return is_top; } }
89 public override bool IsBottom { get { return is_bottom; } }
91 public override bool LessEqual (Interval that)
94 if (this.TryTrivialLessEqual (that, out result))
97 //less equal <==> is included in
98 return LowerBound >= that.LowerBound && UpperBound <= that.UpperBound;
101 public bool LessEqual (IEnumerable<Interval> right)
103 return right.Any (LessEqual);
106 public override Interval Join (Interval that, bool widening, out bool weaker)
110 return widening ? Widen (that) : Join (that);
113 public override Interval Join (Interval that)
116 if (this.TryTrivialJoin (that, out result))
119 return For (Rational.Min (LowerBound, that.LowerBound),
120 Rational.Max (UpperBound, that.UpperBound));
123 public override Interval Widen (Interval that)
126 if (this.TryTrivialJoin (that, out result))
129 return For (LowerBound < that.LowerBound
130 ? ThresholdDB.GetPrevious (LowerBound)
132 UpperBound > that.UpperBound
133 ? ThresholdDB.GetNext (UpperBound)
137 public override Interval Meet (Interval that)
140 if (this.TryTrivialMeet (that, out result))
144 Rational.Max (LowerBound, that.LowerBound),
145 Rational.Min (UpperBound, that.UpperBound));
148 public static Interval For (Rational lowerBound, Rational upperBound)
150 return new Interval (lowerBound, upperBound);
153 public static Interval For (Rational lowerBound, long upperBound)
155 return For (lowerBound, Rational.For (upperBound));
158 public static Interval For (long lowerBound, long upperBound)
160 return For (Rational.For (lowerBound), Rational.For (upperBound));
163 public static Interval For (long lower, Rational upperBound)
165 return For (Rational.For (lower), upperBound);
168 public static Interval For (Rational value)
170 return For (value, value);
173 public static Interval For (long value)
175 return For (Rational.For (value));
178 public static Interval operator + (Interval l, Interval r)
180 if (l.IsBottom || r.IsBottom)
183 Rational lower, upper;
184 if (l.IsTop || r.IsTop
185 || !Rational.TryAdd (l.LowerBound, r.LowerBound, out lower)
186 || !Rational.TryAdd (l.UpperBound, r.UpperBound, out upper))
189 return For (lower, upper);
192 public static Interval operator - (Interval l, Interval r)
194 if (l.IsBottom || r.IsBottom)
197 Rational lower, upper;
198 if (l.IsTop || r.IsTop
199 || !Rational.TrySubtract (l.LowerBound, r.UpperBound, out lower)
200 || !Rational.TrySubtract (l.UpperBound, r.LowerBound, out upper))
203 return For (lower, upper);
206 public static Interval operator / (Interval l, Interval r)
208 if (l.IsBottom || r.IsBottom)
212 if (l.IsTop || r.IsTop || r.Includes (0)
213 || !Rational.TryDivide (l.LowerBound, r.UpperBound, out a)
214 || !Rational.TryDivide (l.LowerBound, r.LowerBound, out b)
215 || !Rational.TryDivide (l.UpperBound, r.UpperBound, out c)
216 || !Rational.TryDivide (l.UpperBound, r.LowerBound, out d))
220 var lower = Rational.Min (Rational.Min (a, b), Rational.Min (c, d));
221 var upper = Rational.Max (Rational.Max (a, b), Rational.Max (c, d));
223 return For (lower, upper);
226 public static Interval operator * (Interval l, Interval r)
228 if (l.IsBottom || r.IsBottom)
231 Rational lower, upper;
232 if (l.IsTop || r.IsTop
233 || !Rational.TryMultiply (l.LowerBound, r.LowerBound, out lower)
234 || !Rational.TryMultiply (l.UpperBound, r.UpperBound, out upper))
237 return For (lower, upper);
240 public static Interval operator - (Interval l)
247 if (!Rational.TryUnaryMinus (l.UpperBound, out lower) ||
248 !Rational.TryUnaryMinus (l.LowerBound, out upper))
251 return For (lower, upper);
254 public static bool AreConsecutiveIntegers (Interval prev, Interval next)
256 if (!prev.IsNormal () || !next.IsNormal () ||
257 !prev.UpperBound.IsInteger || !next.LowerBound.IsInteger)
260 return prev.UpperBound + Rational.One == next.LowerBound;
263 public static Interval ApplyConversion (ExpressionOperator conv, Interval intv)
269 case ExpressionOperator.ConvertToInt32:
270 return intv.RefineWithTypeRanges (int.MinValue, int.MaxValue);
276 public bool TryGetSingletonFiniteInt32 (out int value)
280 if (IsFiniteAndInt32 (out lower, out upper) && lower == upper)
281 return true.With (lower, out value);
283 return false.Without (out value);
286 public bool Includes (long x)
288 return this.IsNormal () && LowerBound <= x && x <= UpperBound;
291 public bool Includes (int x)
293 return this.IsNormal () && LowerBound <= x && x <= UpperBound;
296 public bool OverlapsWith (Interval that)
298 return !Meet (that).IsBottom;
301 public bool OnTheLeftOf (Interval that)
303 if (!this.IsNormal () || !that.IsNormal ())
306 return UpperBound <= that.LowerBound;
309 public override Interval ImmutableVersion ()
314 public override Interval Clone ()
319 public override bool Equals (object obj)
321 if (ReferenceEquals (null, obj))
323 if (ReferenceEquals (this, obj))
325 return Equals ((Interval) obj);
328 public bool Equals (Interval that)
330 if (ReferenceEquals (this, that))
332 if (ReferenceEquals (that, null))
335 return LowerBound == that.LowerBound && UpperBound == that.UpperBound;
338 public override int GetHashCode ()
340 return (LowerBound.GetHashCode () * 397) ^ UpperBound.GetHashCode ();
343 public override void Dump (TextWriter tw)
345 tw.WriteLine (ToString ());
348 protected override bool IsFiniteBound (Rational n)
353 bool IsFiniteAndInt32 (out int lower, out int upper)
355 if (IsFinite && LowerBound.IsInt32 && UpperBound.IsInt32) {
357 lower = (int) LowerBound;
358 upper = (int) UpperBound;
361 catch (ArithmeticException) {
365 return false.With (0, out lower).
369 Interval RefineWithTypeRanges (int min, int max)
371 var lower = LowerBound.IsInfinity || !LowerBound.IsInRange (min, max)
372 ? Rational.MinusInfinity
373 : LowerBound.PreviousInt32;
375 var upper = UpperBound.IsInfinity || !UpperBound.IsInRange (min, max)
376 ? Rational.PlusInfinity
377 : UpperBound.NextInt32;
379 return For (lower, upper);
382 internal static class Ranges {
383 static Interval int8Range;
384 static Interval int32Range;
385 static Interval int64Range;
387 public static Interval Int8Range { get { return int8Range ?? (int8Range = For (sbyte.MinValue, sbyte.MaxValue)); } }
388 public static Interval Int32Range { get { return int32Range ?? (int32Range = For (int.MinValue, int.MaxValue)); } }
389 public static Interval Int64Range { get { return int64Range ?? (int64Range = For (int.MinValue, int.MaxValue)); } }
391 public static Interval GetIntervalForType (TypeNode type, IMetaDataProvider provider)
393 if (provider.Equal (provider.System_Int8, type))
395 if (provider.Equal (provider.System_Int32, type))
397 if (provider.Equal (provider.System_Int64, type))