// bslh_fibonaccibadhashwrapper.h                                     -*-C++-*-
#ifndef INCLUDED_BSLH_FIBONACCIBADHASHWRAPPER
#define INCLUDED_BSLH_FIBONACCIBADHASHWRAPPER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a wrapper to improve "bad" hash algorithms.
//
//@CLASSES:
//  bslh::FibonacciBadHashWrapper: wrapper to improve "bad" hashes
//
//@DESCRIPTION: This component provides a functor type,
// `bslh::FibonacciBadHashWrapper`, that can be used to wrap a hash algorithm
// ameliorating the unwanted properties of a poor hash function.
//
// For example, the identity function is often used to hash integer values (see
// `bsl::hash<int>`), and although the identity function has the property that
// two different input values are unlikely to result in the same hash value, it
// does not have many of the other desirable properties of a hash function
// (common input values are not evenly distributed over the range of hash
// values, a small change the input does not change the hash value extensively,
// and it is easy to determine the input from a hash value).
//
// The `bslh::FibonacciBadHashWrapper` can be used on the output of a bad hash
// function (like the identity function) to address some (but not all) of those
// deficiencies.  Specifically, the resulting hash value is more evenly
// distributed over the range, and a small change in the input will result in a
// larger change in the hash value.
//
///Requirements Upon Wrapped Hash Function Object
///----------------------------------------------
// The wrapped hash function object must be a function object, default
// constructible, copy constructible, and destructible.  The result of
// `operator()` must be convertable to `size_t`.  For an instance of a hash
// functor, `operator()` must provide the same result for each argument value
// for the lifetime of the functor.  For the `bslh::FibonacciBadHashWrapper` to
// support the type `KEY` in its `operator()`, the wrapped hash function's
// `operator()` must be invokable with the type `KEY`.
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1: Obtaining Most-Significant and Least-Significant Bit Variation
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we are using a hash table that requires variation in the
// most-significant and least-significant bits to operate efficiently (e.g.,
// Abseil's flat hash map).  The keys we will be using are small integer
// values, and we would like to use the identity function as the hash functor
// since it is efficient.  A simple and efficient method to obtain a hash
// functor with the necessary qualities is to wrap the identity functor with
// `bslh::FibonaccaBadHashWrapper`.
//
// First, we define our `IdentityHash` class.
// ```
// class IdentityHash {
//     // This class provides a hash algorithm that provides the "identity"
//     // mapping from key to hash.
//
//   public:
//     size_t operator()(const int key) const
//         // Return the specified 'key'.
//     {
//         return static_cast<size_t>(key);
//     }
// };
// ```
// Then, we instantiate an instance of the identity functor and the wrapped
// functor.
// ```
// IdentityHash                                identity;
// bslh::FibonacciBadHashWrapper<IdentityHash> wrapped;
// ```
// Finally, we examine the range of values obtained from small integer values:
// ```
// if (8 == sizeof(size_t)) {
//     ASSERT(18446744073709551614ull == identity(-2));
//     ASSERT(18446744073709551615ull == identity(-1));
//     ASSERT(                      0 == identity( 0));
//     ASSERT(                      1 == identity( 1));
//     ASSERT(                      2 == identity( 2));
//
//     ASSERT(14092058508772706262ull == wrapped(-2));
//     ASSERT( 7046029254386353131ull == wrapped(-1));
//     ASSERT(                      0 == wrapped( 0));
//     ASSERT(11400714819323198485ull == wrapped( 1));
//     ASSERT( 4354685564936845354ull == wrapped( 2));
// }
// else {
//     ASSERT(4294967294u == identity(-2));
//     ASSERT(4294967295u == identity(-1));
//     ASSERT(          0 == identity( 0));
//     ASSERT(          1 == identity( 1));
//     ASSERT(          2 == identity( 2));
//
//     ASSERT(  23791574u == wrapped(-2));
//     ASSERT(2159379435u == wrapped(-1));
//     ASSERT(          0 == wrapped( 0));
//     ASSERT(2135587861u == wrapped( 1));
//     ASSERT(4271175722u == wrapped( 2));
// }
// ```

#include <bslscm_version.h>

#include <bsls_types.h>  // 'bsls::Types::Uint64'

#include <stddef.h>      // 'size_t'

namespace BloombergLP {
namespace bslh {

                      // =============================
                      // class FibonacciBadHashWrapper
                      // =============================

/// This class provides a hash algorithm wrapper that improves the
/// distribution of varying bits from the `HASH` applied to the `KEY`.
template <class HASH>
class FibonacciBadHashWrapper {

    // DATA
    HASH d_hash;  // hash functor

  public:
    // PUBLIC CLASS DATA

    /// The value corresponds to:
    /// ```
    /// 2^64 / phi - 1
    /// ```
    /// where *phi* is the Golden Ratio and 1 is subtracted to avoid the
    /// value being a multiple of 2 (that would throw away one bit of
    /// information from the resultant hash value).  See
    /// https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing.
    static const bsls::Types::Uint64 k_FIBONACCI_HASH_MULTIPLIER
                                                     = 11400714819323198485ull;

    // CREATORS

    /// Create a `FibonacciBadHashWrapper` having a default constructed
    /// hash.
    FibonacciBadHashWrapper();

    /// Create a `FibonacciBadHashWrapper` having the specified `hash`.
    explicit FibonacciBadHashWrapper(const HASH& hash);

    //! FibonacciBadHashWrapper(
    //!                     const FibonacciBadHashWrapper& original) = default;
        // Create a 'FibonacciBadHashWrapper' object having the value of the
        // specified 'original' object.

    //! ~FibonacciBadHashWrapper() = default;
        // Destroy this object.

    // MANIPULATORS
    //! FibonacciBadHashWrapper& operator=(
    //!                          const FibonacciBadHashWrapper& rhs) = default;
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    // ACCESSORS

    /// Return the hash of the specified `key`, computed as the product
    /// of the result of the hash function supplied at construction and
    /// `k_FIBONACCI_HASH_MULTIPLIER`.
    template <class KEY>
    size_t operator()(const KEY& key) const;
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                      // ------------------------------
                      // struct FibonacciBadHashWrapper
                      // ------------------------------

// CREATORS
template <class HASH>
inline
FibonacciBadHashWrapper<HASH>::FibonacciBadHashWrapper()
: d_hash()
{
}

template <class HASH>
inline
FibonacciBadHashWrapper<HASH>::FibonacciBadHashWrapper(const HASH& hash)
: d_hash(hash)
{
}

// ACCESSORS
template <class HASH>
template <class KEY>
inline
size_t FibonacciBadHashWrapper<HASH>::operator()(const KEY& key) const
{
    return static_cast<size_t>(d_hash(key) * k_FIBONACCI_HASH_MULTIPLIER);
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// 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.
// ----------------------------- END-OF-FILE ----------------------------------
