/** @file utils.cpp
 */

#include <sstream>
#include <iomanip>

#include "utils.h"

namespace ace {

/* Interface item implementation.
 */
std::string unsigned2str(size_t num) {
	std::stringstream ss(std::ios::in | std::ios::out);
	ss << num;
	return ss.str();
}

/* Interface item implementation.
 */
std::string double2str(double num, unsigned precision) {
	std::stringstream ss(std::ios::in | std::ios::out);
	ss << std::setprecision(static_cast<std::streamsize>(precision));
	ss << num;
	return ss.str();
}

#ifdef _DEPENDENCY_MODE

/** This helper function gets all possible ways to count `sum` as sum of `addends` addends.
 *	@param sum Sum to be reached.
 *	@param addends_count Number of addends to consider.
 *	@param results Container to store the results (passed by reference for effectivity).
 *	@return Size of `results` at the beginning of function execution.
 */
size_t _get_decompositions(size_t sum, size_t addends_count, sum_decompositions_t& results) {
	// Remember the number of already stored results,
	// to let (possible) superior call of itself know the index from which to alter results.
	size_t started_at = results.size();
	
	if ( addends_count == 0 ) {
		// No possibility to create new sum.
		return started_at;
	}
	if ( (addends_count == 1) || (sum == 0) ) {
		// Only one possibility to create new sum.
		std::vector<size_t> new_one;
		if ( addends_count == 1 ) {
			// Last addend has to be equal to sum.
			new_one.push_back(sum);
		} else {
			// All remaining addends should be equal to zero.
			for ( size_t i = 1; i <= addends_count; ++i ) {
				new_one.push_back(0);
			}
		}
		// Store result.
		results.push_back(new_one);
		return started_at;
	}
	// To store index of last accessed array.
	size_t start_at = 0;
	//
	for ( size_t i = 0; i <= sum; ++i ) {
		// The call to get_decompositions() returns index of first_item in `results`,
		// which has been altered by the function execution. It's very important to recent call,
		// because we have to start altering results at exactly the same index.
		start_at = _get_decompositions(sum - i, addends_count - 1, results);
		// Alter only the items extended by last recursive call to get_decompositions():
		for ( ; start_at < results.size(); ++start_at ) {
			results[start_at].push_back(i);
		}
	}
	return started_at;
}

SumDecompositions::~SumDecompositions(void) {
	// Delete all tables created for overflowed-calls.
	for ( overflow_sum_decompositions_mapping_t::const_iterator iter = _overflow.begin(); iter != _overflow.end(); ++iter ) {
		delete iter->second;
	}
	for ( size_t i = 0; i < _n*_m; ++i ) {
		// Delete all tables.
		delete _tables[i];
	}
	// Delete the index table itself.
	delete[] _tables;
}


void SumDecompositions::_init(void) {
	for ( size_t i = 0; i < _n; ++i ) {
		for ( size_t j = 0; j < _m; ++j ) {
			_tables[i*_m + j] = new sum_decompositions_t();
			// Note the increased parameters! Table indexes starts at 0 for both values,
			// but the tables are created for values = index + 1.
			_get_decompositions(i+1, j+1, *_tables[i*_m + j]);
		}
	}
}


const sum_decompositions_t * SumDecompositions::get_decompositions_table(size_t sum, size_t addends) {
	// Invalid argument(s)!
	if ( (sum == 0) || (addends == 0) ) {
		return NULL;
	}
	// Ok! Return pointer to desired table, if table has been already evaluated.
	if ( (sum <= _n) && (addends <= _m) ) {
		return _tables[(sum-1)*_m + (addends-1)];
	}
	
	// TODO: Else we have to count new table.
	size_t overflow_key = sum*1000 + addends;
	overflow_sum_decompositions_mapping_t::const_iterator iter = _overflow.find(overflow_key);
	if ( iter != _overflow.end() ) {
		return iter->second;
	} else {
		sum_decompositions_t *sd = new sum_decompositions_t();
		_get_decompositions(sum, addends, *sd);
		_overflow.insert(overflow_sum_decompositions_mapping_t::value_type(overflow_key, sd));
		return sd;
	}
}

#else

bools_table_t bools_table(size_t ones, size_t length) {
	bools_table_t table;
	size_t bound = 1 << length;
	for ( size_t i = 0; i < bound; ++i ) {
		if ( bits_in(i) == ones ) {
			std::vector<bool> bools;
			for ( size_t j = 0; j < length; ++j ) {
				bools.push_back(ith_bit(i, j));
			}
			table.push_back(bools);
		}
	}
	return table;
}

#endif

} // namespace ace
