/* mplrs.h: header for mplrs.c
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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Author: Charles Jordan skip@ist.hokudai.ac.jp
Based on plrs by Gary Roumanis
Initial lrs Author: David Avis avis@cs.mcgill.ca
*/

#ifndef MPLRSH
#define MPLRSH 1

#include "lrslib.h"

#include <mpi.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#define USAGE "Usage is: \n mpirun -np <number of processes> mplrs <infile> <outfile> \n or \n mpirun -np <number of processes> mplrs <infile> <outfile> -id <initial depth> -maxc <maxcobases> -maxd <depth> -lmin <int> -lmax <int> -scale <int> -hist <file> -temp <prefix> -freq <file> -stop <stopfile> -checkp <checkpoint file> -restart <checkpoint file> -time <seconds>"

/* Default values for options. */
#define DEF_LMIN 3	/* default -lmin  */
#define DEF_LMAX -1	/* default -lmax  */
#define DEF_ID   2	/* default -id    */
#define DEF_MAXD 0	/* default -maxd  */
#define DEF_MAXC 50	/* default -maxc  */

#define DEF_TEMP   "/tmp/" /* default prefix for temporary files
			 * use /a/b to get files /a/bfilename,
			 * use /a/b/ to get files /a/b/filename
			 */
#define DEF_INPUT  NULL	/* default input filename (or NULL)       */
#define DEF_OUTPUT NULL	/* default output filename (or NULL)	  */
#define DEF_HIST   NULL	/* default histogram filename (or NULL)   */
#define DEF_RESTART NULL/* default restart filename (or NULL)	  */
#define DEF_FREQ   NULL /* default sub-problem size filename (NULL) */
#define DEF_CHECKP NULL	/* default checkpoint filename (or NULL)  */
#define DEF_STOP   NULL	/* default stop-signal filename (or NULL) */

#define DEF_SCALEC 100	/* default multiplicative scaling factor for maxc,
			 * used when L is too large (controlled by lmax) */

/* singly linked list */
typedef struct slist {
        void *data;
        slist *next;
} slist;

typedef struct outlist {
	char *type;
	char *data;
	outlist *next;
} outlist;

/* A linked-list of buffers for MPI communications.
 * req[0...count-1] correspond to buf[0...count-1]
 *
 * When req[i] completes, should free buf[i].
 * When all reqs complete, should free buf, req, tags,sizes,types.
 */
typedef struct msgbuf {
	MPI_Request *req;
	void **buf;
	int count;
	int target;
	int data;   /* optional, use yourself if needed for something */
	int queue;  /* if 1, send items 1...count after 0 has completed */
	/* queue pointers must be NULL or something free()able */
	int *tags;  /* tags to use on items 1...count if queued */
	int *sizes; /* sizes of sends if queued */
	MPI_Datatype *types; /* types of sends if queued */

	msgbuf *next;
} msgbuf;

/* A structure containing the state of this process.
 * Each process has one.
 */
typedef struct mplrsv {
	/* MPI communication buffers */
	msgbuf *outgoing;
	slist *cobasis_list;

	int caughtsig; /* flag for catching a signal */
	/* counts */
	unsigned long rays;
	unsigned long vertices;
	unsigned long bases;
	unsigned long facets;
	unsigned long intvertices;
	lrs_mp Tnum, Tden, tN, tD, Vnum, Vden;

	struct timeval start, end;

	/* MPI info */
	int rank; 			/* my rank */
	int size;			/* number of MPI processes */
	int my_tag;			/* to distinguish MPI sends */
	char host[MPI_MAX_PROCESSOR_NAME]; /* name of this host */

	/* output_list */
	outlist *output_list;

	/* for convenience */
	char *tfn_prefix;
	char *tfn;
	FILE *tfile;
	int initializing;		/* in phase 1? */

	char *input_filename;		/* input filename */
	char *input;			/* buffer for contents of input file */
} mplrsv;

/* A structure for variables only the master needs */
typedef struct masterv {
	slist *cobasis_list;		/* list of work to do (L) */
	unsigned int tot_L;		/* total size of L (total # jobs) */
	unsigned int size_L;		/* current size of L (for histograms
					 * and scaling)
					 */
	unsigned int num_empty;		/* number of times L became empty */
	unsigned int num_producers; 	/* number of producers running */
	unsigned int *act_producers;    /* whether each producer owes us
					 * remaining bases message.
					 * Needed only for histograms.
					 */
	unsigned int live_workers;      /* number that haven't exited */
	/* MPI communication buffers */
	int *workin; 			/* incoming messages from producers
					   desiring work */
	MPI_Request *mworkers;		/* MPI_Requests for these messages */
	msgbuf *incoming;		/* incoming cobases from producers */
	MPI_Request *sigcheck;		/* MPI_Requests for reporting signals*/

	int checkpointing;		/* are we checkpointing now? */

	/* user options */
	unsigned int lmin;		/* option -lmin */
	unsigned int lmax;		/* option -lmax */
	unsigned int scalec;		/* option -scale*/
	unsigned int initdepth;		/* option -id   */
	unsigned int maxdepth;		/* option -maxd */
	unsigned int maxcobases;	/* option -maxc */
	unsigned int time_limit;	/* option -time */

	/* files */
	char *hist_filename;		/*histogram filename (or NULL)*/
	FILE *hist;
	int doing_histogram;		/* are we doing a histogram? */
	char *freq_filename;		/*are we outputting sub-problem sizes?*/
	FILE *freq;
	char *restart_filename;		/* restart from a checkpoint */
	FILE *restart;	
	char *checkp_filename;		/* filename to save checkpoint*/
	FILE *checkp;
	char *stop_filename;		/* option -stop */
	FILE *stop;
	FILE *input;
} masterv;

/* A structure for variables only the consumer needs */
typedef struct consumerv {
	/* MPI communication buffers */
	  /* for headers */
	MPI_Request *prodreq;    	/* consumer keeps an open request
					 * for each producer and master
					 */
	int *prodibf;			/* and two ints as a receive buffer */
	  /* for content */
	msgbuf *incoming; 		/* incoming MPI communication buffers */

	/* output */
	char *output_filename;		/* output filename (or NULL) */
	FILE *output;			/* output file (NULL for stdout) */

	/* status */
	unsigned int num_producers;	/* number of producers still going */
	unsigned int checkpoint;	/* do we want to checkpoint now? */

	/* other */
	int waiting_initial;		/* waiting for initial producer,
					 * hold output until after 'begin'
					 */
} consumerv;

/* MASTER and CONSUMER and INITIAL must be different */
#define MASTER 0   /* the MPI process that becomes master */
#define CONSUMER 1 /* the MPI process that becomes consumer */
#define INITIAL 2

#define CHECKFLAG -3	/* must be distinct negative values */
#define RESTARTFLAG -4

/* define DEBUG to get many mplrs debug messages */
#ifdef DEBUG
#define mprintf(a) printf a
#else
#define mprintf(a)
#endif
/* define DEBUG2 to get even more */
#ifdef DEBUG2
#define mprintf2(a) printf a
#else
#define mprintf2(a)
#endif
/* define DEBUG3 to get lots */
#ifdef DEBUG3
#define mprintf3(a) printf a
#else
#define mprintf3(a)
#endif

/* function prototypes */
void mplrs_init(int, char **);
void mplrs_caughtsig(int);
void master_sendfile(void);
void mplrs_initstrucs();
void mplrs_commandline(int, char **);
void mplrs_initfiles(void);
void bad_args(void);
int mplrs_fallback(void);

int mplrs_master(void);
void send_work(int, int);
void recv_producer_lists(void);
void process_returned_cobases(msgbuf *);
void setparams(int *);
void check_stop(void);
void master_checksigs(void);
void master_restart(void);
void master_checkpoint(void);
void master_checkpointfile(void);
void master_checkpointconsumer(void);
void print_histogram(timeval *, timeval *);

int mplrs_worker(void);
void clean_outgoing_buffers(void); /* shared with master */
void do_work(const int *, const char *);
void process_output(void);
void send_output(int, char *);
void process_cobasis(const char *);
inline slist *addlist(slist *, void *);
void return_unfinished_cobases(void);
char *append_out(char *, int *, const char *);
int mplrs_worker_finished(void);

int mplrs_consumer(void);
void consumer_start_incoming(void);
msgbuf *consumer_queue_incoming(int *, int);
void consumer_proc_messages(void);
int consumer_checkpoint(void);
inline int outgoing_msgbuf_completed(msgbuf *);
inline void free_msgbuf(msgbuf *);
outlist *reverse_list(outlist*);
void send_master_stats(void);
void recv_master_stats(void);
void send_counting_stats(int);
void recv_counting_stats(int);
void initial_print(void);
inline void phase1_print(void);
void final_print(void);
inline char *dupstr(const char *str);

#endif /* MPLRSH */
