#ifndef straighten_h__
#define straighten_h__

#include "straighten_core.h"
#include "straighten_log.h"
#include "search_sort_util.h"
#include "straighten_hash_table.h"


FORCE_INLINE void column_straighten(struct tableau * tableau, struct shape_data_c * s_data) {
	int32_t sign = 0;
	for(int col = 0; col < s_data[0].col_start_single_rows; col++) {
		sign = sign + signed_sort_fast(tableau[0].entries, s_data[0].row_col_to_box[col], s_data[0].conjugate[col]);
	}
	//branchless multiply by 1 if sign even multiply by -1 if sign odd
	tableau[0].coefficient = tableau[0].coefficient * (1 - ((sign & 1)<<1));
}

FORCE_INLINE void dictionary_straighten(struct tableau * tableau, struct shape_data_c * s_data) {
	column_straighten(tableau, s_data);

	for(int u_col = 0; u_col < s_data[0].unique_columns[0].length; u_col++) {
		sort_fast_array(tableau[0].entries + s_data[0].row_col_to_box[s_data[0].unique_columns[0].unique_column_start[u_col]], s_data[0].conjugate[s_data[0].unique_columns[0].unique_column_start[u_col]], s_data[0].unique_columns[0].unique_column_number[u_col]);
	}
 
}

FORCE_INLINE int get_standardization_of_colstraightened(struct tableau * tableau_arr, struct sstd_data_c * sstd_data, struct shape_data_c * s_data, uint8_t * work) {
    for(int b = 0; b < s_data[0].num_boxes; b++) {
    	work[b] = tableau_arr[0].entries[s_data[0].roworder_to_box[b]];
    }
    int pos = 0;
    for(int row = 0; row < s_data[0].conjugate[0]; row++) {
    	 sort_fast(work, pos, s_data[0].shape[row]);
    	 pos += s_data[0].shape[row];
    }

    return straighten_bsearch_r(work, sstd_data[0].all_sstd_tableau, sstd_data[0].num_sstd_tableau, sizeof(struct tableau), sstd_tableau_rowword_single_cmp_rev_r, s_data);
}

FORCE_INLINE int check_in_cache(int32_t dictionary_tableau_index, struct sstd_data_c * sstd_data) {
    return (dictionary_tableau_index >= 0 && sstd_data[0].straighten_cache_index_one[dictionary_tableau_index] != UINT8_MAX);
}

FORCE_INLINE void read_from_cache_int64(int64_t * straighten_result, int64_t coefficient, int32_t dictionary_tableau_index, struct sstd_data_c * sstd_data) {
	uint8_t thread_cache = sstd_data[0].straighten_cache_index_one[dictionary_tableau_index];
    uint32_t cache_index = sstd_data[0].straighten_cache_index_two[dictionary_tableau_index];
    for(int c_index = sstd_data[0].straighten_cache[thread_cache].rows_ptr[cache_index]; c_index < sstd_data[0].straighten_cache[thread_cache].rows_ptr[cache_index+1]; c_index++) {
    	straighten_result[sstd_data[0].straighten_cache[thread_cache].index[c_index]] += (coefficient * (int64_t)sstd_data[0].straighten_cache[thread_cache].values[c_index]);
	}
}

void clear_straightening_cache(struct sstd_data_c * sstd_data, int32_t zero);

extern void tableau_coeff_simplify(struct tableau * tableau_arr, uint32_t num_tableau, struct shape_data_c * s_data);

extern int64_t __c_compute_rearrangement_coeff(const uint8_t *restrict all_monomials, uint8_t *restrict t_data_c, const struct sstd_data_c * sstd_data, const struct shape_data_c *restrict s_data, const int col, const int sign);

FORCE_INLINE int64_t c_compute_rearrangement_coeff(const uint8_t *restrict all_monomials, const uint32_t sstd_index, uint8_t *restrict t_data_c, const struct sstd_data_c * sstd_data, const struct shape_data_c * s_data) {
	memcpy(t_data_c, sstd_data[0].sstd_row_content + (sstd_index * s_data[0].conjugate[0] * sstd_data[0].max_filling_value), s_data[0].conjugate[0] * sstd_data[0].max_filling_value * sizeof(uint8_t));
	
	for (int box=s_data[0].num_boxes-1; box >= s_data[0].box_start_single_rows; box--) {
		if (t_data_c[all_monomials[box]-1] != 0) {
	        t_data_c[all_monomials[box]-1]--;
	    }  
	    else {
	    	return 0;
	    }
	}

	//for single row shapes
	if(s_data[0].box_start_single_rows == 0) {return 1;}

	return __c_compute_rearrangement_coeff(all_monomials, t_data_c, sstd_data, s_data, s_data[0].shape[1]-1, 0);
}
extern void construct_shape_data_c(struct shape_data_c * s_data, uint32_t * shape, uint32_t shape_length);

extern void destruct_shape_data_c(struct shape_data_c * s_data);

extern void construct_sstd_data_c(struct sstd_data_c * sstd_data, uint8_t * content, uint8_t max_filling_value, struct shape_data_c * s_data, struct sstd_data_c_options * options);

extern void destruct_sstd_data_c(struct sstd_data_c * sstd_data);

void set_sstd_column_factorial(struct sstd_data_c * sstd_data, struct shape_data_c * s_data);

void set_sstd_column_repeat_data(struct sstd_data_c * sstd_data, struct shape_data_c * s_data, uint32_t sstd_index, uint32_t ** column_repeats);

void set_sstd_column_overlap_data(struct sstd_data_c * sstd_data, struct shape_data_c * s_data, uint32_t sstd_index, uint32_t ** column_overlaps);

void set_sstd_row_contents_uid(struct sstd_data_c * sstd_data, struct shape_data_c * s_data, uint32_t sstd_index, uint8_t ** row_contents_uid);

struct tableau * random_sstd(struct sstd_data_c * sstd_data);

struct packed_tableau * random_dictionary_tableau(struct sstd_data_c * sstd_data);

void straighten_sstd_basis_single_int64(struct tableau * straighten, struct sstd_data_c * sstd_data, struct shape_data_c * s_data, int64_t * straighten_result);   

void straighten_sstd_basis_single_bigint(struct tableau * straighten, struct sstd_data_c * sstd_data, struct shape_data_c * s_data, mpz_t * straighten_result); 

void straighten_array_to_sstd_basis_threaded_cached_int64(struct tableau * straighten, uint32_t num_straighten, struct sstd_data_c * sstd_data, struct shape_data_c * s_data, int64_t * straighten_result, int simple_straightened, int sstd_basis);



#endif  // straighten_h__