//===- ConstantRangeList.h - A list of constant ranges ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Represent a list of signed ConstantRange and do NOT support wrap around the
// end of the numeric range. Ranges in the list are ordered and not overlapping.
// Ranges should have the same bitwidth. Each range's lower should be less than
// its upper.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_CONSTANTRANGELIST_H
#define LLVM_IR_CONSTANTRANGELIST_H

#include "llvm/ADT/APInt.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include <cstddef>
#include <cstdint>

namespace llvm {

class raw_ostream;

/// This class represents a list of constant ranges.
class [[nodiscard]] ConstantRangeList {
  SmallVector<ConstantRange, 2> Ranges;

public:
  ConstantRangeList() = default;
  ConstantRangeList(ArrayRef<ConstantRange> RangesRef) {
    assert(isOrderedRanges(RangesRef));
    for (const ConstantRange &R : RangesRef) {
      assert(empty() || R.getBitWidth() == getBitWidth());
      Ranges.push_back(R);
    }
  }

  // Return true if the ranges are non-overlapping and increasing.
  LLVM_ABI static bool isOrderedRanges(ArrayRef<ConstantRange> RangesRef);
  LLVM_ABI static std::optional<ConstantRangeList>
  getConstantRangeList(ArrayRef<ConstantRange> RangesRef);

  ArrayRef<ConstantRange> rangesRef() const { return Ranges; }
  SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
  SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
  SmallVectorImpl<ConstantRange>::const_iterator begin() const {
    return Ranges.begin();
  }
  SmallVectorImpl<ConstantRange>::const_iterator end() const {
    return Ranges.end();
  }
  ConstantRange getRange(unsigned i) const { return Ranges[i]; }

  /// Return true if this list contains no members.
  bool empty() const { return Ranges.empty(); }

  /// Get the bit width of this ConstantRangeList. It is invalid to call this
  /// with an empty range.
  uint32_t getBitWidth() const { return Ranges.front().getBitWidth(); }

  /// Return the number of ranges in this ConstantRangeList.
  size_t size() const { return Ranges.size(); }

  /// Insert a new range to Ranges and keep the list ordered.
  LLVM_ABI void insert(const ConstantRange &NewRange);
  void insert(int64_t Lower, int64_t Upper) {
    insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
                         APInt(64, Upper, /*isSigned=*/true)));
  }

  LLVM_ABI void subtract(const ConstantRange &SubRange);

  /// Return the range list that results from the union of this
  /// ConstantRangeList with another ConstantRangeList, "CRL".
  LLVM_ABI ConstantRangeList unionWith(const ConstantRangeList &CRL) const;

  /// Return the range list that results from the intersection of this
  /// ConstantRangeList with another ConstantRangeList, "CRL".
  LLVM_ABI ConstantRangeList intersectWith(const ConstantRangeList &CRL) const;

  /// Return true if this range list is equal to another range list.
  bool operator==(const ConstantRangeList &CRL) const {
    return Ranges == CRL.Ranges;
  }
  bool operator!=(const ConstantRangeList &CRL) const {
    return !operator==(CRL);
  }

  /// Print out the ranges to a stream.
  LLVM_ABI void print(raw_ostream &OS) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  void dump() const;
#endif
};

} // end namespace llvm

#endif // LLVM_IR_CONSTANTRANGELIST_H
