/* Copyright (c) V-Nova International Limited 2022-2026. All rights reserved.
 * This software is licensed under the BSD-3-Clause-Clear License by V-Nova Limited.
 * No patent licenses are granted under this license. For enquiries about patent licenses,
 * please contact legal@v-nova.com.
 * The LCEVCdec software is a stand-alone project and is NOT A CONTRIBUTION to any other project.
 * If the software is incorporated into another project, THE TERMS OF THE BSD-3-CLAUSE-CLEAR LICENSE
 * AND THE ADDITIONAL LICENSING INFORMATION CONTAINED IN THIS FILE MUST BE MAINTAINED, AND THE
 * SOFTWARE DOES NOT AND MUST NOT ADOPT THE LICENSE OF THE INCORPORATING PROJECT. However, the
 * software may be incorporated into a project under a compatible license provided the requirements
 * of the BSD-3-Clause-Clear license are respected, and V-Nova Limited remains
 * licensor of the software ONLY UNDER the BSD-3-Clause-Clear license (not the compatible license).
 * ANY ONWARD DISTRIBUTION, WHETHER STAND-ALONE OR AS PART OF ANY OTHER PROJECT, REMAINS SUBJECT TO
 * THE EXCLUSION OF PATENT LICENSES PROVISION OF THE BSD-3-CLAUSE-CLEAR LICENSE. */

#include <LCEVC/common/clock.h>
#include <LCEVC/common/random.h>
//

/*------------------------------------------------------------------------------*/

static inline uint64_t splitMix64Next(uint64_t* value)
{
    uint64_t x = (*value += 0x9e3779b97f4a7c15);
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
    return x ^ (x >> 31);
}

static inline uint32_t rotl(const uint32_t x, int amount)
{
    return (x << amount) | (x >> (32 - amount));
}

/*------------------------------------------------------------------------------*/

void ldcRandomInitialize(LdcRandom* random, uint64_t seed)
{
    if (seed == 0) {
        seed = ldcClockWallSeconds();
    }

    /* Prepare initial state based upon seed. */
    uint64_t x = splitMix64Next(&seed);
    random->state[0] = (uint32_t)x;
    random->state[1] = (uint32_t)(x >> 32);
    x = splitMix64Next(&seed);
    random->state[2] = (uint32_t)x;
    random->state[3] = (uint32_t)(x >> 32);
}

uint32_t ldcRandomValue(LdcRandom* random)
{
    const uint32_t result = random->state[0] + random->state[3];
    const uint32_t tmp = random->state[1] << 9;

    random->state[2] ^= random->state[0];
    random->state[3] ^= random->state[1];
    random->state[1] ^= random->state[2];
    random->state[0] ^= random->state[3];
    random->state[2] ^= tmp;
    random->state[3] = rotl(random->state[3], 11);

    return result;
}

/*------------------------------------------------------------------------------*/
