/*
 * $Id: GroebnerBaseSeqPairDistributed.java 3626 2011-05-08 09:51:57Z kredel $
 */

package edu.jas.gb;


import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Collections;
import java.util.concurrent.Semaphore;

import org.apache.log4j.Logger;

import edu.jas.poly.ExpVector;
import edu.jas.poly.GenPolynomial;
import edu.jas.structure.RingElem;
import edu.jas.util.ChannelFactory;
import edu.jas.util.DistHashTable;
import edu.jas.util.DistHashTableServer;
import edu.jas.util.SocketChannel;
import edu.jas.util.Terminator;
import edu.jas.util.ThreadPool;


/**
 * Groebner Base distributed algorithm. Implements a distributed memory parallel
 * version of Groebner bases. Using pairlist class, distributed tasks do
 * reduction. Makes some effort to produce the same sequence of critical pairs
 * as in the sequential version. However already reduced pairs are not rereduced
 * if new polynomials appear.
 * @param <C> coefficient type
 * @author Heinz Kredel
 */

public class GroebnerBaseSeqPairDistributed<C extends RingElem<C>> extends GroebnerBaseAbstract<C> {


    private static final Logger logger = Logger.getLogger(GroebnerBaseSeqPairDistributed.class);


    /**
     * Number of threads to use.
     */
    protected final int threads;


    /**
     * Default number of threads.
     */
    protected static final int DEFAULT_THREADS = 2;


    /**
     * Pool of threads to use.
     */
    protected final ThreadPool pool;


    /**
     * Default server port.
     */
    protected static final int DEFAULT_PORT = 4711;


    /**
     * Server port to use.
     */
    protected final int port;


    /**
     * Constructor.
     */
    public GroebnerBaseSeqPairDistributed() {
        this(DEFAULT_THREADS, DEFAULT_PORT);
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     */
    public GroebnerBaseSeqPairDistributed(int threads) {
        this(threads, new ThreadPool(threads), DEFAULT_PORT);
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     * @param red parallelism aware reduction engine
     */
    public GroebnerBaseSeqPairDistributed(int threads, Reduction<C> red) {
        this(threads, new ThreadPool(threads), DEFAULT_PORT, red);
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     * @param port server port to use.
     * @param red parallelism aware reduction engine
     */
    public GroebnerBaseSeqPairDistributed(int threads, int port, Reduction<C> red) {
        this(threads, new ThreadPool(threads), port, red);
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     * @param port server port to use.
     */
    public GroebnerBaseSeqPairDistributed(int threads, int port) {
        this(threads, new ThreadPool(threads), port);
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     * @param pool ThreadPool to use.
     * @param port server port to use.
     */
    public GroebnerBaseSeqPairDistributed(int threads, ThreadPool pool, int port) {
        this(threads, pool, port, new ReductionPar<C>());
    }


    /**
     * Constructor.
     * @param threads number of threads to use.
     * @param pool ThreadPool to use.
     * @param port server port to use.
     * @param red parallelism aware reduction engine
     */
    public GroebnerBaseSeqPairDistributed(int threads, ThreadPool pool, int port, Reduction<C> red) {
        super(red);
        if (!(red instanceof ReductionPar)) {
            logger.warn("parallel GB should use parallel aware reduction");
        }
        if (threads < 1) {
            threads = 1;
        }
        this.threads = threads;
        this.pool = pool;
        this.port = port;
    }


    /**
     * Cleanup and terminate ThreadPool.
     */
    @Override
    public void terminate() {
        if (pool == null) {
            return;
        }
        pool.terminate();
    }


    /**
     * Distributed Groebner base. Slaves maintain pairlist.
     * @param modv number of module variables.
     * @param F polynomial list.
     * @return GB(F) a Groebner base of F or null, if a IOException occurs.
     */
    public List<GenPolynomial<C>> GB(int modv, List<GenPolynomial<C>> F) {

        final int DL_PORT = port + 100;
        ChannelFactory cf = new ChannelFactory(port);
        cf.init();
        DistHashTableServer<Integer> dls = new DistHashTableServer<Integer>(DL_PORT);
        dls.init();
        logger.debug("dist-list server running");

        GenPolynomial<C> p;
        List<GenPolynomial<C>> G = new ArrayList<GenPolynomial<C>>();
        CriticalPairList<C> pairlist = null;
        boolean oneInGB = false;
        int l = F.size();
        int unused;
        ListIterator<GenPolynomial<C>> it = F.listIterator();
        while (it.hasNext()) {
            p = it.next();
            if (p.length() > 0) {
                p = p.monic();
                if (p.isONE()) {
                    oneInGB = true;
                    G.clear();
                    G.add(p);
                    //return G; must signal termination to others
                }
                if (!oneInGB) {
                    G.add(p);
                }
                if (pairlist == null) {
                    pairlist = new CriticalPairList<C>(modv, p.ring);
                }
                // theList not updated here
                if (p.isONE()) {
                    unused = pairlist.putOne();
                } else {
                    unused = pairlist.put(p);
                }
            } else {
                l--;
            }
        }
        if (l <= 1) {
            //return G; must signal termination to others
        }

        logger.debug("looking for clients");
        //long t = System.currentTimeMillis();
        // now in DL, uses resend for late clients
        //while ( dls.size() < threads ) { sleep(); }

        DistHashTable<Integer, GenPolynomial<C>> theList = new DistHashTable<Integer, GenPolynomial<C>>(
                "localhost", DL_PORT);
        List<GenPolynomial<C>> al = pairlist.getList();
        for (int i = 0; i < al.size(); i++) {
            // no wait required
            GenPolynomial<C> nn = theList.put(new Integer(i), al.get(i));
            if (nn != null) {
                logger.info("double polynomials " + i + ", nn = " + nn + ", al(i) = " + al.get(i));
            }
        }

        Terminator fin = new Terminator(threads);
        ReducerServerSeqPair<C> R;
        for (int i = 0; i < threads; i++) {
            R = new ReducerServerSeqPair<C>(fin, cf, theList, G, pairlist);
            pool.addJob(R);
        }
        logger.debug("main loop waiting");
        fin.waitDone();
        int ps = theList.size();
        //logger.debug("#distributed list = "+ps);
        // make sure all polynomials arrived: not needed in master
        // G = (ArrayList)theList.values();
        G = pairlist.getList();
        //logger.debug("#pairlist list = "+G.size());
        if (ps != G.size()) {
            logger.info("#distributed list = " + theList.size() + " #pairlist list = " + G.size());
        }
        long time = System.currentTimeMillis();
        List<GenPolynomial<C>> Gp;
        Gp = minimalGB(G); // not jet distributed but threaded
        time = System.currentTimeMillis() - time;
        logger.info("parallel gbmi = " + time);
        /*
        time = System.currentTimeMillis();
        G = GroebnerBase.<C>GBmi(G); // sequential
        time = System.currentTimeMillis() - time;
        logger.info("sequential gbmi = " + time);
        */
        G = Gp;
        //logger.info("cf.terminate()");
        cf.terminate();
        // no more required // pool.terminate();
        logger.info("theList.terminate()");
        theList.terminate();
        logger.info("dls.terminate()");
        dls.terminate();
        logger.info("" + pairlist);
        return G;
    }


    /**
     * GB distributed client.
     * @param host the server runns on.
     * @throws IOException
     */
    public void clientPart(String host) throws IOException {

        ChannelFactory cf = new ChannelFactory(port + 10); // != port for localhost
        cf.init();
        SocketChannel pairChannel = cf.getChannel(host, port);

        final int DL_PORT = port + 100;
        DistHashTable<Integer, GenPolynomial<C>> theList = new DistHashTable<Integer, GenPolynomial<C>>(host,
                DL_PORT);

        ReducerClientSeqPair<C> R = new ReducerClientSeqPair<C>(pairChannel, theList);
        R.run();

        pairChannel.close();
        theList.terminate();
        cf.terminate();
        return;
    }


    /**
     * Minimal ordered groebner basis.
     * @param Fp a Groebner base.
     * @return a reduced Groebner base of Fp.
     */
    @Override
    public List<GenPolynomial<C>> minimalGB(List<GenPolynomial<C>> Fp) {
        GenPolynomial<C> a;
        ArrayList<GenPolynomial<C>> G;
        G = new ArrayList<GenPolynomial<C>>(Fp.size());
        ListIterator<GenPolynomial<C>> it = Fp.listIterator();
        while (it.hasNext()) {
            a = it.next();
            if (a.length() != 0) { // always true
                // already monic  a = a.monic();
                G.add(a);
            }
        }
        if (G.size() <= 1) {
            return G;
        }

        ExpVector e;
        ExpVector f;
        GenPolynomial<C> p;
        ArrayList<GenPolynomial<C>> F;
        F = new ArrayList<GenPolynomial<C>>(G.size());
        boolean mt;

        while (G.size() > 0) {
            a = G.remove(0);
            e = a.leadingExpVector();

            it = G.listIterator();
            mt = false;
            while (it.hasNext() && !mt) {
                p = it.next();
                f = p.leadingExpVector();
                mt = e.multipleOf(f);
            }
            it = F.listIterator();
            while (it.hasNext() && !mt) {
                p = it.next();
                f = p.leadingExpVector();
                mt = e.multipleOf(f);
            }
            if (!mt) {
                F.add(a);
            } else {
                // System.out.println("dropped " + a.length());
            }
        }
        G = F;
        if (G.size() <= 1) {
            return G;
        }
        Collections.reverse(G); // important for lex GB

        MiReducerServerSeqPair<C>[] mirs = (MiReducerServerSeqPair<C>[]) new MiReducerServerSeqPair[G.size()];
        int i = 0;
        F = new ArrayList<GenPolynomial<C>>(G.size());
        while (G.size() > 0) {
            a = G.remove(0);
            // System.out.println("doing " + a.length());
            List<GenPolynomial<C>> R = new ArrayList<GenPolynomial<C>>(G.size()+F.size());
            R.addAll(G);
            R.addAll(F);
            mirs[i] = new MiReducerServerSeqPair<C>(R,a);
            pool.addJob(mirs[i]);
            i++;
            F.add(a);
        }
        G = F;
        F = new ArrayList<GenPolynomial<C>>(G.size());
        for (i = 0; i < mirs.length; i++) {
            a = mirs[i].getNF();
            F.add(a);
        }
        return F;
    }

}


/**
 * Distributed server reducing worker threads.
 * @param <C> coefficient type
 */

class ReducerServerSeqPair<C extends RingElem<C>> implements Runnable {


    private final Terminator pool;


    private final ChannelFactory cf;


    private SocketChannel pairChannel;


    private final DistHashTable<Integer, GenPolynomial<C>> theList;


    //private List<GenPolynomial<C>> G;
    private final CriticalPairList<C> pairlist;


    private static final Logger logger = Logger.getLogger(ReducerServerSeqPair.class);


    ReducerServerSeqPair(Terminator fin, ChannelFactory cf, DistHashTable<Integer, GenPolynomial<C>> dl,
            List<GenPolynomial<C>> G, CriticalPairList<C> L) {
        pool = fin;
        this.cf = cf;
        theList = dl;
        //this.G = G;
        pairlist = L;
    }


    public void run() {
        logger.debug("reducer server running");
        try {
            pairChannel = cf.getChannel();
        } catch (InterruptedException e) {
            logger.debug("get pair channel interrupted");
            e.printStackTrace();
            return;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("pairChannel = " + pairChannel);
        }
        CriticalPair<C> pair;
        //GenPolynomial<C> pi;
        //GenPolynomial<C> pj;
        //GenPolynomial<C> S;
        GenPolynomial<C> H = null;
        boolean set = false;
        boolean goon = true;
        int polIndex = -1;
        int red = 0;
        int sleeps = 0;

        // while more requests
        while (goon) {
            // receive request
            logger.debug("receive request");
            Object req = null;
            try {
                req = pairChannel.receive();
            } catch (IOException e) {
                goon = false;
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                goon = false;
                e.printStackTrace();
            }
            //logger.debug("received request, req = " + req);
            if (req == null) {
                goon = false;
                break;
            }
            if (!(req instanceof GBSPTransportMessReq)) {
                goon = false;
                break;
            }

            // find pair
            if (logger.isDebugEnabled()) {
                logger.debug("find pair");
                logger
                        .debug("pool.hasJobs() " + pool.hasJobs() + " pairlist.hasNext() "
                                + pairlist.hasNext());
            }
            while (!pairlist.hasNext()) { // wait
                pairlist.update();
                if (!set) {
                    pool.beIdle();
                    set = true;
                }
                if (!pool.hasJobs() && !pairlist.hasNext()) {
                    goon = false;
                    break;
                }
                try {
                    sleeps++;
                    if (sleeps % 10 == 0) {
                        logger.info(" reducer is sleeping");
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    goon = false;
                    break;
                }
            }
            if (!pairlist.hasNext() && !pool.hasJobs()) {
                goon = false;
                break; //continue; //break?
            }
            if (set) {
                set = false;
                pool.notIdle();
            }

            pair = pairlist.getNext();
            /*
             * send pair to client, receive H
             */
            if (logger.isDebugEnabled()) {
                logger.debug("send pair = " + pair);
                logger.info("theList keys " + theList.keySet());
            }
            if (logger.isDebugEnabled()) {
                logger.info("inWork " + pairlist.inWork());
            }
            GBSPTransportMess msg = null;
            if (pair != null) {
                msg = new GBSPTransportMessPairIndex(pair);
            } else {
                msg = new GBSPTransportMess(); //End();
                // goon ?= false;
            }
            try {
                pairChannel.send(msg);
            } catch (IOException e) {
                e.printStackTrace();
                goon = false;
                break;
            }
            // use idle time to update pairlist
            pairlist.update();
            //logger.debug("#distributed list = "+theList.size());
            //logger.debug("receive H polynomial");
            Object rh = null;
            try {
                rh = pairChannel.receive();
            } catch (IOException e) {
                e.printStackTrace();
                goon = false;
                break;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                goon = false;
                break;
            }
            if (logger.isDebugEnabled()) {
                logger.info("received H polynomial rh = " + rh);
            }
            if (rh == null) {
                if (pair != null) {
                    polIndex = pairlist.record(pair, null);
                    //pair.setZero();
                }
                pairlist.update();
            } else if (rh instanceof GBSPTransportMessPoly) {
                // update pair list
                red++;
                H = ((GBSPTransportMessPoly<C>) rh).pol;
                //logger.info("H = " + H);
                if (H == null) {
                    if (pair != null) {
                        polIndex = pairlist.record(pair, H);
                        //pair.setZero();
                    }
                    pairlist.update();
                } else {
                    if (H.isZERO()) {
                        polIndex = pairlist.record(pair, H);
                        //pair.setZero();
                    } else {
                        if (H.isONE()) {
                            // pool.allIdle();
                            pairlist.putOne();
                            theList.put(new Integer(0), H);
                            goon = false;
                            //break;
                        } else {
                            polIndex = pairlist.record(pair, H);
                            // not correct:
                            // polIndex = pairlist.getList().size(); 
                            // pairlist.update();
                            // polIndex = pairlist.put( H );
                            // use putWait ? but still not all distributed
                            theList.put(new Integer(polIndex), H);
                        }
                    }
                }
            } else {
                if (pair != null) {
                    polIndex = pairlist.record(pair, null);
                    //pair.setZero();
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("invalid message " + rh);
                }
            }
        }
        logger.info("terminated, done " + red + " reductions");

        /*
         * send end mark to client
         */
        logger.debug("send end");
        try {
            pairChannel.send(new GBSPTransportMessEnd());
        } catch (IOException e) {
            if (logger.isDebugEnabled()) {
                e.printStackTrace();
            }
        }
        pool.beIdle();
        pairChannel.close();
    }

}


/**
 * Distributed GB transport message.
 */

class GBSPTransportMess implements Serializable {


    /**
     * toString.
     */
    @Override
    public String toString() {
        return "" + this.getClass().getName();
    }
}


/**
 * Distributed GB transport message for requests.
 */

class GBSPTransportMessReq extends GBSPTransportMess {


    public GBSPTransportMessReq() {
    }
}


/**
 * Distributed GB transport message for termination.
 */

class GBSPTransportMessEnd extends GBSPTransportMess {


    public GBSPTransportMessEnd() {
    }
}


/**
 * Distributed GB transport message for polynomial.
 */

class GBSPTransportMessPoly<C extends RingElem<C>> extends GBSPTransportMess {


    /**
     * The polynomial for transport.
     */
    public final GenPolynomial<C> pol;


    /**
     * GBSPTransportMessPoly.
     * @param p polynomial to transfered.
     */
    public GBSPTransportMessPoly(GenPolynomial<C> p) {
        this.pol = p;
    }


    /**
     * toString.
     */
    @Override
    public String toString() {
        return super.toString() + "( " + pol + " )";
    }
}


/**
 * Distributed GB transport message for pairs.
 */

class GBSPTransportMessPair<C extends RingElem<C>> extends GBSPTransportMess {


    public final CriticalPair<C> pair;


    /**
     * GBSPTransportMessPair.
     * @param p pair for transfer.
     */
    public GBSPTransportMessPair(CriticalPair<C> p) {
        this.pair = p;
    }


    /**
     * toString.
     */
    @Override
    public String toString() {
        return super.toString() + "( " + pair + " )";
    }
}


/**
 * Distributed GB transport message for index pairs.
 */

class GBSPTransportMessPairIndex extends GBSPTransportMess {


    public final Integer i;


    public final Integer j;


    /**
     * GBSPTransportMessPairIndex.
     * @param p pair for transport.
     */
    public GBSPTransportMessPairIndex(CriticalPair p) {
        if (p == null) {
            throw new NullPointerException("pair may not be null");
        }
        this.i = new Integer(p.i);
        this.j = new Integer(p.j);
    }


    /**
     * GBSPTransportMessPairIndex.
     * @param i first index.
     * @param j second index.
     */
    public GBSPTransportMessPairIndex(int i, int j) {
        this.i = new Integer(i);
        this.j = new Integer(j);
    }


    /**
     * GBSPTransportMessPairIndex.
     * @param i first index.
     * @param j second index.
     */
    public GBSPTransportMessPairIndex(Integer i, Integer j) {
        this.i = i;
        this.j = j;
    }


    /**
     * toString.
     */
    @Override
    public String toString() {
        return super.toString() + "( " + i + "," + j + " )";
    }
}


/**
 * Distributed clients reducing worker threads.
 */

class ReducerClientSeqPair<C extends RingElem<C>> implements Runnable {


    private final SocketChannel pairChannel;


    private final DistHashTable<Integer, GenPolynomial<C>> theList;


    private final ReductionPar<C> red;


    private static final Logger logger = Logger.getLogger(ReducerClientSeqPair.class);


    ReducerClientSeqPair(SocketChannel pc, DistHashTable<Integer, GenPolynomial<C>> dl) {
        pairChannel = pc;
        theList = dl;
        red = new ReductionPar<C>();
    }


    public void run() {
        if (logger.isDebugEnabled()) {
            logger.debug("pairChannel = " + pairChannel + "reducer client running");
        }
        CriticalPair<C> pair = null;
        GenPolynomial<C> pi;
        GenPolynomial<C> pj;
        GenPolynomial<C> S;
        GenPolynomial<C> H = null;
        //boolean set = false;
        boolean goon = true;
        int reduction = 0;
        //int sleeps = 0;
        Integer pix;
        Integer pjx;

        while (goon) {
            /* protocol:
             * request pair, process pair, send result
             */
            // pair = (Pair) pairlist.removeNext();
            Object req = new GBSPTransportMessReq();
            if (logger.isDebugEnabled()) {
                logger.debug("send request = " + req);
            }
            try {
                pairChannel.send(req);
            } catch (IOException e) {
                goon = false;
                e.printStackTrace();
                break;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("receive pair, goon = " + goon);
            }
            Object pp = null;
            try {
                pp = pairChannel.receive();
            } catch (IOException e) {
                goon = false;
                if (logger.isDebugEnabled()) {
                    e.printStackTrace();
                }
                break;
            } catch (ClassNotFoundException e) {
                goon = false;
                e.printStackTrace();
            }
            if (logger.isDebugEnabled()) {
                logger.info("received pair = " + pp);
            }
            H = null;
            if (pp == null) { // should not happen
                //logger.debug("received pair = " + pp);
                continue;
            }
            if (pp instanceof GBSPTransportMessEnd) {
                goon = false;
                continue;
            }
            if (pp instanceof GBSPTransportMessPair || pp instanceof GBSPTransportMessPairIndex) {
                pi = pj = null;
                if (pp instanceof GBSPTransportMessPair) {
                    pair = ((GBSPTransportMessPair<C>) pp).pair;
                    if (pair != null) {
                        pi = pair.pi;
                        pj = pair.pj;
                        //logger.debug("pair: pix = " + pair.i 
                        //               + ", pjx = " + pair.j);
                    }
                }
                if (pp instanceof GBSPTransportMessPairIndex) {
                    pix = ((GBSPTransportMessPairIndex) pp).i;
                    pjx = ((GBSPTransportMessPairIndex) pp).j;
                    //logger.info("waiting for pix = " + pix); 
                    pi = theList.getWait(pix);
                    //logger.info("waiting for pjx = " + pjx); 
                    pj = theList.getWait(pjx);
                    //logger.info("pix = " + pix + ", pjx = " +pjx);
                }

                if (logger.isDebugEnabled()) {
                    logger.info("pi = " + pi.leadingExpVector() + ", pj = " + pj.leadingExpVector());
                }
                if (pi != null && pj != null) {
                    S = red.SPolynomial(pi, pj);
                    //System.out.println("S   = " + S);
                    if (S.isZERO()) {
                        // pair.setZero(); does not work in dist
                        H = S;
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("ht(S) = " + S.leadingExpVector());
                        }
                        H = red.normalform(theList, S);
                        reduction++;
                        if (H.isZERO()) {
                            // pair.setZero(); does not work in dist
                        } else {
                            H = H.monic();
                            if (logger.isDebugEnabled()) {
                                logger.info("ht(H) = " + H.leadingExpVector());
                            }
                        }
                    }
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("invalid message = " + pp);
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // send H or must send null
            //logger.debug("#distributed list = "+theList.size());
            if (logger.isDebugEnabled()) {
                logger.info("send H polynomial = " + H);
            }
            try {
                pairChannel.send(new GBSPTransportMessPoly<C>(H));
            } catch (IOException e) {
                goon = false;
                e.printStackTrace();
                break;
            }
        }
        logger.info("terminated, done " + reduction + " reductions");
        pairChannel.close();
    }
}


/**
 * Distributed server reducing worker threads for minimal GB Not jet distributed
 * but threaded.
 */

class MiReducerServerSeqPair<C extends RingElem<C>> implements Runnable {


    private final List<GenPolynomial<C>> G;


    private GenPolynomial<C> H;


    private final Semaphore done = new Semaphore(0);


    private final Reduction<C> red;


    private static final Logger logger = Logger.getLogger(MiReducerServerSeqPair.class);


    MiReducerServerSeqPair(List<GenPolynomial<C>> G, GenPolynomial<C> p) {
        this.G = G;
        H = p;
        red = new ReductionPar<C>();
    }


    /**
     * getNF. Blocks until the normal form is computed.
     * @return the computed normal form.
     */
    public GenPolynomial<C> getNF() {
        try {
            done.acquire(); //done.P();
        } catch (InterruptedException e) {
        }
        return H;
    }


    public void run() {
        if (logger.isDebugEnabled()) {
            logger.debug("ht(H) = " + H.leadingExpVector());
        }
        H = red.normalform(G, H); //mod
        done.release(); //done.V();
        if (logger.isDebugEnabled()) {
            logger.debug("ht(H) = " + H.leadingExpVector());
        }
        // H = H.monic();
    }
}


/**
 * Distributed clients reducing worker threads for minimal GB. Not jet used.
 */

class MiReducerClientSeqPair<C extends RingElem<C>> implements Runnable {


    private final List<GenPolynomial<C>> G;


    private GenPolynomial<C> H;


    private final Reduction<C> red;


    private final Semaphore done = new Semaphore(0);


    private static final Logger logger = Logger.getLogger(MiReducerClientSeqPair.class);


    MiReducerClientSeqPair(List<GenPolynomial<C>> G, GenPolynomial<C> p) {
        this.G = G;
        H = p;
        red = new ReductionPar<C>();
    }


    /**
     * getNF. Blocks until the normal form is computed.
     * @return the computed normal form.
     */
    public GenPolynomial<C> getNF() {
        try {
            done.acquire(); //done.P();
        } catch (InterruptedException u) {
            Thread.currentThread().interrupt();
        }
        return H;
    }


    public void run() {
        if (logger.isDebugEnabled()) {
            logger.debug("ht(H) = " + H.leadingExpVector());
        }
        H = red.normalform(G, H); // mod
        done.release(); //done.V();
        if (logger.isDebugEnabled()) {
            logger.debug("ht(H) = " + H.leadingExpVector());
        }
        // H = H.monic();
    }
}
