1e1114045Speter klausler //===-- runtime/random.cpp ------------------------------------------------===//
2e1114045Speter klausler //
3e1114045Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e1114045Speter klausler // See https://llvm.org/LICENSE.txt for license information.
5e1114045Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e1114045Speter klausler //
7e1114045Speter klausler //===----------------------------------------------------------------------===//
8e1114045Speter klausler
9e1114045Speter klausler // Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and
10e1114045Speter klausler // RANDOM_SEED.
11e1114045Speter klausler
12830c0b90SPeter Klausler #include "flang/Runtime/random.h"
13e1114045Speter klausler #include "lock.h"
14e1114045Speter klausler #include "flang/Common/leading-zero-bit-count.h"
15e1114045Speter klausler #include "flang/Common/uint128.h"
16830c0b90SPeter Klausler #include "flang/Runtime/cpp-type.h"
17830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
18*4daa33f6SPeter Klausler #include "flang/Runtime/float128.h"
19e1114045Speter klausler #include <algorithm>
20e1114045Speter klausler #include <cmath>
21e1114045Speter klausler #include <cstdint>
22e1114045Speter klausler #include <ctime>
23e1114045Speter klausler #include <limits>
24e1114045Speter klausler #include <memory>
25e1114045Speter klausler #include <random>
26e1114045Speter klausler
27e1114045Speter klausler namespace Fortran::runtime {
28e1114045Speter klausler
29e1114045Speter klausler // Newer "Minimum standard", recommended by Park, Miller, and Stockmeyer in
30e1114045Speter klausler // 1993. Same as C++17 std::minstd_rand, but explicitly instantiated for
31e1114045Speter klausler // permanence.
32e1114045Speter klausler using Generator =
33e1114045Speter klausler std::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
34e1114045Speter klausler
35e1114045Speter klausler using GeneratedWord = typename Generator::result_type;
36e1114045Speter klausler static constexpr std::uint64_t range{
37e1114045Speter klausler static_cast<std::uint64_t>(Generator::max() - Generator::min() + 1)};
38e1114045Speter klausler static constexpr bool rangeIsPowerOfTwo{(range & (range - 1)) == 0};
39e1114045Speter klausler static constexpr int rangeBits{
40e1114045Speter klausler 64 - common::LeadingZeroBitCount(range) - !rangeIsPowerOfTwo};
41e1114045Speter klausler
42e1114045Speter klausler static Lock lock;
43e1114045Speter klausler static Generator generator;
44ea1a69d6SPeter Klausler static std::optional<GeneratedWord> nextValue;
45ea1a69d6SPeter Klausler
46ea1a69d6SPeter Klausler // Call only with lock held
GetNextValue()47ea1a69d6SPeter Klausler static GeneratedWord GetNextValue() {
48ea1a69d6SPeter Klausler GeneratedWord result;
49ea1a69d6SPeter Klausler if (nextValue.has_value()) {
50ea1a69d6SPeter Klausler result = *nextValue;
51ea1a69d6SPeter Klausler nextValue.reset();
52ea1a69d6SPeter Klausler } else {
53ea1a69d6SPeter Klausler result = generator();
54ea1a69d6SPeter Klausler }
55ea1a69d6SPeter Klausler return result;
56ea1a69d6SPeter Klausler }
57e1114045Speter klausler
58e1114045Speter klausler template <typename REAL, int PREC>
Generate(const Descriptor & harvest)59e1114045Speter klausler inline void Generate(const Descriptor &harvest) {
60e1114045Speter klausler static constexpr std::size_t minBits{
61e1114045Speter klausler std::max<std::size_t>(PREC, 8 * sizeof(GeneratedWord))};
62e1114045Speter klausler using Int = common::HostUnsignedIntType<minBits>;
63e1114045Speter klausler static constexpr std::size_t words{
64e1114045Speter klausler static_cast<std::size_t>(PREC + rangeBits - 1) / rangeBits};
65e1114045Speter klausler std::size_t elements{harvest.Elements()};
66e1114045Speter klausler SubscriptValue at[maxRank];
67e1114045Speter klausler harvest.GetLowerBounds(at);
68e1114045Speter klausler {
69e1114045Speter klausler CriticalSection critical{lock};
70e1114045Speter klausler for (std::size_t j{0}; j < elements; ++j) {
71f3278e0fSPeter Klausler while (true) {
72ea1a69d6SPeter Klausler Int fraction{GetNextValue()};
73e1114045Speter klausler if constexpr (words > 1) {
74e1114045Speter klausler for (std::size_t k{1}; k < words; ++k) {
75f3278e0fSPeter Klausler static constexpr auto rangeMask{
76f3278e0fSPeter Klausler (GeneratedWord{1} << rangeBits) - 1};
77ea1a69d6SPeter Klausler GeneratedWord word{(GetNextValue() - generator.min()) & rangeMask};
78e1114045Speter klausler fraction = (fraction << rangeBits) | word;
79e1114045Speter klausler }
80e1114045Speter klausler }
81e1114045Speter klausler fraction >>= words * rangeBits - PREC;
82f3278e0fSPeter Klausler REAL next{std::ldexp(static_cast<REAL>(fraction), -(PREC + 1))};
83f3278e0fSPeter Klausler if (next >= 0.0 && next < 1.0) {
84f3278e0fSPeter Klausler *harvest.Element<REAL>(at) = next;
85f3278e0fSPeter Klausler break;
86f3278e0fSPeter Klausler }
87f3278e0fSPeter Klausler }
88e1114045Speter klausler harvest.IncrementSubscripts(at);
89e1114045Speter klausler }
90e1114045Speter klausler }
91e1114045Speter klausler }
92e1114045Speter klausler
93e1114045Speter klausler extern "C" {
94e1114045Speter klausler
RTNAME(RandomInit)95e1114045Speter klausler void RTNAME(RandomInit)(bool repeatable, bool /*image_distinct*/) {
96e1114045Speter klausler // TODO: multiple images and image_distinct: add image number
97e1114045Speter klausler {
98e1114045Speter klausler CriticalSection critical{lock};
99e1114045Speter klausler if (repeatable) {
100e1114045Speter klausler generator.seed(0);
101e1114045Speter klausler } else {
102e1114045Speter klausler generator.seed(std::time(nullptr));
103e1114045Speter klausler }
104e1114045Speter klausler }
105e1114045Speter klausler }
106e1114045Speter klausler
RTNAME(RandomNumber)107e1114045Speter klausler void RTNAME(RandomNumber)(
108e1114045Speter klausler const Descriptor &harvest, const char *source, int line) {
109e1114045Speter klausler Terminator terminator{source, line};
110e1114045Speter klausler auto typeCode{harvest.type().GetCategoryAndKind()};
111e1114045Speter klausler RUNTIME_CHECK(terminator, typeCode && typeCode->first == TypeCategory::Real);
112e1114045Speter klausler int kind{typeCode->second};
113e1114045Speter klausler switch (kind) {
114e1114045Speter klausler // TODO: REAL (2 & 3)
115e1114045Speter klausler case 4:
116e1114045Speter klausler Generate<CppTypeFor<TypeCategory::Real, 4>, 24>(harvest);
117*4daa33f6SPeter Klausler return;
118e1114045Speter klausler case 8:
119e1114045Speter klausler Generate<CppTypeFor<TypeCategory::Real, 8>, 53>(harvest);
120*4daa33f6SPeter Klausler return;
121e1114045Speter klausler case 10:
122*4daa33f6SPeter Klausler if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
123*4daa33f6SPeter Klausler #if LDBL_MANT_DIG == 64
124e1114045Speter klausler Generate<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest);
125*4daa33f6SPeter Klausler return;
126e1114045Speter klausler #endif
127e1114045Speter klausler }
128*4daa33f6SPeter Klausler break;
129*4daa33f6SPeter Klausler case 16:
130*4daa33f6SPeter Klausler if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
131*4daa33f6SPeter Klausler #if LDBL_MANT_DIG == 113
132*4daa33f6SPeter Klausler Generate<CppTypeFor<TypeCategory::Real, 16>, 113>(harvest);
133*4daa33f6SPeter Klausler return;
134*4daa33f6SPeter Klausler #endif
135*4daa33f6SPeter Klausler }
136*4daa33f6SPeter Klausler break;
137*4daa33f6SPeter Klausler }
138*4daa33f6SPeter Klausler terminator.Crash("not yet implemented: RANDOM_NUMBER(): REAL kind %d", kind);
139e1114045Speter klausler }
140e1114045Speter klausler
RTNAME(RandomSeedSize)141e1114045Speter klausler void RTNAME(RandomSeedSize)(
142e1114045Speter klausler const Descriptor &size, const char *source, int line) {
143e1114045Speter klausler Terminator terminator{source, line};
144e1114045Speter klausler auto typeCode{size.type().GetCategoryAndKind()};
145e1114045Speter klausler RUNTIME_CHECK(terminator,
146e1114045Speter klausler size.rank() == 0 && typeCode && typeCode->first == TypeCategory::Integer);
147e1114045Speter klausler int kind{typeCode->second};
148e1114045Speter klausler switch (kind) {
149e1114045Speter klausler case 4:
150e1114045Speter klausler *size.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = 1;
151e1114045Speter klausler break;
152e1114045Speter klausler case 8:
153e1114045Speter klausler *size.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = 1;
154e1114045Speter klausler break;
155e1114045Speter klausler default:
156d4609ae4SPeter Steinfeld terminator.Crash(
157d4609ae4SPeter Steinfeld "not yet implemented: RANDOM_SEED(SIZE=): kind %d\n", kind);
158e1114045Speter klausler }
159e1114045Speter klausler }
160e1114045Speter klausler
RTNAME(RandomSeedPut)161e1114045Speter klausler void RTNAME(RandomSeedPut)(
162e1114045Speter klausler const Descriptor &put, const char *source, int line) {
163e1114045Speter klausler Terminator terminator{source, line};
164e1114045Speter klausler auto typeCode{put.type().GetCategoryAndKind()};
165e1114045Speter klausler RUNTIME_CHECK(terminator,
166e1114045Speter klausler put.rank() == 1 && typeCode && typeCode->first == TypeCategory::Integer &&
167e1114045Speter klausler put.GetDimension(0).Extent() >= 1);
168e1114045Speter klausler int kind{typeCode->second};
169e1114045Speter klausler GeneratedWord seed;
170e1114045Speter klausler switch (kind) {
171e1114045Speter klausler case 4:
172e1114045Speter klausler seed = *put.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>();
173e1114045Speter klausler break;
174e1114045Speter klausler case 8:
175e1114045Speter klausler seed = *put.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>();
176e1114045Speter klausler break;
177e1114045Speter klausler default:
178d4609ae4SPeter Steinfeld terminator.Crash("not yet implemented: RANDOM_SEED(PUT=): kind %d\n", kind);
179e1114045Speter klausler }
180e1114045Speter klausler {
181e1114045Speter klausler CriticalSection critical{lock};
182e1114045Speter klausler generator.seed(seed);
183ea1a69d6SPeter Klausler nextValue = seed;
184e1114045Speter klausler }
185e1114045Speter klausler }
186e1114045Speter klausler
RTNAME(RandomSeedDefaultPut)187e1114045Speter klausler void RTNAME(RandomSeedDefaultPut)() {
188e1114045Speter klausler // TODO: should this be time &/or image dependent?
189e1114045Speter klausler {
190e1114045Speter klausler CriticalSection critical{lock};
191e1114045Speter klausler generator.seed(0);
192e1114045Speter klausler }
193e1114045Speter klausler }
194e1114045Speter klausler
RTNAME(RandomSeedGet)195e1114045Speter klausler void RTNAME(RandomSeedGet)(
196e1114045Speter klausler const Descriptor &got, const char *source, int line) {
197e1114045Speter klausler Terminator terminator{source, line};
198e1114045Speter klausler auto typeCode{got.type().GetCategoryAndKind()};
199e1114045Speter klausler RUNTIME_CHECK(terminator,
200e1114045Speter klausler got.rank() == 1 && typeCode && typeCode->first == TypeCategory::Integer &&
201e1114045Speter klausler got.GetDimension(0).Extent() >= 1);
202e1114045Speter klausler int kind{typeCode->second};
203e1114045Speter klausler GeneratedWord seed;
204e1114045Speter klausler {
205e1114045Speter klausler CriticalSection critical{lock};
206ea1a69d6SPeter Klausler seed = GetNextValue();
207ea1a69d6SPeter Klausler nextValue = seed;
208e1114045Speter klausler }
209e1114045Speter klausler switch (kind) {
210e1114045Speter klausler case 4:
211e1114045Speter klausler *got.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = seed;
212e1114045Speter klausler break;
213e1114045Speter klausler case 8:
214e1114045Speter klausler *got.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = seed;
215e1114045Speter klausler break;
216e1114045Speter klausler default:
217d4609ae4SPeter Steinfeld terminator.Crash("not yet implemented: RANDOM_SEED(GET=): kind %d\n", kind);
218e1114045Speter klausler }
219e1114045Speter klausler }
220e1114045Speter klausler } // extern "C"
221e1114045Speter klausler } // namespace Fortran::runtime
222