// 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
                      // =============================

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

    // DATA
    HASH d_hash;  // hash functor

  public:
    // PUBLIC CLASS DATA
    static const bsls::Types::Uint64 k_FIBONACCI_HASH_MULTIPLIER
                                                     = 11400714819323198485ull;
        // 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.

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

    explicit FibonacciBadHashWrapper(const HASH& hash);
        // Create a 'FibonacciBadHashWrapper' having the specified '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
    template <class KEY>
    size_t operator()(const KEY& key) const;
        // 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'.
};

// ============================================================================
//                             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 ----------------------------------