libmath: Add HashCombine

Add a helper function to combine hashes and
make half and mat/vec std::hashable.

Bug: 158790260
Test: libmath unit tests
Change-Id: I11eb70152ce5a35a6d833cd108732e5b21704fe1
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 22d4712..ab96950 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -22,6 +22,7 @@
         "com.android.media.swcodec",
         "com.android.neuralnetworks",
     ],
+
     min_sdk_version: "29",
 
     export_include_dirs: ["include"],
@@ -32,4 +33,23 @@
     }
 }
 
+cc_library_headers {
+    name: "libmath_headers",
+    export_include_dirs: ["include"],
+    host_supported: true,
+    vendor_available: true,
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+    min_sdk_version: "apex_inherit",
+
+    target:  {
+        windows: {
+            enabled: true,
+        }
+    }
+}
+
 subdirs = ["tests"]
diff --git a/libs/math/include/math/HashCombine.h b/libs/math/include/math/HashCombine.h
new file mode 100644
index 0000000..e91b52b
--- /dev/null
+++ b/libs/math/include/math/HashCombine.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+
+namespace android {
+static inline void hashCombineSingleHashed(size_t& combinedHash, size_t hash) {
+    combinedHash = 31 * combinedHash + hash;
+}
+
+template<typename T>
+static inline void hashCombineSingle(size_t& combinedHash, const T& val) {
+    hashCombineSingleHashed(combinedHash, std::hash<T>{}(val));
+}
+
+template<typename... Types>
+static inline size_t hashCombine(const Types& ... args) {
+    size_t hash = 0;
+    ( hashCombineSingle(hash, args), ... );
+    return hash;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 20f852f..0dac662 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -19,9 +19,11 @@
 
 #include <math.h>
 #include <stdint.h>
+#include <math/HashCombine.h>
 #include <sys/types.h>
 
 #include <cmath>
+#include <functional>
 #include <limits>
 #include <iostream>
 
@@ -250,6 +252,17 @@
         }
         return r;
     }
+
+    // This isn't strictly a unary operator, but it is a common place shared between both
+    // matrix and vector classes
+    size_t hash() const {
+        VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+        size_t hashed = 0;
+        for (size_t i = 0; i < rv.size(); i++) {
+            android::hashCombineSingle(hashed, rv[i]);
+        }
+        return hashed;
+    }
 };
 
 /*
@@ -606,3 +619,16 @@
 // -------------------------------------------------------------------------------------
 }  // namespace details
 }  // namespace android
+
+namespace std {
+    template<template<typename T> class VECTOR, typename T>
+    struct hash<VECTOR<T>> {
+        static constexpr bool IS_VECTOR =
+            std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
+
+        typename std::enable_if<IS_VECTOR, size_t>::type
+        operator()(const VECTOR<T>& v) const {
+            return v.hash();
+        }
+    };
+}
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 617a0ab..5ec9bf7 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <functional>
 #include <iosfwd>
 #include <limits>
 #include <type_traits>
@@ -202,6 +203,12 @@
     inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
 };
 
+template<> struct hash<android::half> {
+    size_t operator()(const android::half& half) {
+        return std::hash<float>{}(half);
+    }
+};
+
 } // namespace std
 
 #ifdef LIKELY_DEFINED_LOCAL
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
index 0184f56..472d337 100644
--- a/libs/math/tests/Android.bp
+++ b/libs/math/tests/Android.bp
@@ -41,3 +41,10 @@
     static_libs: ["libmath"],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "hashcombine_test",
+    srcs: ["hashcombine_test.cpp"],
+    static_libs: ["libmath"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 604072e..a514d98 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -94,4 +94,13 @@
     EXPECT_EQ(f4.xy, h2);
 }
 
+
+TEST_F(HalfTest, Hash) {
+    float4 f4a(1,2,3,4);
+    float4 f4b(2,2,3,4);
+    half4 h4a(f4a), h4b(f4b);
+
+    EXPECT_NE(std::hash<half4>{}(h4a), std::hash<half4>{}(h4b));
+}
+
 }; // namespace android
diff --git a/libs/math/tests/hashcombine_test.cpp b/libs/math/tests/hashcombine_test.cpp
new file mode 100644
index 0000000..96c6e81
--- /dev/null
+++ b/libs/math/tests/hashcombine_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HashCombineTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HashCombineTest : public testing::Test {
+protected:
+};
+
+TEST_F(HashCombineTest, Basics) {
+    char a = 40;
+    int b = 32;
+    int c = 55;
+    float d = 42.f;
+    float d_ = 42.1f;
+
+    EXPECT_NE(hashCombine(a, b, c, d), hashCombine(a, b, c, d_));
+}
+
+}; // namespace android