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