#ifndef straighten_core_h__
#define straighten_core_h__

//comment this line out if you are using mingw64
#define _STRAIGHTEN_USING_LINUX

#define _STRAIGHTEN_COMPILE_TIME_LIMIT 4

#ifdef _STRAIGHTEN_USING_LINUX
#define _GNU_SOURCE
#else
#define _STRAIGHTEN_USE_QSORT_S
#endif

#define _STRAIGHTEN_NUM_THREADS 4
#define _STRAIGHTEN_CPU_CACHE_LINE_SIZE 512
#define _STRAIGHTEN_MAX_DICTIONARY 50000000ll
#define _STRAIGHTEN_PERTHREAD_CACHE_ENTRIES 10000000
#define _STRAIGHTEN_PERTHREAD_CACHE 150000000
#define _STRAIGHTEN_PERTHREAD_CACHE_INCREMENT 150000000
#define _STRAIGHTEN_PERTHREAD_CACHE_MAX 600000000

#define ANSI_COLOR_RED     "\x1b[31m"
#define ANSI_COLOR_GREEN   "\x1b[32m"
#define ANSI_COLOR_YELLOW  "\x1b[33m"
#define ANSI_COLOR_BLUE    "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN    "\x1b[36m"
#define ANSI_COLOR_RESET   "\x1b[0m"

#define	FORCE_INLINE inline __attribute__((always_inline))

#define straighten_max_macro(x, y) (((x) > (y)) ? (x) : (y))
#define straighten_min_macro(x, y) (((x) < (y)) ? (x) : (y))

#define straighten_likely(x)      __builtin_expect(!!(x), 1)
#define straighten_unlikely(x)    __builtin_expect(!!(x), 0)

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>
#include <gmp.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <math.h>
#include <ctype.h>
#include <stdarg.h>
#include <inttypes.h>
#include "straighten_log.h"

extern int STRAIGHTEN_LOGGING_INDENT;

#ifdef _STRAIGHTEN_USING_LINUX
#define straighten_int64mul_overflow(a,b,c) \
	if(straighten_unlikely(__builtin_smull_overflow(a,b,c))) { \
		printf("Fatal Error: Int64 multiplication overflow.\n"); \
		exit(EXIT_FAILURE); \
	}

#define straighten_int64add_overflow(a,b,c) \
	if(straighten_unlikely(__builtin_saddl_overflow(a,b,c))) { \
		printf("Fatal Error: Int64 addition overflow.\n"); \
		exit(EXIT_FAILURE); \
	}
#else
#define straighten_int64mul_overflow(a,b,c) \
	if(straighten_unlikely(__builtin_smulll_overflow(a,b,c))) { \
		printf("Fatal Error: Int64 multiplication overflow.\n"); \
		exit(EXIT_FAILURE); \
	}

#define straighten_int64add_overflow(a,b,c) \
	if(straighten_unlikely(__builtin_saddll_overflow(a,b,c))) { \
		printf("Fatal Error: Int64 addition overflow.\n"); \
		exit(EXIT_FAILURE); \
	}
#endif

#define straighten_timing_macro(a, s) \
    do { \
        struct timespec start, finish; \
        double elapsed; \
        clock_gettime(CLOCK_MONOTONIC, &start); \
        a; \
        clock_gettime(CLOCK_MONOTONIC, &finish); \
        elapsed = (finish.tv_sec - start.tv_sec); \
		elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0; \
		straighten_log(STRAIGHTEN_VINFO, "%s took: %f seconds", s, elapsed); \
    } while (0)

#define s_printf(...) \
    do { \
    	fprintf(stdout, "%*s", STRAIGHTEN_LOGGING_INDENT, ""); \
    	fprintf(stdout, __VA_ARGS__); \
    	fflush(stdout); \
    } while (0)

struct tableau {
    uint8_t * entries;
    int64_t coefficient;
};

struct packed_tableau {
    uint8_t * entries;
};

struct sparse_cache_CSR_32 {
    int16_t * values;
    uint16_t * index;
    uint32_t * rows_ptr;
    uint32_t rows_ptr_current;
    uint32_t rows_ptr_max_length;
    uint32_t values_length;
    int filled_flag;
};

struct sparse_CSR_32 {
    int32_t * values;
    uint32_t * columns_index;
    uint32_t * rows_ptr;
    uint32_t rows_ptr_length;
    uint32_t values_length;
    uint32_t number_rows;
    uint32_t number_columns;
};

struct sparse_CSR_8 {
    int8_t * values;
    uint32_t * columns_index;
    uint32_t * rows_ptr;
    uint32_t rows_ptr_length;
    uint32_t values_length;
    uint32_t number_rows;
    uint32_t number_columns;
};

struct columns_iterator {
	uint32_t * unique_column_start;
	uint32_t * unique_column_number;
	uint32_t length;
};

struct shape_data_c {
	uint32_t num_boxes;
	uint32_t num_packed_boxes;
	uint32_t * shape;
	uint32_t shape_length; 
	uint32_t shape_index;

	uint32_t * conjugate;	

	struct columns_iterator * unique_columns;

	int32_t box_start_single_rows;  
	int32_t col_start_single_rows;

	uint32_t * col_row_to_box;
	uint32_t * row_col_to_box;
	uint32_t * box_to_col;
	uint32_t * box_to_row;
	uint32_t * roworder_to_box;

	char * shape_string;
};

struct sstd_data_c {
	uint8_t max_filling_value;
	uint8_t * content;
	struct tableau * all_sstd_tableau;
	uint32_t num_sstd_tableau;

	uint8_t * permutations;
	uint64_t * permutation_counts;

	struct packed_tableau * all_dictionary_tableau;
	uint32_t num_dictionary_tableau;
	struct pt_hash_table * dictionary_hash;
	struct sparse_CSR_32 * D_basis_matrix;
	struct sparse_CSR_8 * R_coeff_skip;
	
	uint8_t * sstd_row_content;
	uint64_t * sstd_column_factorial;
	
	uint32_t lower_bound_sstd;
	uint32_t max_shared_12;
	
	uint8_t * straighten_cache_index_one;
	uint32_t * straighten_cache_index_two;
	struct sparse_cache_CSR_32 * straighten_cache;
};

struct sstd_data_c_options {
	uint8_t set_sstd;
	uint8_t set_Dbasis;
	uint8_t set_rowcontent;
	uint8_t set_dictionary;
	uint8_t set_dictionary_hash;
	uint8_t set_cache;
	uint8_t set_sstd_column_factorial;
	uint8_t gen_tableau_threaded;
	uint8_t do_not_sort_sstd;
	uint8_t dictionary_hash_copy_data;
	uint8_t use_lower_bound;
	double lower_bound_fraction;
};


struct straighten_array_temp {
	int64_t * temp_straighten_result;
	uint8_t * standardization_work;
	struct packed_tableau * tableau_work;
	uint8_t * straightening_content_work;
};

#endif //straighten_core_h__
