From 7bdc076a460a5085c978c45a6a5d4cf76be093b2 Mon Sep 17 00:00:00 2001
From: Maximilian Reininghaus <maximilian.reininghaus@kit.edu>
Date: Mon, 17 Dec 2018 17:54:04 +0100
Subject: [PATCH] uniform real distribution with units

---
 Framework/Random/CMakeLists.txt            |  1 +
 Framework/Random/UniformRealDistribution.h | 32 +++++++++++++++
 Framework/Random/testRandom.cc             | 48 +++++++++++++++++++++-
 3 files changed, 80 insertions(+), 1 deletion(-)
 create mode 100644 Framework/Random/UniformRealDistribution.h

diff --git a/Framework/Random/CMakeLists.txt b/Framework/Random/CMakeLists.txt
index 3c1947d2..230f84f6 100644
--- a/Framework/Random/CMakeLists.txt
+++ b/Framework/Random/CMakeLists.txt
@@ -7,6 +7,7 @@ set (
 set (
   CORSIKArandom_HEADERS
   RNGManager.h
+  UniformRealDistribution.h
   )
 
 set (
diff --git a/Framework/Random/UniformRealDistribution.h b/Framework/Random/UniformRealDistribution.h
new file mode 100644
index 00000000..c90ab674
--- /dev/null
+++ b/Framework/Random/UniformRealDistribution.h
@@ -0,0 +1,32 @@
+#ifndef _include_random_distributions_h
+#define _include_random_distributions_h
+
+#include <corsika/units/PhysicalUnits.h>
+#include <random>
+
+namespace corsika::random {
+
+  template <class TQuantity>
+  class UniformRealDistribution {
+    using RealType = typename TQuantity::value_type;
+    std::uniform_real_distribution<RealType> dist{RealType(0.), RealType(1.)};
+
+    TQuantity const a, b;
+
+  public:
+    UniformRealDistribution(TQuantity b)
+        : a{TQuantity(phys::units::detail::magnitude_tag, 0)}
+        , b(b) {}
+    UniformRealDistribution(TQuantity a, TQuantity b)
+        : a(a)
+        , b(b) {}
+
+    template <class Generator>
+    TQuantity operator()(Generator& g) {
+      return a + dist(g) * (b - a);
+    }
+  };
+
+} // namespace corsika::random
+
+#endif
diff --git a/Framework/Random/testRandom.cc b/Framework/Random/testRandom.cc
index 041a96c2..56d8157a 100644
--- a/Framework/Random/testRandom.cc
+++ b/Framework/Random/testRandom.cc
@@ -1,4 +1,3 @@
-
 /**
  * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
  *
@@ -14,7 +13,11 @@
 #include <catch2/catch.hpp>
 
 #include <corsika/random/RNGManager.h>
+#include <corsika/random/UniformRealDistribution.h>
+#include <corsika/units/PhysicalUnits.h>
 #include <iostream>
+#include <limits>
+#include <random>
 
 using namespace corsika::random;
 
@@ -37,3 +40,46 @@ SCENARIO("random-number streams can be registered and retrieved") {
     }
   }
 }
+
+TEST_CASE("UniformRealDistribution") {
+  using namespace corsika::units::si;
+  std::mt19937 rng;
+
+  corsika::random::UniformRealDistribution<LengthType> dist(1_m, 2_m);
+
+  SECTION("range") {
+    corsika::random::UniformRealDistribution<LengthType> dist(1_m, 2_m);
+
+    LengthType min =
+        +1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
+    LengthType max =
+        -1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
+
+    for (int i{0}; i < 1'000'000; ++i) {
+      LengthType x = dist(rng);
+      min = std::min(min, x);
+      max = std::max(max, x);
+    }
+
+    CHECK(min / 1_m == Approx(1.));
+    CHECK(max / 2_m == Approx(1.));
+  }
+
+  SECTION("range") {
+    corsika::random::UniformRealDistribution<LengthType> dist(18_cm);
+
+    LengthType min =
+        +1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
+    LengthType max =
+        -1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
+
+    for (int i{0}; i < 1'000'000; ++i) {
+      LengthType x = dist(rng);
+      min = std::min(min, x);
+      max = std::max(max, x);
+    }
+
+    CHECK(min / 1_m == Approx(0.).margin(1e-3));
+    CHECK(max / 18_cm == Approx(1.));
+  }
+}
-- 
GitLab