#include "straighten.h"
#include "plethysm.h"

int main(void)
{
    srand((unsigned int)time(NULL));
	setbuf(stdout, NULL);

    // log level 0 is very verbose, log level 1 is verbose, log level 2 is (default) info, log level log level 3+ will only print warnings, errors and fatal errors
    straighten_set_log_level(1);

    // uncomment this if you want the message log to print debug info like line numbers, file, and function names
    //straighten_set_debug(1);

    // there are three different display modes for the messages: 0 basic, 1 (default) minor color coded, 2 full color coded
    //straighten_set_log_display(0);




    /* Example 1: Straightening - a small case */
    /*
    struct shape_data_c s_data = {};
    struct sstd_data_c sstd_data = {};
    struct sstd_data_c_options options = {};

    // set the shape
    uint32_t shape_length = 3;
    uint32_t shape[3] = {2,2,1};  
    
    // set the content
    uint8_t max_filling_value = 7;
    uint8_t content[7] = {1,1,1,1,0,0,1};

    // set the options to create the semistandard tableau and D-basis, this is the minimum required for straightening.
    options.set_sstd = 1;
    options.set_Dbasis = 1;

    // this constructs the shape_data_c struct that is required for all operations
    construct_shape_data_c(&s_data, shape, shape_length);

    // this constructs the sstd_data_c struct that contains the array of semistandard tableau and the D-basis
    construct_sstd_data_c(&sstd_data, content, max_filling_value, &s_data, &options);

    //  the tableau we want to straighten
    struct tableau * filling_to_straighten = (struct tableau *)calloc(1, sizeof(struct tableau));
    filling_to_straighten[0].coefficient = 1;
    // the entries of a filling are stored in column order (top to bottom, left to right)
    uint8_t ents[5] = {1,7,4,2,3};
    filling_to_straighten[0].entries = &ents[0];

    // where we will store the results of straightening
    int64_t * straighten_result = (int64_t*) calloc(sstd_data.num_sstd_tableau, sizeof(int64_t));

    // straighten and then print the result
    straighten_sstd_basis_single_int64(filling_to_straighten, &sstd_data, &s_data, straighten_result);
    s_printf("Result:\n");
    print_straighten_result_int64_t(straighten_result, &sstd_data, &s_data, 0, 0);

    // free the data
    destruct_shape_data_c(&s_data);
    destruct_sstd_data_c(&sstd_data);
    free(filling_to_straighten);
    free(straighten_result);

    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */



    /* Example 2: Construct an array of ~44 million (!) semistandard tableau and then pick a random one.*/
    /*
    struct shape_data_c s_data = {};
    struct sstd_data_c sstd_data = {};
    struct sstd_data_c_options options = {};

    uint32_t shape_length = 3;
    uint32_t shape[3] = {12,8,3};  
    
    uint8_t max_filling_value = 23;
    uint8_t content[23] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

    // set the options to just create the semistandard tableau
    options.set_sstd = 1;
    options.gen_tableau_threaded = 1;
    // if we are straightening then the semistandard tableau array must be sorted in the row word order
    // however, if we are only interested in a random tableau then they do not need to be sorted
    options.do_not_sort_sstd = 1;

    // this constructs the shape_data_c struct that is required for all operations
    construct_shape_data_c(&s_data, shape, shape_length);

    // this constructs the sstd_data_c struct that contains the array of semistandard tableau and the D-basis
    construct_sstd_data_c(&sstd_data, content, max_filling_value, &s_data, &options);

    // print a random semistandard tableau
    s_printf("A random semistandard tableau:\n");
    print_tableau(random_sstd(&sstd_data), &s_data, 1, 1, 1, 0);

    // free the data
    destruct_shape_data_c(&s_data);
    destruct_sstd_data_c(&sstd_data);

    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */


    /* Example 3: Straightening ~200k (!) tableau with 36 boxes. Caches the results and then straightens a second time. */
    
    struct shape_data_c s_data = {};
    struct sstd_data_c sstd_data = {};
    struct sstd_data_c_options options = {};

    uint32_t shape_length = 3;
    uint32_t shape[3] = {21,8,7};  
    
    uint8_t max_filling_value = 6;
    uint8_t content[6] = {6,6,6,6,6,6};

    // to straighen and cache we need to construct all semistandard tableau, all dictionary tableau, the dictionary tableau hash table, and instantiate the cache
    options.set_sstd = 1;
    options.set_Dbasis = 1;
    options.set_dictionary = 1;
    options.set_dictionary_hash = 1;
    options.set_cache = 1;
	
	// this constructs the shape_data_c struct that is required for all operations
    construct_shape_data_c(&s_data, shape, shape_length);

    // this constructs the sstd_data_c struct that contains the array of semistandard tableau, the D-basis, and the dictionary tableau/hash
    construct_sstd_data_c(&sstd_data, content, max_filling_value, &s_data, &options);

    // the array of fillings to straighen - in this case we take the first 1 million dictionary tableau
    struct tableau * fillings_to_straighten = (struct tableau *)calloc(200000, sizeof(struct tableau));
    uint8_t * st_entries = (uint8_t*) calloc(200000 * s_data.num_boxes, sizeof(uint8_t));
    for(int tab = 0; tab < 200000; tab++) {
        fillings_to_straighten[tab].coefficient = 1;
        fillings_to_straighten[tab].entries = st_entries;
        st_entries += s_data.num_boxes;
        for(int b = 0; b < s_data.num_boxes; b++) {
            fillings_to_straighten[tab].entries[b] = get_packed(sstd_data.all_dictionary_tableau[tab].entries, b);
        }
    }

    // we are going to be straightening using threads, so the result array needs to have NUM_THREADS * NUM_SSTD entries
    int64_t * straighten_result = (int64_t*) calloc(_STRAIGHTEN_NUM_THREADS * sstd_data.num_sstd_tableau, sizeof(int64_t));
    
    straighten_timing_macro(straighten_array_to_sstd_basis_threaded_cached_int64(fillings_to_straighten, 200000, &sstd_data, &s_data, straighten_result, 1, 0), "Straightening");
     
    s_printf("Hit enter to straighten again using the cache.");
    getchar();
    // zero out the results and straighten again
    memset(straighten_result, 0, _STRAIGHTEN_NUM_THREADS * sstd_data.num_sstd_tableau * sizeof(int64_t));
    straighten_timing_macro(straighten_array_to_sstd_basis_threaded_cached_int64(fillings_to_straighten, 200000, &sstd_data, &s_data, straighten_result, 1, 0), "Straightening using cached values");
    
    s_printf("Hit enter to display the results (this will print only the indices).");
    getchar();
    print_straighten_result_int64_t(straighten_result, &sstd_data, &s_data, 1, 0);

    // free the data
    destruct_shape_data_c(&s_data);
    destruct_sstd_data_c(&sstd_data);

    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    


    /* Example 4: Straightening ~500k (!) tableau with 36 boxes. This time restrict all results to those tableau whose index in row word order is >= 8940 */
    /*
    struct shape_data_c s_data = {};
    struct sstd_data_c sstd_data = {};
    struct sstd_data_c_options options = {};

    uint32_t shape_length = 3;
    uint32_t shape[3] = {21,8,7};  
    
    uint8_t max_filling_value = 6;
    uint8_t content[6] = {6,6,6,6,6,6};

    // to straighen and cache we need to construct all semistandard tableau, all dictionary tableau, the dictionary tableau hash table, and instantiate the cache
    options.set_sstd = 1;
    options.set_Dbasis = 1;
    options.set_dictionary = 1;
    options.set_dictionary_hash = 1;
    options.set_cache = 1;
    
    // this constructs the shape_data_c struct that is required for all operations
    construct_shape_data_c(&s_data, shape, shape_length);

    // this constructs the sstd_data_c struct that contains the array of semistandard tableau, the D-basis, and the dictionary tableau/hash
    construct_sstd_data_c(&sstd_data, content, max_filling_value, &s_data, &options);

    // THE ONLY CHANGE from previous example
    sstd_data.lower_bound_sstd = 7000;

    // the array of fillings to straighen - in this case we take the first 1 million dictionary tableau
    struct tableau * fillings_to_straighten = (struct tableau *)calloc(500000, sizeof(struct tableau));
    uint8_t * st_entries = (uint8_t*) calloc(500000 * s_data.num_boxes, sizeof(uint8_t));
    for(int tab = 0; tab < 500000; tab++) {
        fillings_to_straighten[tab].coefficient = 1;
        fillings_to_straighten[tab].entries = st_entries;
        st_entries += s_data.num_boxes;
        for(int b = 0; b < s_data.num_boxes; b++) {
            fillings_to_straighten[tab].entries[b] = get_packed(sstd_data.all_dictionary_tableau[tab].entries, b);
        }
    }

    // we are going to be straightening using threads, so the result array needs to have NUM_THREADS * NUM_SSTD entries
    int64_t * straighten_result = (int64_t*) calloc(_STRAIGHTEN_NUM_THREADS * sstd_data.num_sstd_tableau, sizeof(int64_t));
    
    straighten_timing_macro(straighten_array_to_sstd_basis_threaded_cached_int64(fillings_to_straighten, 500000, &sstd_data, &s_data, straighten_result, 1, 1), "Straightening");


    s_printf("Hit enter to display the results (this will print only the indices).");
    getchar();
    print_straighten_result_int64_t(straighten_result, &sstd_data, &s_data, 1, 0);

    // free the data
    destruct_shape_data_c(&s_data);
    destruct_sstd_data_c(&sstd_data);

    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */


    /* Example 5: Straightening ~500k (!) tableau with 36 boxes. Same as example 3 but straightens only in D-basis, does not change to sstd basis. */
    /*
    struct shape_data_c s_data = {};
    struct sstd_data_c sstd_data = {};
    struct sstd_data_c_options options = {};

    uint32_t shape_length = 3;
    uint32_t shape[3] = {21,8,7};  
    
    uint8_t max_filling_value = 6;
    uint8_t content[6] = {6,6,6,6,6,6};

    // to straighen and cache we need to construct all semistandard tableau, all dictionary tableau, the dictionary tableau hash table, and instantiate the cache
    options.set_sstd = 1;
    options.set_Dbasis = 1;
    options.set_dictionary = 1;
    options.set_dictionary_hash = 1;
    options.set_cache = 1;
    
    // this constructs the shape_data_c struct that is required for all operations
    construct_shape_data_c(&s_data, shape, shape_length);

    // this constructs the sstd_data_c struct that contains the array of semistandard tableau, the D-basis, and the dictionary tableau/hash
    construct_sstd_data_c(&sstd_data, content, max_filling_value, &s_data, &options);

    // the array of fillings to straighen - in this case we take the first 1 million dictionary tableau
    struct tableau * fillings_to_straighten = (struct tableau *)calloc(500000, sizeof(struct tableau));
    uint8_t * st_entries = (uint8_t*) calloc(500000 * s_data.num_boxes, sizeof(uint8_t));
    for(int tab = 0; tab < 500000; tab++) {
        fillings_to_straighten[tab].coefficient = 1;
        fillings_to_straighten[tab].entries = st_entries;
        st_entries += s_data.num_boxes;
        for(int b = 0; b < s_data.num_boxes; b++) {
            fillings_to_straighten[tab].entries[b] = get_packed(sstd_data.all_dictionary_tableau[tab].entries, b);
        }
    }

    // we are going to be straightening using threads, so the result array needs to have NUM_THREADS * NUM_SSTD entries
    int64_t * straighten_result = (int64_t*) calloc(_STRAIGHTEN_NUM_THREADS * sstd_data.num_sstd_tableau, sizeof(int64_t));
    
    // only change from example 3 is the 1=>0 in final argument of straighten_array_to_sstd_basis_threaded_cached_int64
    straighten_timing_macro(straighten_array_to_sstd_basis_threaded_cached_int64(fillings_to_straighten, 500000, &sstd_data, &s_data, straighten_result, 1, 0), "Straightening");
     
    s_printf("Hit enter to display the results (this will print only the indices).");
    getchar();
    print_straighten_result_int64_t(straighten_result, &sstd_data, &s_data, 1, 0);

    // free the data
    destruct_shape_data_c(&s_data);
    destruct_sstd_data_c(&sstd_data);

    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */


    /* Example 6: Reads in the Wildon plethysm coefficient database and computes the combinatorial basis for the HWV subspace associated to each shape. */
    // this takes approx 1min on my laptop, 6-5 takes approx 30 mins, 6-6 approx 18 hours, I imagine that 6-7 is doable but would require a server
    /*
    uint32_t outer = 4;
    uint32_t inner = 3;
    straighten_set_log_level(0);
    straighten_timing_macro(construct_isotypic_basis_all(outer, inner, 1), "Finding isotypic basis all");
    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */
    
    /* Example 7: Reads in the Wildon plethysm coefficient database and finds the largest plethysm coefficient */
    /*
    // this will store the partitions, it will be a 2d array of shape outer * #partitions
    uint32_t * partitions;
    // this will store the plethysm coefficients associated with the partitions in the previous arry
    uint32_t * plethysms;
    uiont32_t outer = 6;
    uint32_t inner = 6;
    uint32_t num_partitions = load_foulkes(outer, inner, &partitions, &plethysms);
    uint32_t max_plethysm = 0;
    // this will record the index of the first partition with the max plethysm coeff
    uint32_t max_plethysm_index = 0;
    for(int par = 0; par < num_partitions; par++) {
        if(plethysms[par] > max_plethysm) {
            max_plethysm = plethysms[par];
            max_plethysm_index = par;
        }
    }

    // get a string representation of the first partition with the max plethysm coeff
    char shape_str[128];
    int32_t s_index = sprintf(&shape_str[0], "[");
    for(int entry = 0; entry < outer; entry++) {
        if(partitions[(outer * max_plethysm_index) + entry] > 0) {
            s_index += sprintf(&shape_str[s_index], "%u,", partitions[(outer * max_plethysm_index) + entry]);
        }
    }
    s_index--; //removes the trailing comma
    s_index += sprintf(&shape_str[s_index], "]");

    s_printf("The maximum plethysm coefficient was %d and the first partition with this coefficient was %s.\n", max_plethysm, shape_str);

    free(partitions);
    free(plethysms);
    straighten_log(STRAIGHTEN_INFO, "Successfully freed memory and exiting.");
    */

    return 0;
}