// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: cache.cpp 815 2011-09-10 12:57:27Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "cache.h"
#include "system/state.h"

#include <algorithm>

using namespace std;

const int32_t Cache::NOT_FOUND = -1;

////////////////////////////////////////////////////////////////////

static const int32_t nrPrimes = 28;
static const uint32_t primes[nrPrimes] = {
    53ul,         97ul,         193ul,       389ul,       769ul,
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul, 
    1610612741ul, 3221225473ul, 4294967291ul // == 2^32 -5
};

inline static uint32_t nextPrime(uint32_t n) {
    const uint32_t* first = primes;
    const uint32_t* last = primes + nrPrimes;
    const uint32_t* pos = lower_bound(first, last, n);
    return (pos == last) ? *(last - 1) : *pos;
}

BitstateCache::BitstateCache(uint32_t size) :
    Cache(nextPrime(size)),
    cachedStates(nextPrime(size), NOT_FOUND)
{
    assert(Cache::size == cachedStates.size());
}

int32_t& BitstateCache::lookup(const State* state) {
    return cachedStates[state->hashKey() % size];
}

////////////////////////////////////////////////////////////////////

DiscretePartCache::DiscretePartCache(uint32_t size) :
    Cache(size),
    cachedStates(size)
{}

bool DiscretePartCache::isEqual(const State* lhs, const State* rhs) const {
    return lhs->discEqual(rhs);
}

int32_t& DiscretePartCache::lookup(const State* state) {
    pair<cache_t::iterator, bool> pos = cachedStates.insert(make_pair(state->hashKey(), list_t()));
    list_t& l = pos.first->second;
    
    if (!pos.second) { // found states with same hash key
	for (list_t::iterator it = l.begin(); it != l.end(); ++it) {
	    if (isEqual(it->first, state)) {
		return it->second;
	    }
	}
    }
    // there was no state with the same discrete part
    l.push_front(make_pair(state, NOT_FOUND));	
    return l.front().second;
}

////////////////////////////////////////////////////////////////////

WholeStateCache::WholeStateCache(uint32_t size) :
    DiscretePartCache(size)
{}

bool WholeStateCache::isEqual(const State* lhs, const State* rhs) const {
    if (lhs->discEqual(rhs)) {
	switch (lhs->zone().relation(rhs->zone())) {
	case base_SUPERSET:  // lhs > rhs
	case base_EQUAL:     // lhs == rhs
	    return true;
	case base_SUBSET:    // lhs < rhs
	    // TODO: is it safe to say true here????
	case base_DIFFERENT: // lhs & rhs == 0
	    return false;	    
	}
    }
    return false;
}
