/* 
 * @HEADER
 *
 * ***********************************************************************
 *
 *  Zoltan Toolkit for Load-balancing, Partitioning, Ordering and Coloring
 *                  Copyright 2012 Sandia Corporation
 *
 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
 * the U.S. Government retains certain rights in this software.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the Corporation nor the names of the
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Questions? Contact Karen Devine	kddevin@sandia.gov
 *                    Erik Boman	egboman@sandia.gov
 *
 * ***********************************************************************
 *
 * @HEADER
 */
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// File:      driver.cc                                                     //
// Project:   Local HSFC Ordering                                           //
// Author:    Michael Wolf                                                  //
// Date Started:  11/02/2009                                                //
//                                                                          //
// Description:                                                             //
//              File tests local HSFC ordering for simple test problem      //
//                                                                          //
// $Id: driver.cc 19 2009-12-18 17:52:36Z mmwolf $                           //
//////////////////////////////////////////////////////////////////////////////
#include "Zoltan_config.h"

#ifdef HAVE_MPI
#include <mpi.h>
#endif

#include <iostream>
#include <math.h>
#include <vector>
#include <set>
#include <iterator>
#include <list>
#include <stdlib.h>


extern "C" {
#include <zoltan.h>
#include <zoltan_types.h>
}


#define SUCCESS 0
#define FAIL 1

int locNumObj = 16;   //number of objects locally

unsigned int answers[4][16] = {{12,  8,  9, 13, 14, 15, 11, 10,  6,  7,  3,  2,  1,  5,  4,  0},
                               {28, 24, 25, 29, 30, 31, 27, 26, 22, 23, 19, 18, 17, 21, 20, 16},
		               {44, 40, 41, 45, 46, 47, 43, 42, 38, 39, 35, 34, 33, 37, 36, 32},
		               {60, 56, 57, 61, 62, 63, 59, 58, 54, 55, 51, 50, 49, 53, 52, 48}};

int order(int worldsize, int myrank);
int checkResults(int myrank, ZOLTAN_ID_PTR permGIDs);

//////////////////////////////////////////////////////////////
// Zoltan query functions
//////////////////////////////////////////////////////////////
int zoltNumObjs(void *data,int *ierr);

void zoltGetObjs(void *data, int num_gid_entries, 
                      int num_lid_entries, ZOLTAN_ID_PTR global_ids, 
                      ZOLTAN_ID_PTR local_ids, int wgt_dim, 
		      float *obj_wgts, int *ierr);
int zoltNumGeom(void *data,int *ierr);
void zoltGeom(void *data, int num_gid_entries, int num_lid_entries, ZOLTAN_ID_PTR global_id, 
              ZOLTAN_ID_PTR local_id, double *geom_vec, int *ierr);
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
  float ver;

#ifdef HAVE_MPI
  MPI_Init(&argc,&argv);
#endif

  Zoltan_Initialize(argc,argv,&ver); //initialize Zoltan

  int worldsize, myrank;

#ifdef HAVE_MPI
  MPI_Comm_size(MPI_COMM_WORLD,&worldsize);
  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
#else
  worldsize = 1;
  myrank = 0;
#endif

  if(worldsize!=1 && worldsize!=2 && worldsize!=4)
  {
#ifdef HAVE_MPI
    MPI_Finalize();
#endif

    if(myrank==0)
    {
      std::cout << "WARNING for testPartitionMVInput() with number of processors = " << worldsize << std::endl
	        << "        Number of processes not supported .... (by default) returning success" << std::endl;
    }

    return SUCCESS;
  }

  int retFlag = 0;

  retFlag = order(worldsize,myrank);
 
#ifdef HAVE_MPI
  MPI_Finalize();
#endif

  if (retFlag==0)
  {
    return FAIL;
  }

  return SUCCESS;
}
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
  int order(int worldsize, int myrank)
{
  struct Zoltan_Struct *zz;

  //  sprintf(paramPartitions,"%d",numprocs);

  zz = Zoltan_Create(MPI_COMM_WORLD);


  // General Zoltan Parameters
  Zoltan_Set_Param(zz,"ORDER_METHOD","LOCAL_HSFC");
  //Zoltan_Set_Param(zz,"ORDER_START_INDEX","1");


  // register query functions
  Zoltan_Set_Fn(zz, ZOLTAN_NUM_OBJ_FN_TYPE, (void (*)()) zoltNumObjs, 0);
  Zoltan_Set_Fn(zz, ZOLTAN_OBJ_LIST_FN_TYPE, (void (*)()) zoltGetObjs, 0);
  Zoltan_Set_Fn(zz, ZOLTAN_NUM_GEOM_FN_TYPE, (void (*)()) zoltNumGeom, 0);
  Zoltan_Set_Fn(zz, ZOLTAN_GEOM_FN_TYPE, (void (*)()) zoltGeom, 0);

  int numGidEntries=1;

  ZOLTAN_ID_PTR global_ids=0;
  ZOLTAN_ID_PTR permGIDs = 0;

  global_ids = new ZOLTAN_ID_TYPE[locNumObj];
  permGIDs = new ZOLTAN_ID_TYPE[locNumObj];

  for(int i=0;i<locNumObj; i++)
  {
    global_ids[i]=16*myrank + (i+1);
  }


  Zoltan_Order (zz, numGidEntries, locNumObj, global_ids, permGIDs);

  int successFlag = checkResults(myrank, permGIDs);

  int returnFlag;

#ifdef HAVE_MPI
  MPI_Allreduce(&successFlag,&returnFlag,1,MPI_INT,MPI_LAND,MPI_COMM_WORLD);
#else
  returnFlag = successFlag;
#endif

  if(myrank==0)
  {
    if(returnFlag == 1)
    {
      std::cout   << "Success in testing local HSFC ordering on simple problem, numProcs = "
                  << worldsize << std::endl;
    }
    else
    {
      std::cout   << "Failure in testing local HSFC ordering on simple problem, numProcs = "
                  << worldsize << std::endl;
    }
  }

  delete [] global_ids;
  delete [] permGIDs;
  Zoltan_Destroy(&zz);

  return returnFlag;
}
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
int checkResults(int myrank, ZOLTAN_ID_PTR permGIDs)
{
   for(int i=0;i<locNumObj; i++)
   {
     if(permGIDs[i] != answers[myrank][i])
     {
       return 0;
     }
   }  
   return 1;
}
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int zoltNumObjs(void *data,int *ierr)
{
  *ierr=0;

  return locNumObj;
}
//////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
void zoltGetObjs(void *data, int num_gid_entries, 
                               int num_lid_entries, ZOLTAN_ID_PTR global_ids, 
                               ZOLTAN_ID_PTR local_ids, int wgt_dim, 
                               float *obj_wgts, int *ierr)
{
  int myrank;

#ifdef HAVE_MPI
  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
#else
  myrank=0;
#endif

  for(int i=0;i<locNumObj;i++)
  {
    global_ids[i]=16*myrank + (i+1);
    local_ids[i]=i+1;
  }

  *ierr=0;

  return;
}
//////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int zoltNumGeom(void *data,int *ierr)
{
  return 2;
}
//////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
void zoltGeom(void *data, int num_gid_entries, int num_lid_entries, ZOLTAN_ID_PTR global_id, 
              ZOLTAN_ID_PTR local_id, double *geom_vec, int *ierr)
{

  int myrank;

#ifdef HAVE_MPI
  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
#else
  myrank = 0;
#endif

  geom_vec[0] = (double) ( 4*(((*global_id)-1)/32) + (((*global_id)-1)%4)+1);
  geom_vec[1] = (double)  ((((*global_id)-1)%32)/4 + 1);

  *ierr=0;

  return;
}
//////////////////////////////////////////////////////////////

