/*
   File: imatrix.c
   Defines graph coloring routines
  
   Copyright 2005, Radboud University of Nijmegen.
  
   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 2 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 Library General Public License for more details.
  
   You should have received a copy of the GNU Library General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   CVS ID: "$Id: imatrix.c,v 1.11 2005/09/06 10:03:16 marcs Exp $"
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <stdlib.h>
#include <cdl3rts.h>

/* Libabase includes */
#include <abase_error.h>

/* Local includes */
#include "imatrix.h"

/*
** Prototype
*/
static int color_it();
int first_free_color(int i, int num);
int delphi();


/*
** Global values
*/

static char** matrix;
static int size;
static int* color;
static int* out;
static int* count;
static int* palette;

/*
** Definitions
*/

#define INC 32
#define NO_COLOR (-1)
#define NO_COUNT (-1)


/*
** ACTION initialize imatrix(>INT)
*/

void
E212_initialize_imatrix_INT(value nr_affix_terminals)
{
    int i;
    size = Int(nr_affix_terminals);

    if (size == 0) {
        return;
    }

    /* allocate the matrix */
    matrix = (char **) malloc(size * sizeof(char *));
    if (matrix == NULL)
       abs_fatal("imatrix(initialize_matrix): not enough memory");

    for (i = 0; i < size; i++) {
        matrix[i] = (char *) calloc(size, sizeof(char));
        if (matrix[i] == NULL)
           abs_fatal ("imatrix(initialize_matrix): not enough memory");
    }

    /* Init color,out and count */
    color = (int *) malloc(size * sizeof(int));
    if (color == NULL)
       abs_fatal ("imatrix(initialize_matrix): not enough memory");

    for (i = 0; i < size; i++)
       color[i] = NO_COLOR;

    out = (int *) calloc(size, sizeof(int));
    if (out == NULL)
       abs_fatal ("imatrix(initialize_matrix): not enough memory");

    count = (int *) calloc(size, sizeof(int));
    if (count == NULL)
       abs_fatal ("imatrix(initialize_matrix): not enough memory");

    /* Init palette */
    palette = (int *) malloc((size / INC + 1) * INC * sizeof(int));
    if (palette == NULL)
       abs_fatal ("imatrix(initialize_matrix): not enough memory");
}


/*
** ACTION imatrix set (>INT1, >INT2)
*/
void E213_imatrix_set_INT_INT(value v_INT, value v_INT1)
{
    matrix[Int(v_INT)][Int(v_INT1)] = 1;
}

/*
** FUNCTION get color (>INT1, INT2>)
*/
void E215_get_color_INT_INT(value node_value, value* res)
{
    int node = Int(node_value);

    if (color[node] == NO_COLOR)
       abs_fatal ("imatrix(get_color): enquiry to uncolored node '%d'", node);

    *res = C_INT(color[node]);
}


/*
** ACTION color imatrix(INT>)
*/
void E214_color_imatrix_INT(value* result)
{
    int i,j;

    /* Set up count array */
    for (i = 0; i < size; i++) {
        for (j = 0; j < size; j++) {
            if (matrix[i][j]) {
                count[i]++;
            }
        }
    }

    /* Color it */
    *result = C_INT(color_it(INC));
}


static int
color_it(int n)
{
    int i,j;
    int rem;

    /* Look for row with less than n crosses */
    rem = 0;
    for(i = 0; i < size; i++) {
        if (out[i]) {
            continue;
        }

        rem++;

        if (count[i] < n) {
            break;
        }
    }

    /* Found? */
    if (i == size) {
        /* Nope, can we color them? */
        if ( rem <= n)
        {
            /* Color remaining nodes */
            for (i = 0; i < size; i++) {
                if (!out[i]) {
                    if ((color[i] = first_free_color(i, n)) == NO_COLOR)
                       abs_fatal ("imatrix(color_it): cannot color remaining nodes");
                }
            }

            return n; /* Wow */
        }

        /* We cannot color the remaining nodes, consult the oracle */
        i=delphi();
    }

    /* Remove node from graph */
    for (j = 0; j < size; j++) {
        if (matrix[i][j]) {
            count[j]--;
            matrix[j][i] = 0;
        }
    }

    out[i] = 1;

    /* Color the resulting graph */
    n = color_it(n);

    /* Rebuild graph */
    out[i] = 0;
    for (j = 0; j < size; j++) {
            if (matrix[i][j]) {
            matrix[j][i] = 1;
            count[j]++;
        }
    }

    /* Try to color node */
    if ((color[i] = first_free_color(i, n)) == NO_COLOR) {
        return color_it(n + INC); /* try with more colors */
    }

    return n;
}


/*
** delphi - the oracle
*/

int
delphi()
{
    int i;
    int ret = 0;
    int max = NO_COUNT;

    for (i = 0; i < size; i++) {
        if(!out[i] && count[i] > max) {
            max = count[i];
            ret = i;
        }
    }

    if (max == NO_COUNT)
       abs_fatal ("imatrix(delphi): no applicable node");

    return ret;
}


/*
** Returns first free applicable color for node i
** with num colors to choose from.
** NO_COLOR if there is no such color
*/

int
first_free_color(int i, int num)
{
    int j;

    /* Clear palette */
    for (j = 0; j < num; j++) {
        palette[j] = 0;
    }

    /* Mark used colors in palette */
    for (j = 0; j < size; j++) {
        if (matrix[i][j] && color[j] != NO_COLOR) {
            palette[color[j]] = 1;
        }
    }

    /* Pick one out */
    for (j = 0; j < num; j++) {
        if (palette[j] == 0) {
            break;
        }
    }

    if (j == num) {
        return NO_COLOR;
    }

    return j;
}

