/** @file config.h
 *
 *	Header file with declarations and definitions of program configuration
 *	constants, basic defines and typedefs used throughout the program.
 *
 *	Note: Almost every compilation unit includes this file, so any change
 *	within file leads to recompilation of whole program.
 *
 *	(C) Ceslav Przywara 2008, MFF UK Prague
 *
 */

/** @namespace ace
 *	
 *	All modules within the program are declared in namespace ace in order to
 *	not pollute global namespace. Some modules declares their own namespaces.
 */


// TODO: Consider putting implementation constants (such as bits_per_char) somewhere else.

#ifndef _CONFIG_H
#define	_CONFIG_H

#include <cstddef>

// "32-bit or 64-bit?", that is the question, my friend.
#if defined(_M_X64) || defined(_M_IA64) || defined(__x86_64__) || defined(__IA64__)
// _M_X64, _M_IA64 - Visual Studio
// __x86_64__, __IA64__ - GCC
/** Run in 64-bit mode.
 */
#define _64_BIT

#elif defined(_M_IX86) || defined(__i386__)
// _M_IX86 - Visual Studio
// __i386__ - GCC
/** Run in 32-bit mode.
 */
#define _32_BIT
#else
#error "CPU word size not detected!"
#endif

// Surface or dependency mode?
#if !defined(_DEPENDENCY_MODE) && !defined(_SURFACE_MODE)
#define _DEPENDENCY_MODE // Default mode.
#elif defined(_DEPENDENCY_MODE) && defined(_SURFACE_MODE)
#undef _SURFACE_MODE // Only one mode allowed.
#endif

//// CONSTANTS

namespace ace {

/** All program constants happily live together in this nicenamed namespace.
 */
namespace constants {

/** Max grammity defines maximum allowed value of N.
 */
extern const size_t max_grammity; // If you change this, you should probably change typedef for ngram_type_t
// and static-initialized tables used to hashing (see ngrams.h and ngrams.cpp)!

/** Bits per char.
 */
extern const size_t bits_per_char;
/** Maximum allowed string length is used as chars pool block size
 *	(block size is +1, to have place for trailing NULL sign).
 */
const size_t max_string_length = 65535; // Not declared as extern - must be visible for other compilation units.
/** Maximum allowed collocation window size.
 */
extern const size_t max_collocation_window_size;
/** Number of N-grams to hold in single memory pool block.
 */
extern const size_t ngram_within_block;
/** Default output statistics precision.
 */
extern const size_t default_precision;
/** Null char.
 */
const char null_char = '\0';
/** End-of-line.
 */
const char eol = '\n';
/** Max sentence length.
 */
extern const size_t max_sentence_length;
/** Declaration of character used as indicator of significant positions within
 *	(morphologic) tag masks.
 */
extern const char significant_position_mark;
/** Declaration of character used as indicator of insignificant positions
 *	within (morphologic) tag masks.
 */
extern const char insignificant_position_mark;
/** Declaration of (inclusive) upper bound for sentence radius size
 *	(see context.h for more info).
 */
extern const size_t sentences_radius_max_size;
/** Declaration of (inclusive) upper bound for words radius size
 *	(see context.h for more info).
 */
extern const size_t words_radius_max_size;

} // namespace constants


//// TYPEDEFs

/** This typedef determines max_sentence_length constant definition
 *	(maximum allowed value of this type is max sentence length).
 */
typedef unsigned short word_order_t;
/** This type must be capable of holding number of all different strings which
 *	are stored in certain store.
 */
typedef unsigned int string_index_t;
/** Type used for representation of N value and also for indexing of N-gram
 *	members (so must be capable of holding constants::max_grammity value).
 */
typedef unsigned char ngram_size_t;
/** This type must be capable of holding number of all different dependency
 *	types which may occurr within program run.
 */
typedef unsigned char dependency_index_t;
/** This type must be capable of holding number of all different tags which may
 *	occurr within program run.
 */
typedef unsigned char tag_index_t;
/** This type must be capable of holding |max_grammity| bits (not the value!)
 */
typedef unsigned char ngram_type_t;

/** Class holds lemma and tag indices together.
 *	It's quite likely that when processing large text corpora,
 *	the default schema (24 bits for lemma, 8 bits for tag)
 *	will require change - here's the only place that needs to be altered.
 */
class TaggedLemma {
	/** Lemma and tag are kept on the same address
	 *	Lemma resides in 24 LSB bits.
	 *	Tag ressides in: 8 MSB bits.
	 */
	union {
		string_index_t _lemma;
		tag_index_t _bits[sizeof(string_index_t) / sizeof(tag_index_t)];
	};
	/**
	 */
	static const size_t _last_bit_index;
	/**
	 */
	static const string_index_t _lemma_bits_mask;
public:
	/** @param lemma Lemma index.
	 *	@param tag Tag index.
	 */
	TaggedLemma(string_index_t lemma, tag_index_t tag): _lemma(lemma) {
		_bits[_last_bit_index] = tag;
	}
	/** Lemma getter.
	 */
	inline string_index_t lemma(void) const { return _lemma & _lemma_bits_mask; }
	/** Tag getter.
	 */
	inline tag_index_t tag(void) const { return _bits[_last_bit_index]; }
};

/** Type holding context window settings.
 */
struct ContextWindow {
	/** Context window mode - can be one of SENTENCE_MODE or WINDOW_MODE.
	 */
	enum Mode {
		SENTENCE_MODE,
		WORD_MODE
	} mode;
	/** Context window radius.
	 */
	size_t radius;
	/** Constructor inits struct with given values.
	 *	@param m Context window mode (default is SENTENCE_MODE).
	 *	@param r Context window radius (default is 0 - valid only in SENTENCE_MODE!).
	 */
	ContextWindow(Mode m = SENTENCE_MODE, size_t r = 0): mode(m), radius(r) {}
	/** @return True, if settings are valid.
	 */
	inline bool check(void) { 
		return ((mode == SENTENCE_MODE) && (radius <= constants::sentences_radius_max_size)) || ((mode == WORD_MODE) && (1 <= radius) && (radius <= constants::words_radius_max_size));
	}
};

/** Lower bounds for evaluated N-grams. If N-gram doesn't pass these values,
 *	it's discarded (and not even evaluated).
 */
struct Sieves {
	/** Lower bound for expected frequency (inclusive).
	 */
	double expected_frequency;
	/** Lower bound for observed frequency (inclusive).
	 */
	size_t frequency;
	/** Default constructor sets both bounds to 0.
	 */
	Sieves(void): expected_frequency(0.0), frequency(0) {}
};

/** Thresholds for various statistical tests evaluations.
 */
struct Thresholds {
	/** Pearson's chi square test threshold.
	 */
	double chi_square_test;
	/** Log likelihood ratio threshold.
	 */
	double log_likelihood_ratio;
	/** Mutual information threshold.
	 */
	double mutual_information;
	/** Pearson's coeficient threshold.
	 */
	double pearsons_coefficient;
	/** Student's t-test threshold.
	 */
	double t_test;
	/** Z score threshold.
	 */
	double z_score;
	/** Default constructor sets all thresholds to 0.
	 */
	Thresholds(void): chi_square_test(0.0), log_likelihood_ratio(0.0), mutual_information(0.0), pearsons_coefficient(0.0), t_test(0.0), z_score(0.0) {}
};


//// SETTINGS

/** Namespace holds (global) program settings and functions checking their corectness.
 */
namespace settings {
	/**
	 */
	extern size_t bucket_size;
#ifdef _SURFACE_MODE
	/**
	 */
	extern size_t collocation_window_size;
#endif
	/** Context window settings.
	 */
	extern ContextWindow context_window;
	/** Context filters filename.
	 */
	extern const char *context_filter_file;
	/** Context output filename.
	 */
	extern const char *context_output_file;
	/** Context morphologic tag mask.
	 *	Note: it's only used for determination of words in context equality.
	 *	It's NOT used by context filter (morphologic tag is used).
	 */
//	TODO: Remove?
//	extern const char *context_tag_mask;
	/**
	 */
	extern size_t crop_ratio;
	/** Directory with data files.
	 */
	extern const char *dir;
	/** File with morphologic filter.
	 */
	extern const char *morphologic_filter_file;
	/** File for morphologic filter stats.
	 */
	extern const char *morphologic_filter_stats_file;
	/** Input filename.
	 */
	extern const char *input_file;
	/** N-gram size.
	 */
	extern ngram_size_t n;
	/** Output filename.
	 */
	extern const char *output_file;
	/** Output statistics precision.
	 */
	extern size_t precision;
	/** When compiled in thrifty mode, do sorting before N-grams output processing?
	 */
	extern bool sort;
	/** Stats output filename.
	 */
	extern const char *stats_file;
	/** To sieve the resulting statistics by given thresholds.
	 */
	extern Sieves sieves;
	/** To sieve the resulting statistics by given thresholds.
	 */
	extern Thresholds thresholds;
	/** If true, all non-zero thresholds must be passed,
	 *	otherwise passing one of them is enough.
	 */
	extern bool all_thresholds_together;
	/** Tag mask determines significant positions of morphological tag.
	 */
	extern const char *morphologic_tag_mask;
	/** Be verbose?
	 */
	extern bool verbose;

	/** @return True, if mask contains only allowed marks.
	 */
	bool check_mask(const char *mask);
	/**	@return True, if context has to be gathered.
	 */
	bool context_tracing_on(void);
	/** @return True, if all given settings are set correctly (both mandatory and optional).
	 */
	bool correct(void);
	/** @return True, if all mandatory settings are given.
	 */
	bool ready(void);

} // namespace settings

} // namespace ace

#endif
