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