/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <cstddef>

namespace lean {

void mix(unsigned & a, unsigned & b, unsigned & c) {
    a -= b; a -= c; a ^= (c >> 13);
    b -= c; b -= a; b ^= (a << 8);
    c -= a; c -= b; c ^= (b >> 13);
    a -= b; a -= c; a ^= (c >> 12);
    b -= c; b -= a; b ^= (a << 16);
    c -= a; c -= b; c ^= (b >> 5);
    a -= b; a -= c; a ^= (c >> 3);
    b -= c; b -= a; b ^= (a << 10);
    c -= a; c -= b; c ^= (b >> 15);
}

// Bob Jenkin's hash function.
// http://burtleburtle.net/bob/hash/doobs.html
unsigned hash_str(size_t length, unsigned char const * str, unsigned init_value) {
    unsigned a, b, c;
    size_t len;

    /* Set up the internal state */
    len = length;
    a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
    c = init_value;      /* the previous hash value */

    /*---------------------------------------- handle most of the key */
    while (len >= 12) {
        a += reinterpret_cast<unsigned const *>(str)[0];
        b += reinterpret_cast<unsigned const *>(str)[1];
        c += reinterpret_cast<unsigned const *>(str)[2];
        mix(a, b, c);
        str += 12; len -= 12;
    }

    /*------------------------------------- handle the last 11 bytes */
    c += length;
    switch (len) {
        /* all the case statements fall through */
    case 11:  c+=((unsigned)str[10] << 24);  /* fall-thru */
    case 10:  c+=((unsigned)str[9] << 16);   /* fall-thru */
    case 9 :  c+=((unsigned)str[8] << 8);    /* fall-thru */
        /* the first byte of c is reserved for the length */
    case 8 :  b+=((unsigned)str[7] << 24);   /* fall-thru */
    case 7 :  b+=((unsigned)str[6] << 16);   /* fall-thru */
    case 6 :  b+=((unsigned)str[5] << 8);    /* fall-thru */
    case 5 :  b+=(unsigned)str[4];           /* fall-thru */
    case 4 :  a+=((unsigned)str[3] << 24);   /* fall-thru */
    case 3 :  a+=((unsigned)str[2] << 16);   /* fall-thru */
    case 2 :  a+=((unsigned)str[1] << 8);    /* fall-thru */
    case 1 :  a+=(unsigned)str[0];
        /* case 0: nothing left to add */
    }
    mix(a, b, c);
    /*-------------------------------------------- report the result */
    return c;
}

}
