#define TABLE_SIZE_MAX 0x10000

I64 output_found, passes, table_size;

U8      *gate_type_table  = NULL, 
        *displayed_design = NULL, 
        *added_this_pass  = NULL;

U16 *input1_table = NULL, 
        *input2_table = NULL, 
        *input3_table = NULL;

#define CONNECT_WIDTH   16
#define GATE_WIDTH              37

      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      /* Graphics Not Rendered in HTML */
      
      
      
      /* Graphics Not Rendered in HTML */
      
      
      
      /* Graphics Not Rendered in HTML */
      
      
      
      /* Graphics Not Rendered in HTML */
      

U8 *gate_type_list =    "NULL\0OUTPUT\0INPUT\0"
                                                "NOT\0AND\0OR\0NAND\0NOR\0XOR\0AND3\0OR3\0NAND3\0NOR3\0";

#define GT_NULL         0  //Specifies that table entry has not been filled-in
#define GT_OUTPUT       1  //Specifies the table entry is a desired output
#define GT_INPUT        2  //Specifies that table entry comes from an input signal

#define GT_FIRST_REAL_GATE              3
#define GT_NOT                                  3
#define GT_AND                                  4
#define GT_OR                                   5
#define GT_NAND                                 6
#define GT_NOR                                  7
#define GT_XOR                                  8
#define GT_AND3                                 9
#define GT_OR3                                  10
#define GT_NAND3                                11
#define GT_NOR3                                 12
#define GT_ENTRIES_NUM                  13

#define SEL_GATES_NUM   128

U8 *imgs[GT_ENTRIES_NUM] = {NULL, NULL, NULL, 
                                                        <NOT>, <AND>, <OR>, <NAND>, <NOR>, <XOR>, <AND3>, <OR3>, <NAND3>, <NOR3>};

I64 num_inputs_entered, num_outputs_entered;
I64 num_sel_gates, 
        sel_gates[SEL_GATES_NUM];

U0 GetGates()
{
        I64 i;
        U8 *st;

        "\nEnter the available gate types in the order you prefer them to be used.\n"
        "Your choices are:\n";
        for (i = GT_FIRST_REAL_GATE; i < GT_ENTRIES_NUM; i++)
                "%z ", i, gate_type_list;
        '\n';

        num_sel_gates = 0;
        while (num_sel_gates < GT_ENTRIES_NUM)
        {
                "%d", num_sel_gates;
                st = StrGet(" Gate: ");
                if (!*st)
                {
                        Free(st);
                        return;
                }
                i = ListMatch(st, gate_type_list, LMF_IGNORE_CASE);
                Free(st);
                if (i < GT_FIRST_REAL_GATE)
                        "Invalid response\n";
                else
                        sel_gates[num_sel_gates++] = i;
        }
}

U0 Init()
{
        I64 i;

        do
        {
                table_size = I64Get("\nTable size in hex (3 input=0x100,4=0x10000): ", 0);
                if (table_size > TABLE_SIZE_MAX)
                        "Too large\n";
                else if (table_size < 1)
                {
                        "No table specified, aborting.\n";
                        throw;
                }
        }
        while (table_size > TABLE_SIZE_MAX);

        gate_type_table  = CAlloc(table_size * sizeof(U8));
        displayed_design = MAlloc((table_size + 7) / 8);
        added_this_pass  = MAlloc((table_size + 7) / 8);
        input1_table    = MAlloc(table_size * sizeof(U16));
        input2_table    = MAlloc(table_size * sizeof(U16));
        input3_table    = MAlloc(table_size * sizeof(U16));

        "\nEnter the hex truth table column values of inputs.\n";
        if (table_size <= 0x100)
                "For example, enter A=0xF0, B=0xCC and C=0xAA.\n";
        else
                "For example, enter A=0xFF00, B=0xF0F0, C=0xCCCC and D=0xAAAA.\n";
        num_inputs_entered = 0;
        while (TRUE)
        {
                "Input %C: ", 'A' + num_inputs_entered;
                i = I64Get("", -1);
                if (i < 0)
                        break;
                if (i > table_size)
                        "Too large\n";
                else
                {
                        if (gate_type_table[i])
                                "Duplicate\n";
                        else
                        {
                                gate_type_table[i] = GT_INPUT;
                                input1_table[i] = num_inputs_entered++;
                        }
                }
        }
        if (!num_inputs_entered)
        {
                "No inputs specified, aborting.\n";
                throw;
        }

        "\nEnter the hex truth table columns values of the outputs.\n";
        num_outputs_entered = 0;
        while (TRUE)
        {
                "Output %C: ", 'A' + num_outputs_entered;
                i = I64Get("", -1);
                if (i < 0)
                        break;
                if (i > table_size)
                        "Too large\n";
                else
                {
                        if (gate_type_table[i] == GT_INPUT)
                                "To produce this output, connect to input %C\n", 'A' + input1_table[i];
                        else if (gate_type_table[i] == GT_OUTPUT)
                                "Duplicate\n";
                        else
                        {
                                gate_type_table[i] = GT_OUTPUT;
                                input1_table[i] = num_outputs_entered++;
                        }
                }
        }

        if (!num_outputs_entered)
        {
                "No output specified, aborting.\n";
                throw;
        }
}

U0 DrawDesign(CDC *dc, I64 *_y, I64 output, I64 depth, I64 *_x_out, I64 *_y_out)
{
        I64 y = *_y, type = gate_type_table[output], 
                xx = (passes - depth) * (GATE_WIDTH + CONNECT_WIDTH), yy = y, 
                x1, y1, x2, y2, x3, y3;

        if (_x_out)
                *_x_out = xx;

        if (_y_out)
                *_y_out = yy;

        if (Bt(displayed_design, output) && type != GT_INPUT)
        {
                dc->color = GREEN;
                GrPrint(dc, xx - FONT_WIDTH * 3, y - 4, "Dup");
                y += 10;
        }
        else
                switch (type)
                {
                        case GT_INPUT:
                                dc->color = GREEN;
                                GrPrint(dc, xx - FONT_WIDTH - 4, y - 4, "%C", 'A' + input1_table[output]);
                                y += 10;
                                break;

                        case GT_NOT:
                                if (!Bt(displayed_design, output))
                                {
                                        y += 16;
                                        DrawDesign(dc, &y, input1_table[output], depth + 1, &x1, &y1);
                                        yy = y1;

                                        dc->color = BLUE;
                                        Sprite3(dc, xx, yy, 0, imgs[type]);

                                        dc->color = RED;
                                        GrLine(dc, xx - GATE_WIDTH, yy, x1, y1);
                                        if (_y_out)
                                                *_y_out = yy;
                                }
                                break;

                        case GT_AND:
                        case GT_OR:
                        case GT_NAND:
                        case GT_NOR:
                        case GT_XOR:
                                if (!Bt(displayed_design, output))
                                {
                                        y += 24;
                                        DrawDesign(dc, &y, input1_table[output], depth + 1, &x1, &y1);
                                        DrawDesign(dc, &y, input2_table[output], depth + 1, &x2, &y2);
                                        yy = (y1 + y2) / 2;

                                        dc->color = BLUE;
                                        Sprite3(dc, xx, yy, 0, imgs[type]);

                                        dc->color = RED;
                                        GrLine(dc, xx - GATE_WIDTH, yy - 4, x1, y1);
                                        GrLine(dc, xx - GATE_WIDTH, yy + 4, x2, y2);
                                        if (_y_out)
                                                *_y_out = yy;
                                }
                                break;

                        case GT_AND3:
                        case GT_OR3:
                        case GT_NAND3:
                        case GT_NOR3:
                                if (!Bt(displayed_design, output))
                                {
                                        y += 32;
                                        DrawDesign(dc, &y, input1_table[output], depth + 1, &x1, &y1);
                                        DrawDesign(dc, &y, input2_table[output], depth + 1, &x2, &y2);
                                        DrawDesign(dc, &y, input3_table[output], depth + 1, &x3, &y3);
                                        yy = (y1 + y2 + y3) / 3;

                                        dc->color = BLUE;
                                        Sprite3(dc, xx, yy, 0, imgs[type]);

                                        dc->color = RED;
                                        GrLine(dc, xx - GATE_WIDTH, yy - 8, x1, y1);
                                        GrLine(dc, xx - GATE_WIDTH, yy,         x2, y2);
                                        GrLine(dc, xx - GATE_WIDTH, yy + 8, x3, y3);
                                        if (_y_out)
                                                *_y_out = yy;
                                }
                                break;
                }
        dc->color = BLACK;
        GrPrint(dc, xx, yy + 3, "%04X", output);
        Bts(displayed_design, output);
        if (_y)
                *_y = y;
}

U0 DrawIt(CTask *, CDC *dc)
{
        I64 y = 0;

        MemSet(displayed_design, 0, (table_size + 7) / 8 * sizeof(Bool));
        DrawDesign(dc, &y, output_found, 0, NULL, NULL);
}

U0 FillNot(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                {
                        progress1 = i;
                        j= (~i) & (table_size - 1);
                        old_type = gate_type_table[j];
                        if (old_type < GT_INPUT)
                        {
                                gate_type_table[j] = GT_NOT;
                                input1_table[j] = i;
                                Bts(added_this_pass, j);
                                *chged = TRUE;
                                if (old_type == GT_OUTPUT)
                                {
                                        if (output_found < 0)
                                                output_found = j;
                                        *num_outputs_found = *num_outputs_found + 1;
                                }
                        }
                }
}

U0 FillAnd(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                {
                                        progress1 = i;
                                        j= (i & k) & (table_size - 1);
                                        old_type = gate_type_table[j];
                                        if (old_type < GT_INPUT)
                                        {
                                                gate_type_table[j] = GT_AND;
                                                input1_table[j] = i;
                                                input2_table[j] = k;
                                                Bts(added_this_pass, j);
                                                *chged = TRUE;
                                                if (old_type == GT_OUTPUT)
                                                {
                                                        if (output_found < 0)
                                                                output_found = j;
                                                        *num_outputs_found = *num_outputs_found + 1;
                                                }
                                        }
                                }
}

U0 FillOr(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                {
                                        progress1 = i;
                                        j= (i | k) & (table_size - 1);
                                        old_type = gate_type_table[j];
                                        if (old_type < GT_INPUT)
                                        {
                                                gate_type_table[j] = GT_OR;
                                                input1_table[j] = i;
                                                input2_table[j] = k;
                                                Bts(added_this_pass, j);
                                                *chged = TRUE;
                                                if (old_type == GT_OUTPUT)
                                                {
                                                        if (output_found < 0)
                                                                output_found = j;
                                                        *num_outputs_found = *num_outputs_found + 1;
                                                }
                                        }
                                }
}

U0 FillNAnd(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                {
                                        progress1 = i;
                                        j= (~ (i & k)) & (table_size - 1);
                                        old_type = gate_type_table[j];
                                        if (old_type < GT_INPUT)
                                        {
                                                gate_type_table[j] = GT_NAND;
                                                input1_table[j] = i;
                                                input2_table[j] = k;
                                                Bts(added_this_pass, j);
                                                *chged = TRUE;
                                                if (old_type == GT_OUTPUT)
                                                {
                                                        if (output_found < 0)
                                                                output_found = j;
                                                        *num_outputs_found = *num_outputs_found + 1;
                                                }
                                        }
                                }
}

U0 FillNOr(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                {
                                        progress1 = i;
                                        j= (~ (i | k)) & (table_size - 1);
                                        old_type = gate_type_table[j];
                                        if (old_type < GT_INPUT)
                                        {
                                                gate_type_table[j] = GT_NOR;
                                                input1_table[j] = i;
                                                input2_table[j] = k;
                                                Bts(added_this_pass, j);
                                                *chged = TRUE;
                                                if (old_type == GT_OUTPUT)
                                                {
                                                        if (output_found < 0)
                                                                output_found = j;
                                                        *num_outputs_found = *num_outputs_found + 1;
                                                }
                                        }
                                }
}

U0 FillXor(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                {
                                        progress1 = i;
                                        j= (i ^ k) & (table_size - 1);
                                        old_type = gate_type_table[j];
                                        if (old_type < GT_INPUT)
                                        {
                                                gate_type_table[j] = GT_XOR;
                                                input1_table[j] = i;
                                                input2_table[j] = k;
                                                Bts(added_this_pass, j);
                                                *chged = TRUE;
                                                if (old_type == GT_OUTPUT)
                                                {
                                                        if (output_found < 0)
                                                                output_found = j;
                                                        *num_outputs_found = *num_outputs_found + 1;
                                                }
                                        }
                                }
}

U0 FillAnd3(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, l, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                        for (l = 0; l < table_size; l++)
                                                if (gate_type_table[l] > GT_OUTPUT && !Bt(added_this_pass, l))
                                                {
                                                        progress1 = i;
                                                        j= (i & k & l) & (table_size - 1);
                                                        old_type = gate_type_table[j];
                                                        if (old_type < GT_INPUT)
                                                        {
                                                                gate_type_table[j] = GT_AND3;
                                                                input1_table[j] = i;
                                                                input2_table[j] = k;
                                                                input3_table[j] = l;
                                                                Bts(added_this_pass, j);
                                                                *chged = TRUE;
                                                                if (old_type == GT_OUTPUT)
                                                                {
                                                                        if (output_found < 0)
                                                                                output_found = j;
                                                                        *num_outputs_found = *num_outputs_found + 1;
                                                                }
                                                        }
                                                }
}

U0 FillOr3(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, l, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                        for (l = 0; l < table_size; l++)
                                                if (gate_type_table[l] > GT_OUTPUT && !Bt(added_this_pass, l))
                                                {
                                                        progress1 = i;
                                                        j= (i | k | l) & (table_size - 1);
                                                        old_type = gate_type_table[j];
                                                        if (old_type < GT_INPUT)
                                                        {
                                                                gate_type_table[j] = GT_OR3;
                                                                input1_table[j] = i;
                                                                input2_table[j] = k;
                                                                input3_table[j] = l;
                                                                Bts(added_this_pass, j);
                                                                *chged = TRUE;
                                                                if (old_type == GT_OUTPUT)
                                                                {
                                                                        if (output_found < 0)
                                                                                output_found = j;
                                                                        *num_outputs_found = *num_outputs_found + 1;
                                                                }
                                                        }
                                                }
}

U0 FillNAnd3(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, l, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                        for (l = 0; l < table_size; l++)
                                                if (gate_type_table[l] > GT_OUTPUT && !Bt(added_this_pass, l))
                                                {
                                                        progress1 = i;
                                                        j= (~(i & k & l)) & (table_size - 1);
                                                        old_type = gate_type_table[j];
                                                        if (old_type < GT_INPUT)
                                                        {
                                                                gate_type_table[j] = GT_NAND3;
                                                                input1_table[j] = i;
                                                                input2_table[j] = k;
                                                                input3_table[j] = l;
                                                                Bts(added_this_pass, j);
                                                                *chged = TRUE;
                                                                if (old_type == GT_OUTPUT)
                                                                {
                                                                        if (output_found < 0)
                                                                                output_found = j;
                                                                        *num_outputs_found = *num_outputs_found + 1;
                                                                }
                                                        }
                                                }
}

U0 FillNOr3(Bool *chged, I64 *num_outputs_found)
{
        I64 i, j, k, l, old_type;

        for (i = 0; i < table_size; i++)
                if (gate_type_table[i] > GT_OUTPUT && !Bt(added_this_pass, i))
                        for (k = 0; k < table_size; k++)
                                if (gate_type_table[k] > GT_OUTPUT && !Bt(added_this_pass, k))
                                        for (l = 0; l < table_size; l++)
                                                if (gate_type_table[l] > GT_OUTPUT && !Bt(added_this_pass, l))
                                                {
                                                        progress1 = i;
                                                        j= (~(i | k | l)) & (table_size - 1);
                                                        old_type = gate_type_table[j];
                                                        if (old_type < GT_INPUT)
                                                        {
                                                                gate_type_table[j] = GT_NOR3;
                                                                input1_table[j] = i;
                                                                input2_table[j] = k;
                                                                input3_table[j] = l;
                                                                Bts(added_this_pass, j);
                                                                *chged = TRUE;
                                                                if (old_type == GT_OUTPUT)
                                                                {
                                                                        if (output_found < 0)
                                                                                output_found = j;
                                                                        *num_outputs_found = *num_outputs_found + 1;
                                                                }
                                                        }
                                                }
}

I64 FillGateTable()
{
        I64  current_gate, num_outputs_found = 0;
        Bool chged = TRUE;

        passes = 1;
        output_found = -1;
        ProgressBarsReset;
        progress1_max = table_size;
        '\n';
        while (num_outputs_found<num_outputs_entered && chged)
        {
                "Pass : %d\n", passes++;
                chged = FALSE;
                MemSet(added_this_pass, 0, (table_size + 7) / 8);
                for (current_gate = 0; current_gate < num_sel_gates && num_outputs_found < num_outputs_entered; current_gate++)
                {
                        switch (sel_gates[current_gate])
                        {
                                case GT_NOT:    FillNot  (&chged, &num_outputs_found); break;
                                case GT_AND:    FillAnd  (&chged, &num_outputs_found); break;
                                case GT_OR:     FillOr   (&chged, &num_outputs_found); break;
                                case GT_NAND:   FillNAnd (&chged, &num_outputs_found); break;
                                case GT_NOR:    FillNOr  (&chged, &num_outputs_found); break;
                                case GT_XOR:    FillXor  (&chged, &num_outputs_found); break;
                                case GT_AND3:   FillAnd3 (&chged, &num_outputs_found); break;
                                case GT_OR3:    FillOr3  (&chged, &num_outputs_found); break;
                                case GT_NAND3:  FillNAnd3(&chged, &num_outputs_found); break;
                                case GT_NOR3:   FillNOr3 (&chged, &num_outputs_found); break;
                        }
                }
        }
        ProgressBarsReset;

        return num_outputs_found;
}

U0 CleanUp()
{
        Free(gate_type_table);
        Free(displayed_design);
        Free(added_this_pass);
        Free(input1_table);
        Free(input2_table);
        Free(input3_table);
}

U0 DigitalLogic()
{
        gate_type_table  = NULL;
        displayed_design = NULL;
        added_this_pass  = NULL;
        input1_table = NULL;
        input2_table = NULL;
        input3_table = NULL;

        SettingsPush; //See SettingsPush
        AutoComplete;
        WinBorder(ON);
        WinMax;
        DocClear;
        GetGates;
        try
        {
                Init;
                if (FillGateTable)
                {
                        DocCursor;
                        DocClear;
                        Fs->draw_it = &DrawIt;
                        CharGet;
                        DocClear;
                        Refresh(2, TRUE);
                        DocBottom;
                }
        }
        catch
                PutExcept;
        SettingsPop;
        CleanUp;
}