// main.c
// Copyright (C) 2023 Mark Bowron

// Produce header file psi-numbers.h

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.


#include    <stdio.h>
#include    <stdlib.h>
#include    <inttypes.h>
#include    <stdbool.h>
#include    <string.h>
#include    "set_operators.h"
#include    "set_families.h"
#include    "set_relations.h"
#include    "collapses.h"


int main(int argc, const char * argv[])
{
    FILE       *input = NULL;
    uint32_t   *point_closures, set;
    uint32_t  **psi_array;
    int         j, k, l, space, spacesize, max_spacesize = 11;
    int      ***psi, **psi_equals;
    int         have_psi[71] = {0};
    KFZ       **kfz;
    KF        **kf;
    KFenum      key = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
                    20,21,22,23,24,25,26,27,28,29,30,31,32,33};
    KFenum     *kf_fam = &key;

// Allocate memory for the structs that store KFZ(A) and KF(A) for all A in X.

    kfz = (KFZ **)malloc(9 * sizeof(KFZ *));
    if (kfz==0)
    {
        printf("\nCould not allocate space for kfz.\n");
        exit(0);
    }
    kf = (KF **)malloc(9 * sizeof(KF *));
    if (kf==0)
    {
        printf("\nCould not allocate space for kf.\n");
        exit(0);
    }

// No need for cardinalities 0 and 1.

    for (spacesize = 0; spacesize < 2; spacesize++)
    {
        kfz[spacesize] = NULL;
        kf[spacesize] = NULL;
    }
    for (spacesize = 2; spacesize < 9; spacesize++)
    {
        kfz[spacesize] = CreateKFZ(spacesize);
        kf[spacesize] = CreateKF(spacesize);
    }

    psi_array = (uint32_t **)malloc(34 * sizeof(uint32_t *));
    if (psi_array==0)
    {
        printf("\nCould not allocate space for psi_array.\n");
        exit(0);
    }
    psi_equals = (int **)malloc(34 * sizeof(int *));
    if (psi_equals==0)
    {
        printf("\nCould not allocate space for psi_equals.\n");
        exit(0);
    }
    for (j=0; j<34; j++)
    {
        psi_array[j] = (uint32_t *)malloc((one<<max_spacesize) * sizeof(uint32_t));
        if (psi_array[j]==0)
        {
            printf("\nCould not allocate space for psi_array[%d].\n", j);
            exit(0);
        }
        for (k=0; k<(one<<max_spacesize); k++) psi_array[j][k] = 0;
        psi_equals[j] = (int *)malloc(34 * sizeof(int));
        if (psi_equals[j]==0)
        {
            printf("\nCould not allocate space for psi_equals[%d].\n", j);
            exit(0);
        }
        for (k=0; k<34; k++) psi_equals[j][k] = 0;
    }
    psi = (int ***)malloc(71 * sizeof(int **));
    if (psi==0)
    {
        printf("\nCould not allocate space for psi.\n");
        exit(0);
    }
    for (j=0; j<71; j++)
    {
        psi[j] = (int **)malloc(34 * sizeof(int *));
        if (psi[j]==0)
        {
            printf("\nCould not allocate space for psi[%d].\n", j);
            exit(0);
        }
        for (k=0; k<34; k++)
        {
            psi[j][k] = (int *)malloc(34 * sizeof(int));
            if (psi[j][k]==0)
            {
                printf("\nCould not allocate space for psi[%d][%d].\n", j, k);
                exit(0);
            }
            for (l=0; l<34; l++) psi[j][k][l] = 0;
        }
    }

// Sets are represented by unsigned 32-bit binary integers with leading zeros.
// Assuming X = {0,1,...,n-1} the element k is in A iff the coefficient of 2^k in the
// binary integer representing A is 1 (i.e., the bits of the binary integer representing
// A are in reverse order compared to X).

    point_closures = (uint32_t *)malloc(spacesize * sizeof(uint32_t));
    if (point_closures==0)
    {
        printf("\nCould not allocate space for point_closures.\n");
        exit(0);
    }

// When we first encounter a set with psi-number n, for all x,y <= 34 we set
//
//     psi[n][x][y] = 1 iff psi_array[x][set] == psi_array[y][set].
//
// We then intersect all psi[] matrices pairwise and compute the psi-numbers of these
// intersections.

    for (spacesize = 2; spacesize < 9; spacesize++)
    {
        switch (spacesize)
        {
            case 2:
                input = fopen("2-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 2-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 3:
                input = fopen("3-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 3-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 4:
                input = fopen("4-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 4-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 5:
                input = fopen("5-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 5-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 6:
                input = fopen("6-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 6-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 7:
                input = fopen("7-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 7-spaces.txt for input.\n");
                    exit(0);
                }
                break;
            case 8:
                input = fopen("8-spaces.txt", "r");
                if (input==0)
                {
                    printf("\nCould not open file 8-spaces.txt for input.\n");
                    exit(0);
                }
        }

        space = 0;
        while (space < spaces[spacesize])
        {

// Each line of input has the closure of every singleton.  These can alternatively be
// regarded as smallest open neighborhoods.  Either interpretation yields all
// nonhomeomorphic spaces of cardinality = spacesize, but any given line does not
// in general produce the same space under the two.

// Read the current line of input into the point_closures array.

            for (j=0; j<spacesize; j++) fscanf(input, "%" PRIu32, &point_closures[j]);

// Compute KF(A) then copy KFZ(A), KZ(A) for every A in X.

            ComputeKF(kf[spacesize],spacesize,point_closures);
            CopyKFZfromKF(kfz[spacesize],kf[spacesize],spacesize);

// Store KF(A) in array format.

            CopyKFarray(psi_array,kf[spacesize],spacesize);

// Compute all 0-1 psi-number matrices psi[j] (just once for each j).

            for (set=0; set<(one<<spacesize); set++)
            {
                j = Psi(set,spacesize,kfz[spacesize]);
                if (!have_psi[j])
                {
                    for (k=0; k<34; k++)
                        for (l=0; l<34; l++)
                        {
                            if (psi_array[k][set] == psi_array[l][set]) psi[j][k][l] = 1;
                            else psi[j][k][l] = 0;
                        }
                    have_psi[j] = 1;
                }
            }
            space++;
        }
        fclose(input);
    }

// Print psi-numbers.h

    printf("// psi-numbers.h\n");
    printf("// Copyright (C) 2023 Mark Bowron\n\n");
    printf("// Psi-number matrices.\n\n");
    printf("// This program is free software: you can redistribute it and/or modify\n");
    printf("// it under the terms of the GNU General Public License as published by\n");
    printf("// the Free Software Foundation, either version 3 of the License, or\n");
    printf("// (at your option) any later version.\n\n");
    printf("// This program is distributed in the hope that it will be useful,\n");
    printf("// but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
    printf("// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
    printf("// GNU General Public License for more details.\n\n");
    printf("static const int psi_numbers[71][34][34] = {\n");

    for (j=0; j<71; j++)
    {
        printf("{");
        for (k=0; k<33; k++)
        {
            printf("{");
            for (l=0; l<33; l++) printf("%d,", psi[j][k][l]);
            printf("%d},", psi[j][k][33]);
        }
        printf("{");
        for (l=0; l<33; l++) printf("%d,", psi[j][33][l]);
        if (j<70) printf("%d}},\n", psi[j][33][33]);
        else printf("%d}}};\n\n", psi[j][33][33]);
    }

    free(point_closures);
    for (j=0; j<34; j++)
    {
        free(psi_array[j]);
        free(psi_equals[j]);
    }
    free(psi_array);
    free(psi_equals);
    for (j=0; j<71; j++)
    {
        for (k=0; k<34; k++) free(psi[j][k]);
        free(psi[j]);
    }
    free(psi);
    for (spacesize = 2; spacesize < 9; spacesize++)
    {
        FreeKFZ(kfz[spacesize]);
        FreeKF(kf[spacesize]);
    }
    free(kfz);
    free(kf);
}
