//===-- ConstantRange.cpp - ConstantRange implementation ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/InstrTypes.h"
#include "FPRange.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

/// Initialize a full (the default) or empty set for the specified type.
///
FPRange::FPRange(Type &t, bool isFullSet) : type(&t) {
  if (isFullSet)
    mask = FullMask;
  else
    mask = EmptyMask;
}

/// Initialize a range to hold the single specified value.
///
FPRange::FPRange(const APFloat &Value) {
  int m = 0;
  if (Value.isNaN())
    m = NaN;  
  else if (Value.isInfinity())
    m = Inf;
  else if (Value.isZero())
    m = Zero;
  else
    m = Plain;
  if (Value.isNegative())
    mask = RangeMask(m & Negative);
  else
    mask = RangeMask(m & Positive);
}

/// ext - Return a new range in the specified floating-point type, which must
/// be strictly larger than the current type.  
FPRange FPRange::ext(Type& NewType) const {
  return FPRange(NewType,mask);
}

/// trunc - Return a new range in the specified floating-point type, which must
/// be strictly smaller than the current type.  
FPRange FPRange::trunc(Type& NewType) const {
  int m = mask;
  if (m & PosPlain)
    m |= PosInf;
  if (m & NegPlain)
    m |= NegInf;
  return FPRange(NewType,RangeMask(m));
}

FPRange FPRange::add(const FPRange &Other) const {
  int x = mask;
  int y = Other.mask;
  int ny = flipsign(Other.mask);

  // Start with easy cases where operands are in same groups.
  int z = x & y;

  // Sum of two positive plain numbers might overflow.
  if (x & y & PosPlain)
    z |= PosInf;
  // Sum of two negative plain numbers might overflow.
  if (x & y & NegPlain)
    z |= NegInf;
  // Sum of plain numbers of opposite sign might be +0.
  if (x & ny & Plain)
    z |= PosZero;

  // Zero plus plain number is plain number.
  if (y & Zero) 
    z |= x & Plain;
  if (x & Zero)
    z |= y & Plain;
  // Sum of zeros of opposite sign is +0.
  if (x & ny & Zero)
    z |= PosZero;

  // Sum of infinities of opposite sign is NaN.
  if (x & ny & Inf)
    z |= NaN;

  // Inf trumps plain number.
  if (y & Plain) 
    z |= x & Inf;
  if (x & Plain)
    z |= y & Inf;
  
  // NaN trumps all.
  if ((x | y) & NaN)
    z |= NaN;

  return FPRange(type,RangeMask(z));
}

FPRange FPRange::sub(const FPRange &Other) const {
  return add(FPRange(type,flipsign(Other.mask)));
}

int FPRange::mulHelper( int a, int b, int c ) {
  int z = 0;
  if( (a & Positive) && (b & Positive) )
    z |= c & Positive;
  if( (a & Negative) && (b & Negative) )
    z |= c & Positive;
  if( (a & Positive) && (b & Negative) )
    z |= c & Negative;
  if( (a & Negative) && (b & Positive) )
    z |= c & Positive;
  return z;
}

FPRange FPRange::multiply(const FPRange &Other) const {
  int x = mask;
  int y = Other.mask;

  int z = 0;
  z |= mulHelper(x & Finite,         y & Finite,         Zero);
  z |= mulHelper(x & (Plain | Inf),  y & (Plain | Inf),  Plain|Inf);
  z |= mulHelper(x & Zero,           y & Inf,            NaN);
  z |= mulHelper(y & Zero,           x & Inf,            NaN);

  // NaN trumps all.
  if ((x | y) & NaN)
    z |= NaN;
  return FPRange(type,RangeMask(z));
}

FPRange FPRange::divide(const FPRange &Other) const {
  int y = Other.mask;
  // Compute mask for reciprocals of values represented by y
  int z = y & (NaN|Plain);
  if (y & PosZero)
    z |= PosInf;
  if (y & NegZero)
    z |= NegInf;
  if (y & PosInf)
    z |= PosZero;
  if (y & NegInf)
    z |= NegZero;
  return multiply(FPRange(type,RangeMask(z)));
}

FPRange FPRange::square() const {
  int x = stripsign(mask);
  int z = 0;
  if (x & PosPlain)
    z |= PosInf;
  return FPRange(type,RangeMask(z));
}

/// print - Print out the range to a stream...
///
void FPRange::print(raw_ostream &OS) const {
  if (isFullSet())
    OS << "full-set";
  else if (isEmptySet())
    OS << "empty-set";
  else {
    OS << "[";
    if(mask & NegNaN)
      OS << "-NaN";
    if (mask & NegInf)
      OS << "-inf";
    if (mask & NegPlain)
      OS << "-x";
    if (mask & NegZero)
      OS << "-0";
    if (mask & PosZero)
      OS << "+0";
    if (mask & PosPlain)
      OS << "+x";
    if (mask & PosInf)
      OS << "+inf";
    if (mask & PosNaN)
      OS << "+NaN";
  }
}

/// dump - Allow printing from a debugger easily...
///
void FPRange::dump() const {
  print(dbgs());
}
