// This is the Singular code that performs the computations described in Section 6
in the paper
//
// "Algebraic characterization of uniquely vertex colorable graphs"
//
// by Christopher J. Hillar and Troels Windfeldt.

// Options

option(redSB);
option(noredefine);
option(noprot);

timer = 1;

// Load libraries

LIB "general.lib";
LIB "qhmoduli.lib";

// Define functions

proc number_of_vertices(list G)
{
    return(G[1]);
}

proc number_of_edges(list G)
{
    return(nrows(G[2]));
}

proc edge_polynomial(list G, int n, int d)
{
    intmat edges = G[2];
    int i = edges[n,1];
    int j = edges[n,2];
    if (i < j)
    {
        return(x(i)^d-x(j)^d);
    }
    else
    {
        return(x(j)^d-x(i)^d);    
    }
}

proc graph_polynomial(list G)
{
    list l;
    int m = number_of_edges(G);
    for (int i = 1; i <= m; i++)
    {
        l[i] = edge_polynomial(G, i, 1);
    }
    return(product(l));
}

proc bin(int n, int k)
{
    list l = ringlist(basering);
    def R2 = ring(l);
    ring S = 0, x ,lp;
    int b = int(binomial(n, k, 0));
    setring(R2);
    return(b);
}

proc I(expr, int k)
{
    if (typeof(expr) == "int") { int n = expr; }
    else 
    {
        list G = expr;
        int n = number_of_vertices(G);
    }    
    ideal A;
    for (int i = 1; i <= n; i++)
    {
        A[i] = x(i)^k-1;
    }
    if (typeof(expr) == "int") { return(A); }
    int m = number_of_edges(G);
    for (int i = 1; i <= m; i++)
    {
        A[n+i] = quotient(edge_polynomial(G, i, k), edge_polynomial(G, i, 1))[1];
    }
    return(A);
}

proc J(int n, int k)
{
    if (k >= n) { return( ERROR("k must be less than n."); ); }
    k++;
    int m = bin(n,k);
    intmat B[m][k];
    for (int i = 1; i <= k; i++)
    {
        B[1,i] = i;    
    }
    for (int i = 2; i <= m; i++)
    {
        if (B[i-1,k] < n)
        {
            for (int j = 1; j < k; j++)
            {
                B[i,j] = B[i-1,j];
            }
            B[i,k] = B[i-1,k] + 1;
        }
        else
        {
            int l = k;
            while (B[i-1,l] == n-k+l) { l--; }
            for (int j = 1; j < l; j++) { B[i,j] = B[i-1,j]; }
            for (int j = l; j <= k; j++) { B[i,j] = B[i-1,l] + j - (l - 1); }
        }
    }
    ideal A;
    intmat edges[bin(k,2)][2];
    list G = n, edges;
    for (int l = 1; l <= nrows(B); l++)
    {
        int m = 1;
        for (int i = 1; i < k; i++)
        {
            for (int j = i + 1; j <= k; j++)
            {
                G[2][m,1] = B[l,i];
                G[2][m,2] = B[l,j];
                m++;
            }
        }
        A[l] = graph_polynomial(G);
    }
    return(A);
}

proc is_colorable(list G, int k, int m)
{
    if (m == 2)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        ideal A = I(n,k);
        ideal B = I(G,k);
        for (int i = 1; i <= m; i++)
        {
            A = ideal(groebner(A + ideal(B[n+i])));
        }
        A = std(A);
        return(vdim(A) != 0);    
    }
    if (m == 3)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        ideal A = I(n,k);
        ideal B = I(G,k);
        for (int i = 1; i <= m; i++)
        {
            A = ideal(groebner(A + ideal(B[n+i])));
        }
        A = std(A);
        return(reduce(1, A) != 0);
    }
    if (m == 4)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        poly f = 1;
        ideal A = I(n,k);
        for (int i = 1; i <= m; i++)
        {
            f = reduce(f * edge_polynomial(G, i, 1), A);
        }
        return(f != 0);
    }
    if (m == 5)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        poly f = 1;
        ideal A = J(n,k);
        for (int i = 1; i <= m; i++)
        {
            f = reduce(f * edge_polynomial(G, i, 1), A);
        }
        return(f != 0);
    }
    return( ERROR("Method m must be 2, 3, 4, or 5."); );
}

proc max_in_color_class(list G, int i)
{
    int n = number_of_vertices(G);
    list l;
    for (int j = 1; j <= n; j++)
    {
        if (G[3][j] == G[3][i]) { l = l + list(j); }
    }
    return(Max(l));
}

proc h(list U, int d)
{
    if (size(U) == 1) { return(x(U[1])^d); }
    else
    {
        return(quotient((h(delete(U, 2), d+1) - h(delete(U, 1),
d+1)),(x(U[1])-x(U[2])))[1]);
    }
}

proc g(list G, int i)
{
    int n = number_of_vertices(G);
    int k = Max(G[3]);
    if (i == n) { return(x(n)^k-1); }
    list l;
    for (int j = 1; j <= n; j++) { l = l + list(max_in_color_class(G, j)); }
    int m_0 = Min(l);
    int m_i = max_in_color_class(G, i);
    if (i == m_i or m_0 == m_i)
    {
        list v;
        list U;
        for (int j = 1; j <= n; j++) { v[j] = 0; }
        for (int j = 1; j <= n; j++)
        {
            int m_j = max_in_color_class(G, j);
            if (m_j > m_i) { v[m_j] = 1; }
        }
        v[i] = 1;
        for (int j = 1; j <= n; j++)
        {
            if (v[j] == 1) { U = U + list(j); }
        }
        return(h(U, k+1-size(U)));
    }
    else
    {
        return(x(i)-x(max_in_color_class(G, i)));
    }
}

proc g_bar(list G, int i)
{
    int n = number_of_vertices(G);
    int k = Max(G[3]);
    if (i == n) { return(1); }
    list l;
    for (int j = 1; j <= n; j++) { l = l + list(max_in_color_class(G, j)); }
    int m_0 = Min(l);
    int m_i = max_in_color_class(G, i);
    if (i == m_i or m_0 == m_i)
    {
        list v;
        list U;
        for (int j = 1; j <= n; j++) { v[j] = 0; }
        for (int j = 1; j <= n; j++)
        {
            int m_j = max_in_color_class(G, j);
            if (m_j > m_i) { v[m_j] = 1; }
        }
        v[i] = 1;
        for (int j = 1; j <= n; j++)
        {
            if (v[j] == 1) { U = U + list(j); }
        }
        intmat edges[bin(size(U),2)][2];
        list G = n, edges;
        int m = 1;
        for (int j1 = 1; j1 < size(U); j1++)
        {
            for (int j2 = j1 + 1; j2 <= size(U); j2++)
            {
                G[2][m,1] = U[j1];
                G[2][m,2] = U[j2];
                m++;
            }
        }
        return(graph_polynomial(G));
    }
    else
    {
        list U = i, max_in_color_class(G, i);
        return(h(U, 2));
    }
}

proc is_uniquely_colorable(list G, int m)
{
    int k = Max(G[3]);
    if (m == 3)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        ideal A = I(n,k);
        ideal B = I(G,k);
        for (int i = 1; i <= m; i++)
        {
            A = ideal(groebner(A + ideal(B[n+i])));
        }
        A = std(A);
        for (int i = 1; i <= n; i++)
        {
            if (reduce(g(G, i), A) != 0) { return(0); }
        }
        return(1);
    }
    if (m == 4)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        poly f = 1;
        poly l = 1;
        for (int i = 1; i <= n; i++) {l = l * g_bar(G, i); }
        ideal A = groebner(I(n,k) + ideal(l));
        for (int i = 1; i <= m; i++)
        {
            f = reduce(f * edge_polynomial(G, i, 1), A);
        }
        return(f == 0);        
    }
    if (m == 5)
    {
        int n = number_of_vertices(G);
        int m = number_of_edges(G);
        ideal A = I(n,k);
        ideal B = I(G,k);
        for (int i = 1; i <= m; i++)
        {
            A = ideal(groebner(A + ideal(B[n+i])));
        }
        A = std(A);
        return(string(vdim(A)) == factorial(k));    
    }
    return( ERROR("Method m must be 3, 4, or 5."); );
}

// Define the graph in Figure 1.

int n1 = 12;
int m1 = 23;
intmat edges1[m1][2] =
1,2,
1,4,
1,6,
1,12,
2,3,
2,5,
2,7,
3,8,
3,10,
4,9,
4,11,
5,6,
5,9,
5,12,
6,7,
6,10,
7,8,
7,11,
8,9,
8,12,
9,10,
10,11,
11,12;
intvec coloring1 = 1, 3, 2, 2, 1, 3, 2, 1, 3, 1, 3, 2;
list G1 = n1, edges1, coloring1;

// Define the graph in Figure 2.

int n2 = 24;
int m2 = 45;
intmat edges2[m2][2] =
1,2,
1,4,
1,6,
1,12,
2,3,
2,7,
3,10,
4,9,
4,11,
5,6,
5,9,
5,12,
6,7,
6,10,
7,8,
7,11,
8,9,
8,12,
10,11,
11,12,
13,14,
13,16,
13,18,
13,24,
14,15,
14,19,
15,22,
16,21,
16,23,
17,18,
17,21,
17,24,
18,19,
18,22,
19,20,
19,23,
20,21,
20,24,
22,23,
23,24,
2,14,
10,14,
10,22,
7,15,
3,18;
intvec coloring2 = 1, 3, 2, 2, 1, 3, 2, 1, 3, 1, 3, 2, 3, 2, 1, 1, 2, 1, 3, 2, 3, 3,
2, 1;
list G2 = n2, edges2, coloring2;

list G = G2;

// Main

ring R = 2, x(1..G[1]), dp;

system("--ticks-per-sec", "100");
int t = timer;

t = timer;
is_uniquely_colorable(G, 5);
t = timer-t; t;
