Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

594 lines
25 KiB
C#

//
// DisInterval.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 System.Text;
using Mono.CodeContracts.Static.DataStructures;
using Mono.CodeContracts.Static.Lattices;
namespace Mono.CodeContracts.Static.Analysis.Numerical {
public class DisInterval : IntervalBase<DisInterval, Rational> {
public static readonly DisInterval NotZero =
For (Sequence<Interval>.From (Interval.For (Rational.MinusInfinity, -1L),
Interval.For (1L, Rational.PlusInfinity)));
static DisInterval cached_bottom;
static DisInterval cached_top;
readonly Sequence<Interval> intervals;
readonly Interval join_interval;
readonly State state;
DisInterval (Interval interval)
: base (interval.LowerBound, interval.UpperBound)
{
var list = Sequence<Interval>.Empty;
if (interval.IsTop)
state = State.Top;
else if (interval.IsBottom)
state = State.Bottom;
else {
state = State.Normal;
list = list.Cons (interval);
}
intervals = list;
join_interval = interval;
}
DisInterval (Sequence<Interval> intervals)
: base (Rational.MinusInfinity, Rational.PlusInfinity)
{
bool isBottom;
this.intervals = Normalize (intervals, out isBottom);
if (isBottom) {
join_interval = Interval.BottomValue;
state = State.Bottom;
return;
}
join_interval = JoinAll (intervals);
if (join_interval.IsBottom)
state = State.Bottom;
else if (join_interval.IsTop)
state = intervals.Length () <= 1 ? State.Top : State.Normal;
else {
LowerBound = join_interval.LowerBound;
UpperBound = join_interval.UpperBound;
state = State.Normal;
}
}
DisInterval (State state)
: base (Rational.MinusInfinity, Rational.PlusInfinity)
{
this.state = state;
join_interval = state == State.Bottom ? Interval.BottomValue : Interval.TopValue;
intervals = Sequence<Interval>.Empty;
}
public static DisInterval BottomValue { get { return cached_bottom ?? (cached_bottom = new DisInterval (State.Bottom)); } }
public static DisInterval TopValue { get { return cached_top ?? (cached_top = new DisInterval (State.Top)); } }
public Interval AsInterval { get { return join_interval; } }
public override DisInterval Top { get { return TopValue; } }
public override DisInterval Bottom { get { return BottomValue; } }
public override bool IsTop { get { return state == State.Top; } }
public override bool IsBottom { get { return state == State.Bottom; } }
public bool IsNotZero
{
get
{
if (!this.IsNormal ())
return false;
return intervals.All (intv => !intv.Includes (0));
}
}
public bool IsPositiveOrZero { get { return this.IsNormal () && LowerBound >= 0L; } }
public override bool Equals (object other)
{
if (ReferenceEquals (this, other))
return true;
var that = other as DisInterval;
if (that == null) {
var intv = other as Interval;
if (intv == null)
return false;
return Equals (For (intv));
}
if (state == that.state && join_interval.Equals (that.join_interval))
return HaveSameIntervals (intervals, that.intervals);
return false;
}
static bool HaveSameIntervals (Sequence<Interval> left, Sequence<Interval> right)
{
if (left.Length () != right.Length ())
return false;
var curLeft = left;
var curRight = right;
while (!curLeft.IsEmpty ()) {
if (!curLeft.Head.Equals (curRight.Head))
return false;
curLeft = curLeft.Tail;
curRight = curRight.Tail;
}
return true;
}
public override int GetHashCode ()
{
unchecked {
return (state.GetHashCode () * 397) ^
(join_interval != null ? join_interval.GetHashCode () : 0);
}
}
public static Sequence<Interval> Normalize (Sequence<Interval> intervals, out bool isBottom)
{
if (intervals.Length () == 0) {
isBottom = false;
return intervals;
}
Comparison<Interval> comparison =
(a, b) => a.Equals (b) ? 0 : a.UpperBound <= b.UpperBound ? -1 : 1;
var intervalList = new List<Interval> (intervals.AsEnumerable ());
intervalList.Sort (comparison);
var list = Sequence<Interval>.Empty;
var bottomCnt = 0;
Interval last = null;
foreach (var t in intervalList) {
var cur = t;
if (cur.IsBottom)
bottomCnt++;
else if (cur.IsTop) {
isBottom = false;
return Sequence<Interval>.Empty;
}
else if (!cur.Equals (last)) {
if (last != null) {
while (list != null) {
last = list.Head;
if (Interval.AreConsecutiveIntegers (last, cur)) {
list = list.Tail;
cur = last.Join (cur);
}
else if (last.LessEqual (cur))
list = list.Tail;
else if (last.OverlapsWith (cur)) {
list = list.Tail;
cur = cur.Join (last);
}
else
break;
}
}
last = cur;
list = list.Cons (cur);
}
}
isBottom = bottomCnt == intervals.Length ();
return list.Reverse ();
}
public static Interval JoinAll (Sequence<Interval> list)
{
if (list == null)
return Interval.TopValue;
var res = list.Head;
var cur = list.Tail;
while (cur != null) {
res = res.Join (cur.Head);
cur = cur.Tail;
}
return res;
}
protected override bool IsFiniteBound (Rational n)
{
return !n.IsInfinity;
}
public static DisInterval operator + (DisInterval left, DisInterval right)
{
return OperatorLifting (left, right, (a, b) => a + b, true);
}
public static DisInterval operator * (DisInterval left, DisInterval right)
{
return OperatorLifting (left, right, (a, b) => a * b, true);
}
public static DisInterval operator / (DisInterval left, DisInterval right)
{
return OperatorLifting (left, right, (a, b) => a / b, true);
}
public static DisInterval operator - (DisInterval left, DisInterval right)
{
return OperatorLifting (left, right, (a, b) => a - b, true);
}
public static DisInterval operator - (DisInterval left)
{
if (left.IsBottom || left.IsTop)
return left;
return For (left.intervals.Select (i => -i).Reverse ());
}
static DisInterval OperatorLifting (DisInterval left, DisInterval right,
Func<Interval, Interval, Interval> binop, bool propagateTop)
{
if (left.IsBottom || right.IsBottom)
return BottomValue;
if ((propagateTop && (left.IsTop || right.IsTop)) || (left.IsTop && right.IsTop))
return TopValue;
var intervals = Sequence<Interval>.Empty;
var hasNoNormals = true;
if (propagateTop || (left.IsNormal () && right.IsNormal ()))
foreach (var leftIntv in left.intervals.AsEnumerable ())
foreach (var rightIntv in right.intervals.AsEnumerable ()) {
var res = binop (leftIntv, rightIntv);
if (res.IsTop)
return TopValue;
if (res.IsBottom)
continue;
hasNoNormals = false;
intervals = intervals.Cons (res);
}
else {
var notTop = left.IsTop ? right : left;
var rightIsTop = !left.IsTop;
foreach (var intv in notTop.intervals.AsEnumerable ()) {
var res = rightIsTop
? binop (intv, Interval.TopValue)
: binop (Interval.TopValue, intv);
if (res.IsTop)
return TopValue;
if (res.IsBottom)
continue;
hasNoNormals = false;
intervals = intervals.Cons (res);
}
}
return hasNoNormals ? BottomValue : For (intervals.Reverse ());
}
public override DisInterval Widen (DisInterval that)
{
if (IsTop || that.IsTop)
return TopValue;
return new DisInterval (Widen (intervals, that.intervals));
}
static Sequence<Interval> Widen (Sequence<Interval> left, Sequence<Interval> right)
{
if (left.IsEmpty () || right.IsEmpty ())
return right;
if (left.Length () == 1 && right.Length () == 1)
return Sequence<Interval>.Singleton (left.Head.Widen (right.Head));
if (left.Length () == 1) {
if (left.Head.LessEqual (right.Head))
return right;
return Sequence<Interval>.Singleton (left.Head.Widen (right.Head.Join (right.Last ())));
}
if (right.Length () == 1)
return Sequence<Interval>.Singleton (left.Head.Join (left.Last ()).Widen (right.Head));
var l = left.Head.Widen (right.Head);
var r = left.Last ().Widen (right.Last ());
var list = Sequence<Interval>.Singleton (l);
var curRight = right.Tail;
while (curRight != null && curRight.Tail != null) {
var curLeft = left.Tail;
while (curLeft != null && curRight.Tail != null)
if (curLeft.Head.LessEqual (curRight.Head)) {
list = list.Cons (curRight.Head);
break;
}
}
list = list.Cons (r);
return list.Reverse ();
}
public override DisInterval Meet (DisInterval that)
{
DisInterval result;
if (this.TryTrivialMeet (that, out result))
return result;
bool isBottom;
var meetIntervals = Meet (intervals, that.intervals, out isBottom);
if (isBottom)
return BottomValue;
if (meetIntervals.Length () == 0)
return TopValue;
return For (meetIntervals);
}
static Sequence<Interval> Meet (Sequence<Interval> left, Sequence<Interval> right, out bool isBottom)
{
isBottom = true;
var list = Sequence<Interval>.Empty;
foreach (var leftIntv in left.AsEnumerable ()) {
foreach (var rightIntv in right.AsEnumerable ()) {
var res = leftIntv.Meet (rightIntv);
if (res.IsNormal ()) {
isBottom = false;
list = list.Cons (res);
}
}
}
return list.Reverse ();
}
public override DisInterval ImmutableVersion ()
{
return this;
}
public override DisInterval Clone ()
{
return this;
}
public override void Dump (TextWriter tw)
{
}
public override bool LessEqual (DisInterval that)
{
bool result;
if (this.TryTrivialLessEqual (that, out result))
return result;
if (!join_interval.LessEqual (that.join_interval))
return false;
return intervals.AsEnumerable ().All (inv => that.intervals.Any (inv.LessEqual));
}
public override DisInterval Join (DisInterval that, bool widening, out bool weaker)
{
weaker = false;
return Join (that);
}
public override DisInterval Join (DisInterval that)
{
DisInterval result;
if (this.TryTrivialJoin (that, out result))
return result;
var join = Join (intervals, that.intervals);
if (join.IsEmpty ())
return TopValue;
return For (join);
}
static Sequence<Interval> Join (Sequence<Interval> left, Sequence<Interval> right)
{
var list = Sequence<Interval>.Empty;
var curLeft = left;
var curRight = right;
while (!curLeft.IsEmpty () && !curRight.IsEmpty ()) {
var l = curLeft.Head;
var r = curRight.Head;
if (l.IsTop || r.IsTop)
return Sequence<Interval>.Empty;
if (l.IsBottom)
curLeft = curLeft.Tail;
else if (r.IsBottom)
curRight = curRight.Tail;
else if (l.LessEqual (r)) {
list = list.Cons (r);
curLeft = curLeft.Tail;
curRight = curRight.Tail;
}
else if (r.LessEqual (l)) {
list = list.Cons (l);
curLeft = curLeft.Tail;
curRight = curRight.Tail;
}
else if (r.OverlapsWith (l)) {
list = list.Cons (l.Join (r));
curLeft = curLeft.Tail;
curRight = curRight.Tail;
}
else if (l.OnTheLeftOf (r)) {
list = list.Cons (l);
curLeft = curLeft.Tail;
}
else if (r.OnTheLeftOf (l)) {
list = list.Cons (r);
curRight = curRight.Tail;
}
}
while (!curLeft.IsEmpty ()) {
list = list.Cons (curLeft.Head);
curLeft = curLeft.Tail;
}
while (!curRight.IsEmpty ()) {
list = list.Cons (curRight.Head);
curRight = curRight.Tail;
}
return list.Reverse ();
}
public static DisInterval For (Interval interval)
{
return new DisInterval (interval);
}
public static DisInterval For (Sequence<Interval> intervals)
{
return new DisInterval (intervals);
}
public override string ToString ()
{
if (IsTop)
return "Top";
if (IsBottom)
return this.BottomSymbolIfAny ();
if (intervals != null && intervals.Length () == 1)
return intervals.Head.ToString ();
return string.Format ("({0})", ToString (intervals));
}
string ToString (Sequence<Interval> list)
{
if (list == null)
return "null";
var sb = new StringBuilder ();
var first = true;
foreach (var intv in list.AsEnumerable ()) {
if (first)
first = false;
else
sb.Append (" ");
sb.Append (intv);
}
return sb.ToString ();
}
public DisInterval Select (Func<Interval, Interval> selector)
{
if (IsBottom)
return this;
if (IsTop)
return new DisInterval (selector (Interval.TopValue));
var list = Sequence<Interval>.Empty;
for (var cur = intervals; cur != null; cur = cur.Tail) {
var intv = selector (cur.Head);
if (intv.IsBottom)
return Bottom;
if (intv.IsTop)
return Top;
list = list.Cons (intv);
}
return new DisInterval (list.Reverse ());
}
public static DisInterval EverythingExcept (DisInterval interval)
{
var left = Interval.For (Rational.MinusInfinity, interval.LowerBound - 1L);
var right = Interval.For (interval.UpperBound + 1L, Rational.PlusInfinity);
if (left.IsNormal () && right.IsNormal ())
return new DisInterval (Sequence<Interval>.From (left, right));
if (left.IsNormal ())
return new DisInterval (left);
if (right.IsNormal ())
return new DisInterval (right);
return TopValue;
}
#region Nested type: State
enum State {
Normal = 0,
Top,
Bottom
}
#endregion
}
}