111 lines · plain
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains the definition of the IndexedRange class, which provides11/// an indexable view over a contiguous range of numeric values.12///13//===----------------------------------------------------------------------===//14 15#ifndef MATHTEST_INDEXEDRANGE_HPP16#define MATHTEST_INDEXEDRANGE_HPP17 18#include "mathtest/Numerics.hpp"19 20#include "llvm/Support/MathExtras.h"21 22#include <cassert>23#include <cstdint>24#include <limits>25#include <type_traits>26 27namespace mathtest {28 29template <typename T> class [[nodiscard]] IndexedRange {30 static_assert(IsFloatingPoint_v<T> || std::is_integral_v<T>,31 "Type T must be an integral or floating-point type");32 static_assert(sizeof(T) <= sizeof(uint64_t),33 "Type T must be no wider than uint64_t");34 35public:36 constexpr IndexedRange() noexcept37 : IndexedRange(getMinOrNegInf<T>(), getMaxOrInf<T>(), true) {}38 39 explicit constexpr IndexedRange(T Begin, T End, bool Inclusive) noexcept40 : MappedBegin(mapToOrderedUnsigned(Begin)),41 MappedEnd(mapToOrderedUnsigned(End)) {42 if (Inclusive) {43 assert((Begin <= End) && "Begin must be less than or equal to End");44 } else {45 assert((Begin < End) && "Begin must be less than End");46 --MappedEnd;47 }48 49 assert(((MappedEnd - MappedBegin) < std::numeric_limits<uint64_t>::max()) &&50 "The range is too large to index");51 }52 53 [[nodiscard]] constexpr uint64_t getSize() const noexcept {54 return static_cast<uint64_t>(MappedEnd) - MappedBegin + 1;55 }56 57 [[nodiscard]] constexpr T operator[](uint64_t Index) const noexcept {58 assert((Index < getSize()) && "Index is out of range");59 60 StorageType MappedValue = MappedBegin + Index;61 return mapFromOrderedUnsigned(MappedValue);62 }63 64private:65 using StorageType = StorageTypeOf_t<T>;66 67 // Linearise T values into an ordered unsigned space:68 // * The mapping is monotonic: a >= b if, and only if, map(a) >= map(b).69 // * The difference |map(a) − map(b)| equals the number of representable70 // values between a and b within the same type.71 static constexpr StorageType mapToOrderedUnsigned(T Value) {72 if constexpr (IsFloatingPoint_v<T>) {73 constexpr StorageType SignMask = FPBits<T>::SIGN_MASK;74 StorageType Unsigned = FPBits<T>(Value).uintval();75 return (Unsigned & SignMask) ? SignMask - (Unsigned - SignMask) - 176 : SignMask + Unsigned;77 }78 79 if constexpr (std::is_signed_v<T>) {80 constexpr StorageType SignMask = llvm::maskLeadingOnes<StorageType>(1);81 return __builtin_bit_cast(StorageType, Value) ^ SignMask;82 }83 84 return Value;85 }86 87 static constexpr T mapFromOrderedUnsigned(StorageType MappedValue) {88 if constexpr (IsFloatingPoint_v<T>) {89 constexpr StorageType SignMask = FPBits<T>::SIGN_MASK;90 StorageType Unsigned = (MappedValue < SignMask)91 ? (SignMask - MappedValue) + SignMask - 192 : MappedValue - SignMask;93 94 return FPBits<T>(Unsigned).get_val();95 }96 97 if constexpr (std::is_signed_v<T>) {98 constexpr StorageType SignMask = llvm::maskLeadingOnes<StorageType>(1);99 return __builtin_bit_cast(T, MappedValue ^ SignMask);100 }101 102 return MappedValue;103 }104 105 StorageType MappedBegin;106 StorageType MappedEnd;107};108} // namespace mathtest109 110#endif // MATHTEST_INDEXEDRANGE_HPP111