/*
 * $Id$
 */

package edu.jas.gb;


import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicInteger;

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.DistThreadPool;
import edu.jas.util.RemoteExecutable;
import edu.jas.util.SocketChannel;
import edu.jas.util.TaggedSocketChannel;
import edu.jas.util.Terminator;
import edu.jas.util.ThreadPool;


/**
 * Groebner Base distributed hybrid algorithm. Implements a distributed memory
 * with multi-core CPUs parallel version of Groebner bases with executable
 * channels. Using pairlist class, distributed multi-threaded tasks do
 * reduction, one communication channel per remote node.
 * @param  coefficient type
 * @author Heinz Kredel
 */

public class GroebnerBaseDistributedHybridEC> extends GroebnerBaseAbstract {


    public static final Logger logger = Logger.getLogger(GroebnerBaseDistributedHybridEC.class);


    public final boolean debug = logger.isDebugEnabled();


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


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


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


    /**
     * Default number of threads per compute node.
     */
    protected static final int DEFAULT_THREADS_PER_NODE = 1;


    /**
     * Pool of threads to use.
     */
    //protected final ExecutorService pool; // not for single node tests
    protected transient final ThreadPool pool;


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


    /**
     * Default distributed hash table server port.
     */
    protected final int DHT_PORT;


    /**
     * machine file to use.
     */
    protected final String mfile;


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


    /**
     * Distributed thread pool to use.
     */
    private final transient DistThreadPool dtp;


    /**
     * Distributed hash table server to use.
     */
    private final transient DistHashTableServer dhts;


    /**
     * Message tag for pairs.
     */
    public static final Integer pairTag = Integer.valueOf(1);


    /**
     * Message tag for results.
     */
    public static final Integer resultTag = Integer.valueOf(2);


    /**
     * Message tag for acknowledgments.
     */
    public static final Integer ackTag = Integer.valueOf(3);


    /**
     * Constructor.
     * @param mfile name of the machine file.
     */
    public GroebnerBaseDistributedHybridEC(String mfile) {
        this(mfile, DEFAULT_THREADS, DEFAULT_PORT);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads) {
        this(mfile, threads, new ThreadPool(threads), DEFAULT_PORT);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, int port) {
        this(mfile, threads, new ThreadPool(threads), port);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param threadsPerNode threads per node to use.
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, int threadsPerNode, int port) {
        this(mfile, threads, threadsPerNode, new ThreadPool(threads), port);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param pool ThreadPool to use.
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, ThreadPool pool, int port) {
        this(mfile, threads, DEFAULT_THREADS_PER_NODE, pool, port);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param threadsPerNode threads per node to use.
     * @param pl pair selection strategy
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, int threadsPerNode, PairList pl,
                    int port) {
        this(mfile, threads, threadsPerNode, new ThreadPool(threads), pl, port);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param threadsPerNode threads per node to use.
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, int threadsPerNode, ThreadPool pool,
                    int port) {
        this(mfile, threads, threadsPerNode, pool, new OrderedPairlist(), port);
    }


    /**
     * Constructor.
     * @param mfile name of the machine file.
     * @param threads number of threads to use.
     * @param threadsPerNode threads per node to use.
     * @param pool ThreadPool to use.
     * @param pl pair selection strategy
     * @param port server port to use.
     */
    public GroebnerBaseDistributedHybridEC(String mfile, int threads, int threadsPerNode, ThreadPool pool,
                    PairList pl, int port) {
        super(new ReductionPar(), pl);
        this.threads = threads;
        if (mfile == null || mfile.length() == 0) {
            this.mfile = "../util/machines"; // contains localhost
        } else {
            this.mfile = mfile;
        }
        if (threads < 1) {
            threads = 1;
        }
        this.threadsPerNode = threadsPerNode;
        if (pool == null) {
            pool = new ThreadPool(threads);
        }
        this.pool = pool;
        this.port = port;
        logger.info("machine file " + mfile + ", port = " + port);
        this.dtp = new DistThreadPool(this.threads, this.mfile);
        logger.info("running " + dtp);
        this.DHT_PORT = this.dtp.getEC().getMasterPort() + 100;
        this.dhts = new DistHashTableServer(this.DHT_PORT);
        this.dhts.init();
        logger.info("running " + dhts);
    }


    /**
     * Cleanup and terminate ThreadPool.
     */
    @Override
    public void terminate() {
        terminate(true);
    }


    /**
     * Terminates the distributed thread pools.
     * @param shutDown true, if shut-down of the remote executable servers is
     *            requested, false, if remote executable servers stay alive.
     */
    public void terminate(boolean shutDown) {
        pool.terminate();
        dtp.terminate(shutDown);
        logger.debug("dhts.terminate()");
        dhts.terminate();
    }


    /**
     * Distributed Groebner base.
     * @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> GB(int modv, List> F) {
        String master = dtp.getEC().getMasterHost();
        //int port = dtp.getEC().getMasterPort(); // wrong port
        GBHybridExerClient gbc = new GBHybridExerClient(master, threadsPerNode, port, DHT_PORT);
        for (int i = 0; i < threads; i++) {
            // schedule remote clients
            dtp.addJob(gbc);
        }
        // run master
        List> G = GBMaster(modv, F);

        return G;
    }


    /**
     * Distributed hybrid Groebner base.
     * @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> GBMaster(int modv, List> F) {
        long t = System.currentTimeMillis();
        ChannelFactory cf = new ChannelFactory(port);
        cf.init();

        GenPolynomial p;
        List> G = new ArrayList>();
        PairList pairlist = null;
        boolean oneInGB = false;
        int l = F.size();
        int unused;
        ListIterator> 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 OrderedPairlist(modv, p.ring);
                    pairlist = strategy.create(modv, p.ring);
                    if (!p.ring.coFac.isField()) {
                        throw new IllegalArgumentException("coefficients not from a field");
                    }
                }
                // 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.info("start " + pairlist); 
        DistHashTable> theList = new DistHashTable>(
                        "localhost", DHT_PORT);
        theList.init();
        List> al = pairlist.getList();
        for (int i = 0; i < al.size(); i++) {
            // no wait required
            GenPolynomial nn = theList.put(Integer.valueOf(i), al.get(i));
            if (nn != null) {
                logger.info("double polynomials " + i + ", nn = " + nn + ", al(i) = " + al.get(i));
            }
        }

        Terminator finner = new Terminator(threads * threadsPerNode);
        HybridReducerServerEC R;
        logger.info("using pool = " + pool);
        for (int i = 0; i < threads; i++) {
            R = new HybridReducerServerEC(threadsPerNode, finner, cf, theList, pairlist);
            pool.addJob(R);
            //logger.info("server submitted " + R);
        }
        logger.info("main loop waiting " + finner);
        finner.waitDone();
        int ps = theList.size();
        logger.info("#distributed list = " + ps);
        // make sure all polynomials arrived: not needed in master
        // G = (ArrayList)theList.values();
        G = pairlist.getList();
        if (ps != G.size()) {
            logger.info("#distributed list = " + theList.size() + " #pairlist list = " + G.size());
        }
        for (GenPolynomial q : theList.getValueList()) {
            if (q != null && !q.isZERO()) {
                logger.debug("final q = " + q.leadingExpVector());
            }
        }
        logger.debug("distributed list end");
        long time = System.currentTimeMillis();
        List> Gp;
        Gp = minimalGB(G); // not jet distributed but threaded
        time = System.currentTimeMillis() - time;
        logger.debug("parallel gbmi time = " + time);
        G = Gp;
        logger.debug("server cf.terminate()");
        cf.terminate();
        // no more required // 
        //logger.info("server not pool.terminate() " + pool);
        //pool.terminate();
        logger.debug("server theList.terminate() " + theList.size());
        theList.clear();
        theList.terminate();
        t = System.currentTimeMillis() - t;
        logger.info("server GB end, time = " + t + ", " + pairlist.toString());
        return G;
    }


    /**
     * GB distributed client part.
     * @param host the server runs on.
     * @param port the server runs.
     * @param dhtport of the DHT server.
     * @throws IOException
     */
    public static > void clientPart(String host, int threadsPerNode, int port,
                    int dhtport) throws IOException {
        ChannelFactory cf = new ChannelFactory(port + 10); // != port for localhost
        cf.init();
        logger.info("clientPart connecting to " + host + ", port = " + port + ", dhtport = " + dhtport);
        SocketChannel channel = cf.getChannel(host, port);
        TaggedSocketChannel pairChannel = new TaggedSocketChannel(channel);
        pairChannel.init();

        DistHashTable> theList = new DistHashTable>(host,
                        dhtport);
        theList.init();

        ThreadPool pool = new ThreadPool(threadsPerNode);
        logger.info("client using pool = " + pool);
        for (int i = 0; i < threadsPerNode; i++) {
            HybridReducerClientEC Rr = new HybridReducerClientEC(threadsPerNode, pairChannel, i,
                            theList);
            pool.addJob(Rr);
        }
        logger.debug("clients submitted");

        pool.terminate();
        logger.debug("client pool.terminate()");

        pairChannel.close();
        logger.debug("client pairChannel.close()");

        //master only: theList.clear();
        theList.terminate();
        cf.terminate();
        logger.info("client cf.terminate()");

        channel.close();
        logger.info("client channel.close()");
        return;
    }


    /**
     * Minimal ordered groebner basis.
     * @param Fp a Groebner base.
     * @return a reduced Groebner base of Fp.
     */
    @Override
    public List> minimalGB(List> Fp) {
        GenPolynomial a;
        ArrayList> G;
        G = new ArrayList>(Fp.size());
        ListIterator> 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 p;
        ArrayList> F;
        F = new ArrayList>(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

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

}


/**
 * Distributed server reducing worker proxy threads.
 * @param  coefficient type
 */
class HybridReducerServerEC> implements Runnable {


    public static final Logger logger = Logger.getLogger(HybridReducerServerEC.class);


    public final boolean debug = logger.isDebugEnabled();


    private final Terminator finner;


    private final ChannelFactory cf;


    private TaggedSocketChannel pairChannel;


    private final DistHashTable> theList;


    private final PairList pairlist;


    private final int threadsPerNode;


    /**
     * Message tag for pairs.
     */
    public final Integer pairTag = GroebnerBaseDistributedHybrid.pairTag;


    /**
     * Message tag for results.
     */
    public final Integer resultTag = GroebnerBaseDistributedHybrid.resultTag;


    /**
     * Message tag for acknowledgments.
     */
    public final Integer ackTag = GroebnerBaseDistributedHybrid.ackTag;


    /**
     * Constructor.
     * @param tpn number of threads per node
     * @param fin terminator
     * @param cf channel factory
     * @param dl distributed hash table
     * @param L ordered pair list
     */
    HybridReducerServerEC(int tpn, Terminator fin, ChannelFactory cf,
                    DistHashTable> dl, PairList L) {
        threadsPerNode = tpn;
        finner = fin;
        this.cf = cf;
        theList = dl;
        pairlist = L;
        //logger.info("reducer server created " + this);
    }


    /**
     * Work loop.
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        logger.info("reducer server running with " + cf);
        SocketChannel channel = null;
        try {
            channel = cf.getChannel();
            pairChannel = new TaggedSocketChannel(channel);
            pairChannel.init();
        } catch (InterruptedException e) {
            logger.debug("get pair channel interrupted");
            e.printStackTrace();
            return;
        }
        if (debug) {
            logger.info("pairChannel   = " + pairChannel);
        }
        // record idle remote workers (minus one?)
        //finner.beIdle(threadsPerNode-1);
        finner.initIdle(threadsPerNode);
        AtomicInteger active = new AtomicInteger(0);

        // start receiver
        HybridReducerReceiverEC receiver = new HybridReducerReceiverEC(threadsPerNode, finner, active,
                        pairChannel, theList, pairlist);
        receiver.start();

        Pair pair;
        //boolean set = false;
        boolean goon = true;
        //int polIndex = -1;
        int red = 0;
        int sleeps = 0;

        // while more requests
        while (goon) {
            // receive request if thread is reported incactive
            logger.debug("receive request");
            Object req = null;
            try {
                req = pairChannel.receive(pairTag);
            } catch (InterruptedException e) {
                goon = false;
                e.printStackTrace();
            } catch (IOException e) {
                goon = false;
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                goon = false;
                e.printStackTrace();
            }
            //logger.info("received request, req = " + req);
            if (req == null) {
                goon = false;
                break;
            }
            if (!(req instanceof GBTransportMessReq)) {
                goon = false;
                break;
            }

            // find pair and manage termination status
            logger.debug("find pair");
            while (!pairlist.hasNext()) { // wait
                if (!finner.hasJobs() && !pairlist.hasNext()) {
                    goon = false;
                    break;
                }
                try {
                    sleeps++;
                    //if (sleeps % 10 == 0) {
                    logger.info("waiting for reducers, remaining = " + finner);
                    //}
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    goon = false;
                    break;
                }
            }
            if (Thread.currentThread().isInterrupted()) {
                goon = false;
                break;
            }
            if (!pairlist.hasNext() && !finner.hasJobs()) {
                logger.info("termination detection: no pairs and no jobs left");
                goon = false;
                break; //continue; //break?
            }
            finner.notIdle(); // before pairlist get!!
            pair = pairlist.removeNext();
            // send pair to client, even if null
            if (debug) {
                logger.info("active count = " + active.get());
                logger.info("send pair = " + pair);
            }
            GBTransportMess msg = null;
            if (pair != null) {
                msg = new GBTransportMessPairIndex(pair); //,pairlist.size()-1); // size-1
            } else {
                msg = new GBTransportMess(); // not End(); at this time
                // goon ?= false;
            }
            try {
                red++;
                pairChannel.send(pairTag, msg);
                int a = active.getAndIncrement();
            } catch (IOException e) {
                e.printStackTrace();
                goon = false;
                break;
            }
            //logger.debug("#distributed list = " + theList.size());
        }
        logger.info("terminated, send " + red + " reduction pairs");

        /*
         * send end mark to clients
         */
        logger.debug("send end");
        try {
            for (int i = 0; i < threadsPerNode; i++) { // -1
                //do not wait: Object rq = pairChannel.receive(pairTag);
                pairChannel.send(pairTag, new GBTransportMessEnd());
            }
            // send also end to receiver
            pairChannel.send(resultTag, new GBTransportMessEnd());
            //beware of race condition 
        } catch (IOException e) {
            if (logger.isDebugEnabled()) {
                e.printStackTrace();
            }
        }
        receiver.terminate();

        int d = active.get();
        if (d > 0) {
            logger.info("remaining active tasks = " + d);
        }
        //logger.info("terminated, send " + red + " reduction pairs");
        pairChannel.close();
        logger.debug("redServ pairChannel.close()");
        finner.release();

        channel.close();
        logger.info("redServ channel.close()");
    }
}


/**
 * Distributed server receiving worker thread.
 * @param  coefficient type
 */
class HybridReducerReceiverEC> extends Thread {


    public static final Logger logger = Logger.getLogger(HybridReducerReceiverEC.class);


    public final boolean debug = logger.isDebugEnabled();


    private final DistHashTable> theList;


    private final PairList pairlist;


    private final TaggedSocketChannel pairChannel;


    private final Terminator finner;


    private final int threadsPerNode;


    private final AtomicInteger active;


    private volatile boolean goon;


    /**
     * Message tag for pairs.
     */
    public final Integer pairTag = GroebnerBaseDistributedHybrid.pairTag;


    /**
     * Message tag for results.
     */
    public final Integer resultTag = GroebnerBaseDistributedHybrid.resultTag;


    /**
     * Message tag for acknowledgments.
     */
    public final Integer ackTag = GroebnerBaseDistributedHybrid.ackTag;


    /**
     * Constructor.
     * @param tpn number of threads per node
     * @param fin terminator
     * @param a active remote tasks count
     * @param pc tagged socket channel
     * @param dl distributed hash table
     * @param L ordered pair list
     */
    HybridReducerReceiverEC(int tpn, Terminator fin, AtomicInteger a, TaggedSocketChannel pc,
                    DistHashTable> dl, PairList L) {
        active = a;
        threadsPerNode = tpn;
        finner = fin;
        pairChannel = pc;
        theList = dl;
        pairlist = L;
        goon = true;
        //logger.info("reducer server created " + this);
    }


    /**
     * Work loop.
     * @see java.lang.Thread#run()
     */
    @Override
    public void run() {
        //Pair pair = null;
        GenPolynomial H = null;
        int red = 0;
        int polIndex = -1;
        //Integer senderId; // obsolete

        // while more requests
        while (goon) {
            // receive request
            logger.debug("receive result");
            //senderId = null;
            Object rh = null;
            try {
                rh = pairChannel.receive(resultTag);
                int i = active.getAndDecrement();
            } catch (InterruptedException e) {
                goon = false;
                //e.printStackTrace();
                //?? finner.initIdle(1);
                break;
            } catch (IOException e) {
                e.printStackTrace();
                goon = false;
                finner.initIdle(1);
                break;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                goon = false;
                finner.initIdle(1);
                break;
            }
            logger.info("received H polynomial");
            if (rh == null) {
                if (this.isInterrupted()) {
                    goon = false;
                    finner.initIdle(1);
                    break;
                }
                //finner.initIdle(1);
            } else if (rh instanceof GBTransportMessEnd) { // should only happen from server
                logger.info("received GBTransportMessEnd");
                goon = false;
                //?? finner.initIdle(1);
                break;
            } else if (rh instanceof GBTransportMessPoly) {
                // update pair list
                red++;
                GBTransportMessPoly mpi = (GBTransportMessPoly) rh;
                H = mpi.pol;
                //senderId = mpi.threadId;
                if (H != null) {
                    if (debug) {
                        logger.info("H = " + H.leadingExpVector());
                    }
                    if (!H.isZERO()) {
                        if (H.isONE()) {
                            // finner.allIdle();
                            polIndex = pairlist.putOne();
                            theList.putWait(Integer.valueOf(polIndex), H);
                            //goon = false; must wait for other clients
                            //finner.initIdle(1);
                            //break;
                        } else {
                            polIndex = pairlist.put(H);
                            // use putWait ? but still not all distributed
                            //GenPolynomial nn = 
                            theList.putWait(Integer.valueOf(polIndex), H);
                        }
                    }
                }
            }
            // only after recording in pairlist !
            finner.initIdle(1);
            try {
                pairChannel.send(ackTag, new GBTransportMess());
                logger.debug("send acknowledgement");
            } catch (IOException e) {
                e.printStackTrace();
                goon = false;
                break;
            }
        } // end while
        goon = false;
        logger.info("terminated, received " + red + " reductions");
    }


    /**
     * Terminate.
     */
    public void terminate() {
        goon = false;
        //this.interrupt();
        try {
            this.join();
        } catch (InterruptedException e) {
            // unfug Thread.currentThread().interrupt();
        }
        logger.debug("HybridReducerReceiver terminated");
    }

}


/**
 * Distributed clients reducing worker threads.
 */
class HybridReducerClientEC> implements Runnable {


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


    public final boolean debug = logger.isDebugEnabled();


    private final TaggedSocketChannel pairChannel;


    private final DistHashTable> theList;


    private final ReductionPar red;


    private final int threadsPerNode;


    /*
     * Identification number for this thread.
     */
    //public final Integer threadId; // obsolete


    /**
     * Message tag for pairs.
     */
    public final Integer pairTag = GroebnerBaseDistributedHybrid.pairTag;


    /**
     * Message tag for results.
     */
    public final Integer resultTag = GroebnerBaseDistributedHybrid.resultTag;


    /**
     * Message tag for acknowledgments.
     */
    public final Integer ackTag = GroebnerBaseDistributedHybrid.ackTag;


    /**
     * Constructor.
     * @param tpn number of threads per node
     * @param tc tagged socket channel
     * @param tid thread identification
     * @param dl distributed hash table
     */
    HybridReducerClientEC(int tpn, TaggedSocketChannel tc, Integer tid,
                    DistHashTable> dl) {
        this.threadsPerNode = tpn;
        pairChannel = tc;
        //threadId = 100 + tid; // keep distinct from other tags
        theList = dl;
        red = new ReductionPar();
    }


    /**
     * Work loop.
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        if (debug) {
            logger.info("pairChannel   = " + pairChannel + " reducer client running");
        }
        Pair pair = null;
        GenPolynomial pi, pj, ps;
        GenPolynomial S;
        GenPolynomial H = null;
        //boolean set = false;
        boolean goon = true;
        boolean doEnd = true;
        int reduction = 0;
        //int sleeps = 0;
        Integer pix, pjx, psx;

        while (goon) {
            /* protocol:
             * request pair, process pair, send result, receive acknowledgment
             */
            // pair = (Pair) pairlist.removeNext();
            Object req = new GBTransportMessReq();
            logger.debug("send request");
            try {
                pairChannel.send(pairTag, req);
            } catch (IOException e) {
                goon = false;
                if (logger.isDebugEnabled()) {
                    e.printStackTrace();
                }
                logger.info("receive pair, IOexception ");
                break;
            }
            logger.debug("receive pair, goon = " + goon);
            doEnd = true;
            Object pp = null;
            try {
                pp = pairChannel.receive(pairTag);
            } catch (InterruptedException e) {
                goon = false;
                e.printStackTrace();
            } catch (IOException e) {
                goon = false;
                if (logger.isDebugEnabled()) {
                    e.printStackTrace();
                }
                break;
            } catch (ClassNotFoundException e) {
                goon = false;
                e.printStackTrace();
            }
            if (debug) {
                logger.info("received pair = " + pp);
            }
            H = null;
            if (pp == null) { // should not happen
                continue;
            }
            if (pp instanceof GBTransportMessEnd) {
                goon = false;
                //doEnd = false;
                continue;
            }
            if (pp instanceof GBTransportMessPair || pp instanceof GBTransportMessPairIndex) {
                pi = pj = ps = null;
                if (pp instanceof GBTransportMessPair) { // obsolet, for tests
                    GBTransportMessPair tmp = (GBTransportMessPair) pp;
                    pair = tmp.pair;
                    if (pair != null) {
                        pi = pair.pi;
                        pj = pair.pj;
                        //logger.debug("pair: pix = " + pair.i 
                        //               + ", pjx = " + pair.j);
                    }
                }
                if (pp instanceof GBTransportMessPairIndex) {
                    GBTransportMessPairIndex tmpi = (GBTransportMessPairIndex) pp;
                    pix = tmpi.i;
                    pjx = tmpi.j;
                    psx = tmpi.s;
                    pi = theList.getWait(pix);
                    pj = theList.getWait(pjx);
                    ps = theList.getWait(psx);
                    //logger.info("pix = " + pix + ", pjx = " + pjx + ", psx = " + psx);
                }
                if (pi != null && pj != null) {
                    S = red.SPolynomial(pi, pj);
                    //logger.info("ht(S) = " + S.leadingExpVector());
                    if (S.isZERO()) {
                        // pair.setZero(); does not work in dist
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("ht(S) = " + S.leadingExpVector());
                        }
                        H = red.normalform(theList, S);
                        //logger.info("ht(H) = " + H.leadingExpVector());
                        reduction++;
                        if (H.isZERO()) {
                            // pair.setZero(); does not work in dist
                        } else {
                            H = H.monic();
                            if (logger.isInfoEnabled()) {
                                logger.info("ht(H) = " + H.leadingExpVector());
                            }
                        }
                    }
                } else {
                    logger.info("pi = " + pi + ", pj = " + pj + ", ps = " + ps);
                }
            }
            if (pp instanceof GBTransportMess) {
                logger.debug("null pair results in null H poly");
            }

            // send H or must send null, if not at end
            if (logger.isDebugEnabled()) {
                logger.debug("#distributed list = " + theList.size());
                logger.debug("send H polynomial = " + H);
            }
            try {
                pairChannel.send(resultTag, new GBTransportMessPoly(H)); //,threadId));
                doEnd = false;
            } catch (IOException e) {
                goon = false;
                e.printStackTrace();
            }
            //logger.info("done send poly message of " + pp);
            try {
                //pp = pairChannel.receive(threadId);
                pp = pairChannel.receive(ackTag);
            } catch (InterruptedException e) {
                goon = false;
                e.printStackTrace();
            } catch (IOException e) {
                goon = false;
                if (logger.isDebugEnabled()) {
                    e.printStackTrace();
                }
                break;
            } catch (ClassNotFoundException e) {
                goon = false;
                e.printStackTrace();
            }
            if (!(pp instanceof GBTransportMess)) {
                logger.error("invalid acknowledgement " + pp);
            }
            logger.info("received acknowledgment ");
        }
        logger.info("terminated, " + reduction + " reductions, " + theList.size() + " polynomials");
        if (doEnd) {
            try {
                pairChannel.send(resultTag, new GBTransportMessEnd());
            } catch (IOException e) {
                //e.printStackTrace();
            }
            logger.debug("terminated, send done");
        }
    }
}


/**
 * Objects of this class are to be send to a ExecutableServer.
 */
class GBHybridExerClient> implements RemoteExecutable {


    String host;


    int port;


    int dhtport;


    int threadsPerNode;


    /**
     * GBHybridExerClient.
     * @param host
     * @param port
     * @param dhtport
     */
    public GBHybridExerClient(String host, int threadsPerNode, int port, int dhtport) {
        this.host = host;
        this.threadsPerNode = threadsPerNode;
        this.port = port;
        this.dhtport = dhtport;
    }


    /**
     * run.
     */
    public void run() {
        try {
            GroebnerBaseDistributedHybridEC. clientPart(host, threadsPerNode, port, dhtport);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * String representation.
     */
    @Override
    public String toString() {
        StringBuffer s = new StringBuffer("GBHybridExerClient(");
        s.append("host=" + host);
        s.append(", threadsPerNode=" + threadsPerNode);
        s.append(", port=" + port);
        s.append(", dhtport=" + dhtport);
        s.append(")");
        return s.toString();
    }

}

 

Ads help maintain this website.