// // Interval.cs // // Authors: // Alexander Chebaturkin (chebaturkin@gmail.com) // // Copyright (C) 2012 Alexander Chebaturkin // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.IO; using System.Linq; using Mono.CodeContracts.Static.AST; using Mono.CodeContracts.Static.DataStructures; using Mono.CodeContracts.Static.Lattices; using Mono.CodeContracts.Static.Providers; namespace Mono.CodeContracts.Static.Analysis.Numerical { /// /// Represents a closed interval of values. /// public class Interval : IntervalBase, IEquatable { static Interval cached_top_value; static Interval cached_bottom_value; readonly bool is_bottom; readonly bool is_top; Interval (Rational lowerBound, Rational upperBound) : base (lowerBound, upperBound) { if (lowerBound.IsMinusInfinity && upperBound.IsPlusInfinity || lowerBound.IsMinusInfinity && upperBound.IsMinusInfinity || lowerBound.IsPlusInfinity && upperBound.IsPlusInfinity) { LowerBound = Rational.MinusInfinity; UpperBound = Rational.PlusInfinity; is_top = true; } is_bottom = LowerBound > UpperBound; } public static Interval TopValue { get { if (ReferenceEquals (cached_top_value, null)) cached_top_value = new Interval (Rational.MinusInfinity, Rational.PlusInfinity); return cached_top_value; } } public static Interval BottomValue { get { if (ReferenceEquals (cached_bottom_value, null)) cached_bottom_value = new Interval (Rational.PlusInfinity, Rational.MinusInfinity); return cached_bottom_value; } } public override Interval Top { get { return TopValue; } } public override Interval Bottom { get { return BottomValue; } } public override bool IsTop { get { return is_top; } } public override bool IsBottom { get { return is_bottom; } } public override bool LessEqual (Interval that) { bool result; if (this.TryTrivialLessEqual (that, out result)) return result; //less equal <==> is included in return LowerBound >= that.LowerBound && UpperBound <= that.UpperBound; } public bool LessEqual (IEnumerable right) { return right.Any (LessEqual); } public override Interval Join (Interval that, bool widening, out bool weaker) { weaker = false; return widening ? Widen (that) : Join (that); } public override Interval Join (Interval that) { Interval result; if (this.TryTrivialJoin (that, out result)) return result; return For (Rational.Min (LowerBound, that.LowerBound), Rational.Max (UpperBound, that.UpperBound)); } public override Interval Widen (Interval that) { Interval result; if (this.TryTrivialJoin (that, out result)) return result; return For (LowerBound < that.LowerBound ? ThresholdDB.GetPrevious (LowerBound) : that.LowerBound, UpperBound > that.UpperBound ? ThresholdDB.GetNext (UpperBound) : that.UpperBound); } public override Interval Meet (Interval that) { Interval result; if (this.TryTrivialMeet (that, out result)) return result; return For ( Rational.Max (LowerBound, that.LowerBound), Rational.Min (UpperBound, that.UpperBound)); } public static Interval For (Rational lowerBound, Rational upperBound) { return new Interval (lowerBound, upperBound); } public static Interval For (Rational lowerBound, long upperBound) { return For (lowerBound, Rational.For (upperBound)); } public static Interval For (long lowerBound, long upperBound) { return For (Rational.For (lowerBound), Rational.For (upperBound)); } public static Interval For (long lower, Rational upperBound) { return For (Rational.For (lower), upperBound); } public static Interval For (Rational value) { return For (value, value); } public static Interval For (long value) { return For (Rational.For (value)); } public static Interval operator + (Interval l, Interval r) { if (l.IsBottom || r.IsBottom) return BottomValue; Rational lower, upper; if (l.IsTop || r.IsTop || !Rational.TryAdd (l.LowerBound, r.LowerBound, out lower) || !Rational.TryAdd (l.UpperBound, r.UpperBound, out upper)) return TopValue; return For (lower, upper); } public static Interval operator - (Interval l, Interval r) { if (l.IsBottom || r.IsBottom) return BottomValue; Rational lower, upper; if (l.IsTop || r.IsTop || !Rational.TrySubtract (l.LowerBound, r.UpperBound, out lower) || !Rational.TrySubtract (l.UpperBound, r.LowerBound, out upper)) return TopValue; return For (lower, upper); } public static Interval operator / (Interval l, Interval r) { if (l.IsBottom || r.IsBottom) return BottomValue; Rational a, b, c, d; if (l.IsTop || r.IsTop || r.Includes (0) || !Rational.TryDivide (l.LowerBound, r.UpperBound, out a) || !Rational.TryDivide (l.LowerBound, r.LowerBound, out b) || !Rational.TryDivide (l.UpperBound, r.UpperBound, out c) || !Rational.TryDivide (l.UpperBound, r.LowerBound, out d)) return TopValue; var lower = Rational.Min (Rational.Min (a, b), Rational.Min (c, d)); var upper = Rational.Max (Rational.Max (a, b), Rational.Max (c, d)); return For (lower, upper); } public static Interval operator * (Interval l, Interval r) { if (l.IsBottom || r.IsBottom) return BottomValue; Rational lower, upper; if (l.IsTop || r.IsTop || !Rational.TryMultiply (l.LowerBound, r.LowerBound, out lower) || !Rational.TryMultiply (l.UpperBound, r.UpperBound, out upper)) return TopValue; return For (lower, upper); } public static Interval operator - (Interval l) { if (!l.IsNormal ()) return l; Rational lower; Rational upper; if (!Rational.TryUnaryMinus (l.UpperBound, out lower) || !Rational.TryUnaryMinus (l.LowerBound, out upper)) return TopValue; return For (lower, upper); } public static bool AreConsecutiveIntegers (Interval prev, Interval next) { if (!prev.IsNormal () || !next.IsNormal () || !prev.UpperBound.IsInteger || !next.LowerBound.IsInteger) return false; return prev.UpperBound + Rational.One == next.LowerBound; } public static Interval ApplyConversion (ExpressionOperator conv, Interval intv) { if (intv.is_bottom) return intv; switch (conv) { case ExpressionOperator.ConvertToInt32: return intv.RefineWithTypeRanges (int.MinValue, int.MaxValue); default: return intv; } } public bool TryGetSingletonFiniteInt32 (out int value) { int lower; int upper; if (IsFiniteAndInt32 (out lower, out upper) && lower == upper) return true.With (lower, out value); return false.Without (out value); } public bool Includes (long x) { return this.IsNormal () && LowerBound <= x && x <= UpperBound; } public bool Includes (int x) { return this.IsNormal () && LowerBound <= x && x <= UpperBound; } public bool OverlapsWith (Interval that) { return !Meet (that).IsBottom; } public bool OnTheLeftOf (Interval that) { if (!this.IsNormal () || !that.IsNormal ()) return false; return UpperBound <= that.LowerBound; } public override Interval ImmutableVersion () { return this; } public override Interval Clone () { return this; } public override bool Equals (object obj) { if (ReferenceEquals (null, obj)) return false; if (ReferenceEquals (this, obj)) return true; return Equals ((Interval) obj); } public bool Equals (Interval that) { if (ReferenceEquals (this, that)) return true; if (ReferenceEquals (that, null)) return false; return LowerBound == that.LowerBound && UpperBound == that.UpperBound; } public override int GetHashCode () { return (LowerBound.GetHashCode () * 397) ^ UpperBound.GetHashCode (); } public override void Dump (TextWriter tw) { tw.WriteLine (ToString ()); } protected override bool IsFiniteBound (Rational n) { return n.IsInteger; } bool IsFiniteAndInt32 (out int lower, out int upper) { if (IsFinite && LowerBound.IsInt32 && UpperBound.IsInt32) { try { lower = (int) LowerBound; upper = (int) UpperBound; return true; } catch (ArithmeticException) { } } return false.With (0, out lower). With (0, out upper); } Interval RefineWithTypeRanges (int min, int max) { var lower = LowerBound.IsInfinity || !LowerBound.IsInRange (min, max) ? Rational.MinusInfinity : LowerBound.PreviousInt32; var upper = UpperBound.IsInfinity || !UpperBound.IsInRange (min, max) ? Rational.PlusInfinity : UpperBound.NextInt32; return For (lower, upper); } internal static class Ranges { static Interval int8Range; static Interval int32Range; static Interval int64Range; public static Interval Int8Range { get { return int8Range ?? (int8Range = For (sbyte.MinValue, sbyte.MaxValue)); } } public static Interval Int32Range { get { return int32Range ?? (int32Range = For (int.MinValue, int.MaxValue)); } } public static Interval Int64Range { get { return int64Range ?? (int64Range = For (int.MinValue, int.MaxValue)); } } public static Interval GetIntervalForType (TypeNode type, IMetaDataProvider provider) { if (provider.Equal (provider.System_Int8, type)) return Int8Range; if (provider.Equal (provider.System_Int32, type)) return Int32Range; if (provider.Equal (provider.System_Int64, type)) return Int64Range; return TopValue; } } } }