From 0111fa506aac40b0e10fbed115dad44847269f49 Mon Sep 17 00:00:00 2001
From: Matej Ferencevic <matej.ferencevic@memgraph.io>
Date: Thu, 17 Jan 2019 13:46:01 +0100
Subject: [PATCH] Add logarithmic functions

Reviewers: mtomic, teon.banek

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1811
---
 src/utils/math.hpp        | 26 ++++++++++++++++++++++++++
 tests/unit/CMakeLists.txt |  3 +++
 tests/unit/utils_math.cpp | 12 ++++++++++++
 3 files changed, 41 insertions(+)
 create mode 100644 src/utils/math.hpp
 create mode 100644 tests/unit/utils_math.cpp

diff --git a/src/utils/math.hpp b/src/utils/math.hpp
new file mode 100644
index 000000000..c686fdaff
--- /dev/null
+++ b/src/utils/math.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <cstdint>
+#include <experimental/type_traits>
+
+#include <glog/logging.h>
+
+namespace utils {
+
+static_assert(
+    std::experimental::is_same_v<uint64_t, unsigned long>,
+    "utils::Log requires uint64_t to be implemented as unsigned long.");
+
+/// This function computes the log2 function on integer types. It is faster than
+/// the cmath `log2` function because it doesn't use floating point values for
+/// calculation.
+inline uint64_t Log2(uint64_t val) {
+  // The `clz` function is undefined when the passed value is 0 and the value of
+  // `log` is `-inf` so we special case it here.
+  if (val == 0) return 0;
+  // clzl = count leading zeros from long
+  //        ^     ^       ^          ^
+  int ret = __builtin_clzl(val);
+  return 64UL - static_cast<uint64_t>(ret) - 1UL;
+}
+}  // namespace utils
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 0c1654d64..c603b6f4d 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -323,6 +323,9 @@ target_link_libraries(${test_prefix}utils_exceptions mg-utils)
 add_unit_test(utils_executor.cpp)
 target_link_libraries(${test_prefix}utils_executor mg-utils)
 
+add_unit_test(utils_math.cpp)
+target_link_libraries(${test_prefix}utils_math mg-utils)
+
 add_unit_test(utils_on_scope_exit.cpp)
 target_link_libraries(${test_prefix}utils_on_scope_exit mg-utils)
 
diff --git a/tests/unit/utils_math.cpp b/tests/unit/utils_math.cpp
new file mode 100644
index 000000000..7e74cf3ae
--- /dev/null
+++ b/tests/unit/utils_math.cpp
@@ -0,0 +1,12 @@
+#include <cmath>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "utils/math.hpp"
+
+TEST(UtilsMath, Log2) {
+  for (uint64_t i = 1; i < 1000000; ++i) {
+    ASSERT_EQ(utils::Log2(i), static_cast<uint64_t>(log2(i)));
+  }
+}