<commit_before>#include	<cstdlib>
#include	<cmath>
#include	<iostream>
#include	<iomanip>
#include	<fstream>
#include	<sstream>
#include	<string>
#include	<list>
#include	<vector>
#include	<exception>
#include	"omp.h"

#include	"spline.h"
#include	"mangle.h"
#include	"filehandler.h"

/*

Reads a series of fields which comprise a box of mock "objects".
Replicates this box as necessary.
Applies a Mangle mask to the output and produces a file containing
ra, dec and z of the mock objects, plus the distance modulus and
the 'true' z (i.e. without peculiar velocities).

Currently a serial code, but it could be easily made OpenMP parallel
and with a little work an MPI code.

Author:		Martin White	(UCB,LBNL)
Written:	20-Dec-2015
Modified:

*/

#define		Nres	1000000	// Reserve space for this many objects.
#define		Nrepl	3	// Number of periodic replications.

struct	ObsObj {
  float	ra,dec,zz,zr,dmod,wt;
  int	indx;	// Object index in the original array.
};

void	myexit(const int flag) {
  std::cerr<<"myexit called with flag "<<flag<<std::endl;
  std::cout.flush();
  std::cerr.flush();
  exit(1);
}

void	myexception(std::exception& e) {
  std::cout<<"myexception called with "<<e.what()<<std::endl;
  std::cout.flush();
  std::cerr.flush();
  exit(1);
}

double	periodic(const double x) {
// Wraps x periodically in the range [0,1).
  double tmp = x;
  if (tmp >= 1.0) tmp = x-floor(x);
  if (tmp <  0.0) tmp = 1.0-(-x-floor(-x));
  return(tmp);
}

class Cosmology {
private:
  double Ez(double zz) const {
    // The dimensionless Hubble parameter at redshift zz.
    // This version ignores radiation, so shouldn't be used above z~100.
    double zp1  = 1.0 + zz;
    double rhom = OmM*zp1*zp1*zp1;
    double rhoK = OmK*zp1*zp1;
    double rhox = OmX*pow(zp1,3.0*(1+w0+wa))*exp(-3*wa*zz/zp1);
    double Ez2  = rhom+rhoK+rhox;
    return(sqrt(Ez2));
  }
public:
double	OmM,OmX,OmK,hh,w0,wa;
  double chi(const double zz) const {
    // The comoving distance to redshift zz, in Mpc/h.
    // Specialized for lower redshift.
    const double Lhub=2997.925;	// In Mpc/h.
    const int NN=2500;		// Must be even.
    const double dz = zz/NN;
    double sum= 1.0 + 1.0/Ez(zz);
    int i,wt;
    for (wt=4, i=1; i<NN; ++i) {
      sum += 1.0/Ez(i*dz) * wt;
      wt   = 8/wt;
    }
    sum *= dz/3.0;
    return(sum * Lhub);
  }
  Spline<double> z_of_chi() const {
    // Fit the redshift-distance relation as a spline.
    const int    Nspl=1024;
    const double zmax=10.0;
    std::vector<double> zval,cval;
    try {
      zval.resize(Nspl);
      cval.resize(Nspl);
    } catch(std::exception& e) {myexception(e);}
    zval[0]=cval[0]=0;
    // Do it this way so we can make this loop OpenMP parallel.
#pragma omp parallel for shared(zval,cval)
    for (int i=1; i<Nspl; ++i) {
      zval[i] = 0.0 + i*(zmax-0.0)/Nspl;
      cval[i] = chi(zval[i]);
    }
    Spline<double> zchi(cval,zval);
    return(zchi);
  }
  void set(const double OmM0, const double hh0) {
    OmM     = OmM0;
    OmX     = 1-OmM0;
    OmK     = 0;
    w0      = -1;
    wa      = 0;
  }
  Cosmology() {}  // Don't do anything.
  Cosmology(const double OmM0, const double hh0) {
    set(OmM0,hh0);
  }
  ~Cosmology() {} // Don't need to do anything
};

std::vector<struct ObsObj>	convert2obs(const std::vector<float>& pos,
                                            const std::vector<float>& vel,
                                            const Spline<double>& zchi,
                                            const double boxside,
                                            const double hubble,
                                            const double offset[],
                                            const int ic,
                                            const double zcen,
                                            const double zmin,
                                            const double zmax,
                                            const Mangle::MaskClass& M) {
  // Compute the RA, DEC, z (obs), z (real-space) and distance modulus fields
  // for an array of 3D positions, assuming an offset from the origin given
  // by offset[] and an observation "corner" of the box given by 0<=ic<8,
  // Returns an array of ObsObj structs.
  // Positions are interpolated, linearly, assuming constant velocity.
  // The returned values are cut on zmin<zobs<zmax.
  // This could be made a method of a template class if helpful.
  if (pos.size()%3!=0 || pos.size()!=vel.size()) {
    std::cerr<<"Do not understand length of pos or vel."<<std::endl;
    std::cerr.flush();
    myexit(1);
  }
  const int nobj = pos.size()/3; // Assume only a few objects, fits in an int.
  std::vector<struct ObsObj> Objs;
  try {Objs.resize(nobj);} catch(std::exception& e) {myexception(e);}
#pragma omp parallel for shared(Objs)
  for (int nn=0; nn<nobj; ++nn) {
    double rr=1e-10,nhat[3],cpos[3],cvel[3];
    struct ObsObj O;
    O.indx = nn;
    // Assume loop-unrolling is enabled.
    for (int idim=0; idim<3; ++idim)	// Do the coordinate transformation
      if ( (ic&(1<<idim))>0 ) {		// for viewing from a corner.
        cpos[idim] = 1.0 - pos[3*nn+idim] + offset[idim];
        cvel[idim] = 0.0 - vel[3*nn+idim];
      }
      else {
        cpos[idim] = pos[3*nn+idim] + offset[idim];
        cvel[idim] = vel[3*nn+idim];
      }
    for (int idim=0; idim<3; ++idim)
      rr += cpos[idim]*cpos[idim];
    rr   = sqrt(rr);
    O.zr = zchi(rr*boxside,-1.0);		// Real-space "redshift".
    // Now change cpos, based on cvel, to account for the
    // difference in redshift between zcen and now.
    // Assume this is small enough one iteration is enough.
    double dlna=log( (1+zcen)/(1+O.zr) );
    rr = 1e-10;
    for (int idim=0; idim<3; ++idim) {
      cpos[idim] = periodic(cpos[idim]+cvel[idim]*dlna);
      rr += cpos[idim]*cpos[idim];
    }
    rr   = sqrt(rr);
    O.zr = zchi(rr*boxside,-1.0);		// Real-space "redshift".
    for (int idim=0; idim<3; ++idim)
      nhat[idim] = cpos[idim]/rr;
    double vlos=0;
    for (int idim=0; idim<3; ++idim)
      vlos += cvel[idim]*nhat[idim];
    for (int idim=0; idim<3; ++idim)
      cpos[idim] += vlos*nhat[idim];
    rr=1e-10;
    for (int idim=0; idim<3; ++idim)
      rr += cpos[idim]*cpos[idim];
    rr   = sqrt(rr);
    O.zz = zchi(rr*boxside,-1.0);		// Obs redshift.
    if (O.zz>zmin && O.zz<zmax) {
      O.ra = 180/M_PI*atan2(cpos[1],cpos[0]);	// RA  in degrees.
      if (O.ra<0) O.ra+=360.0;			// Periodically wrap.
      O.dec = 90-180/M_PI*acos(cpos[2]/rr);	// DEC in degrees.
      O.dmod= 25+5*log10(rr*boxside/hubble);	// Distance modulus.
      // This next step can be slow if the mask isn't pixelized.
      O.wt  = M.getweight((90.-O.dec)*M_PI/180.,O.ra*M_PI/180.);
    }
    else {
      O.ra = O.dec = O.dmod = 0;
      O.wt =-1;
    }
    // Put it into the array -- do it this way rather than using
    // push_back so we can do OpenMP parallel.
    Objs[nn] = O;
  }
  return(Objs);
}

void	write_objs(const std::vector<struct ObsObj>& Objs,
                   const char fname[]) {
  // Generate a FileHandler file containing the relevant fields in Objs.
  const int nobj = Objs.size();
  std::vector<float> fvec;
  std::vector<int>   ivec;
  try {
    fvec.resize(nobj);
    ivec.resize(nobj);
  } catch(std::exception& e) {myexception(e);}
  std::vector<long> ndims(1);  ndims[0]=nobj;
  // Do RA, DEC, Z, ZTRUE, DMOD, INDX.
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].ra;
  FileHandler::write_float(fname,  "RA",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].dec;
  FileHandler::write_float(fname, "DEC",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].zz;
  FileHandler::write_float(fname,   "Z",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].zr;
  FileHandler::write_float(fname,  "ZR",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].dmod;
  FileHandler::write_float(fname,"DMOD",fvec,ndims);
  fvec.resize(0);
  for (int i=0; i<nobj; ++i) ivec[i]=Objs[i].indx;
  FileHandler::write_int(fname,"INDX",ivec,ndims);
}

int	main(int argc, char **argv)
{
  if (argc!=9) {
    std::cout<<"Usage: box2sky "
             <<"<OmM> <hub> <boxside> <zcen> <zmin> <zmax> <mask> <fbase>"
             <<std::endl;
    std::cout<<"--Box side should be in Mpc/h."<<std::endl;
    std::cout<<"--Mask should be an ascii, Mangle ply file."<<std::endl;
    myexit(1);
  }
  double OmM = atof(argv[1]);
  double hub = atof(argv[2]);
  double box = atof(argv[3]);
  double zcen= atof(argv[4]);
  double zmin= atof(argv[5]);
  double zmax= atof(argv[6]);
  std::cout<<"# Mock making code running on "<<omp_get_max_threads()
           <<" threads."<<std::endl;
  std::cout<<"# Using OmM="<<OmM<<", hub="<<hub<<" and box="<<box<<"Mpc/h."
           <<std::endl;
  std::cout<<"# Will cut output to "<<zmin<<"<z<"<<zmax<<std::endl;
  std::cout.flush();

  // Load the cosmology and the Mangle mask.
  Cosmology         C(OmM,hub);
  Spline<double> zchi=C.z_of_chi();
  Mangle::MaskClass M(argv[7]);
  std::cout<<"# Read mask containing "<<M.npolygons()
           <<" polygons from "<<argv[7]<<std::endl;
  std::cout.flush();

  // Read the phase space data for the objects.
  std::vector<long> ndims;
  std::vector<float> pos=FileHandler::read_float(argv[8],"pos",ndims);
  if (ndims.size()!=2 || ndims[1]!=3) {
    std::cout<<"Expecting to read (Nobj,3) array from "<<argv[8]<<"[pos]"
             <<std::endl;
    std::cout<<"Instead got dimensions: "<<std::endl;
    for (int i=0; i<ndims.size(); ++i)
      std::cout<<"  ndims["<<i<<"]="<<ndims[i]<<std::endl;
  }
  std::vector<float> vel=FileHandler::read_float(argv[8],"vel",ndims);
  if (ndims.size()!=2 || ndims[1]!=3) {
    std::cout<<"Expecting to read (Nobj,3) array from "<<argv[8]<<"[vel]"
             <<std::endl;
    std::cout<<"Instead got dimensions: "<<std::endl;
    for (int i=0; i<ndims.size(); ++i)
      std::cout<<"  ndims["<<i<<"]="<<ndims[i]<<std::endl;
  }
  std::cout<<"# Read pos/vel information for "<<ndims[0]
           <<" objects from "<<argv[8]<<std::endl;
  std::cout.flush();

  // Now we can subsample these galaxies by the above ratio and apply the mask.
  // The mask checking step can be slow, especially if the mask is not
  // pixelized, but by doing it this way we keep data size manageable.
  for (int ic=0; ic<8; ++ic) {	// For each octant...
    std::vector<struct ObsObj> Objs;
    try {Objs.reserve(Nres);} catch(std::exception& e) {myexception(e);}
    // Accumulate objects from all of the replications.
    double offset[3];
    for (int ix=-Nrepl; ix<=Nrepl; ++ix) {
      offset[0] = (double)ix;
      for (int iy=-Nrepl; iy<=Nrepl; ++iy) {
        offset[1] = (double)iy;
        for (int iz=-Nrepl; iz<=Nrepl; ++iz) {
          offset[2] = (double)iz;
          try {
            std::vector<struct ObsObj> O;
            O = convert2obs(pos,vel,zchi,box,hub,offset,ic,zcen,zmin,zmax,M);
            for (int i=0; i<O.size(); ++i)
              if (O[i].wt>0) {Objs.push_back(O[i]);}
          } catch(std::exception& e) {myexception(e);}
        }
      }
    }
    // Now write the results to file, labeled by octant.
    std::ostringstream ss;  ss<<argv[8]<<"_oct"<<ic;
    write_objs(Objs,ss.str().c_str());
  }

  std::cout<<"# Finished."<<std::endl;std::cout.flush();
  return(0);
}
<commit_msg>More info messages.<commit_after>#include	<cstdlib>
#include	<cmath>
#include	<iostream>
#include	<iomanip>
#include	<fstream>
#include	<sstream>
#include	<string>
#include	<list>
#include	<vector>
#include	<exception>
#include	"omp.h"

#include	"spline.h"
#include	"mangle.h"
#include	"filehandler.h"

/*

Reads a series of fields which comprise a box of mock "objects".
Replicates this box as necessary.
Applies a Mangle mask to the output and produces a file containing
ra, dec and z of the mock objects, plus the distance modulus and
the 'true' z (i.e. without peculiar velocities).

Currently a serial code, but it could be easily made OpenMP parallel
and with a little work an MPI code.

Author:		Martin White	(UCB,LBNL)
Written:	20-Dec-2015
Modified:

*/

#define		Nres	1000000	// Reserve space for this many objects.
#define		Nrepl	3	// Number of periodic replications.

struct	ObsObj {
  float	ra,dec,zz,zr,dmod,wt;
  int	indx;	// Object index in the original array.
};

void	myexit(const int flag) {
  std::cerr<<"myexit called with flag "<<flag<<std::endl;
  std::cout.flush();
  std::cerr.flush();
  exit(1);
}

void	myexception(std::exception& e) {
  std::cout<<"myexception called with "<<e.what()<<std::endl;
  std::cout.flush();
  std::cerr.flush();
  exit(1);
}

double	periodic(const double x) {
// Wraps x periodically in the range [0,1).
  double tmp = x;
  if (tmp >= 1.0) tmp = x-floor(x);
  if (tmp <  0.0) tmp = 1.0-(-x-floor(-x));
  return(tmp);
}

class Cosmology {
private:
  double Ez(double zz) const {
    // The dimensionless Hubble parameter at redshift zz.
    // This version ignores radiation, so shouldn't be used above z~100.
    double zp1  = 1.0 + zz;
    double rhom = OmM*zp1*zp1*zp1;
    double rhoK = OmK*zp1*zp1;
    double rhox = OmX*pow(zp1,3.0*(1+w0+wa))*exp(-3*wa*zz/zp1);
    double Ez2  = rhom+rhoK+rhox;
    return(sqrt(Ez2));
  }
public:
double	OmM,OmX,OmK,hh,w0,wa;
  double chi(const double zz) const {
    // The comoving distance to redshift zz, in Mpc/h.
    // Specialized for lower redshift.
    const double Lhub=2997.925;	// In Mpc/h.
    const int NN=2500;		// Must be even.
    const double dz = zz/NN;
    double sum= 1.0 + 1.0/Ez(zz);
    int i,wt;
    for (wt=4, i=1; i<NN; ++i) {
      sum += 1.0/Ez(i*dz) * wt;
      wt   = 8/wt;
    }
    sum *= dz/3.0;
    return(sum * Lhub);
  }
  Spline<double> z_of_chi() const {
    // Fit the redshift-distance relation as a spline.
    const int    Nspl=1024;
    const double zmax=10.0;
    std::vector<double> zval,cval;
    try {
      zval.resize(Nspl);
      cval.resize(Nspl);
    } catch(std::exception& e) {myexception(e);}
    zval[0]=cval[0]=0;
    // Do it this way so we can make this loop OpenMP parallel.
#pragma omp parallel for shared(zval,cval)
    for (int i=1; i<Nspl; ++i) {
      zval[i] = 0.0 + i*(zmax-0.0)/Nspl;
      cval[i] = chi(zval[i]);
    }
    Spline<double> zchi(cval,zval);
    return(zchi);
  }
  void set(const double OmM0, const double hh0) {
    OmM     = OmM0;
    OmX     = 1-OmM0;
    OmK     = 0;
    w0      = -1;
    wa      = 0;
  }
  Cosmology() {}  // Don't do anything.
  Cosmology(const double OmM0, const double hh0) {
    set(OmM0,hh0);
  }
  ~Cosmology() {} // Don't need to do anything
};

std::vector<struct ObsObj>	convert2obs(const std::vector<float>& pos,
                                            const std::vector<float>& vel,
                                            const Spline<double>& zchi,
                                            const double boxside,
                                            const double hubble,
                                            const double offset[],
                                            const int ic,
                                            const double zcen,
                                            const double zmin,
                                            const double zmax,
                                            const Mangle::MaskClass& M) {
  // Compute the RA, DEC, z (obs), z (real-space) and distance modulus fields
  // for an array of 3D positions, assuming an offset from the origin given
  // by offset[] and an observation "corner" of the box given by 0<=ic<8,
  // Returns an array of ObsObj structs.
  // Positions are interpolated, linearly, assuming constant velocity.
  // The returned values are cut on zmin<zobs<zmax.
  // This could be made a method of a template class if helpful.
  if (pos.size()%3!=0 || pos.size()!=vel.size()) {
    std::cerr<<"Do not understand length of pos or vel."<<std::endl;
    std::cerr.flush();
    myexit(1);
  }
  const int nobj = pos.size()/3; // Assume only a few objects, fits in an int.
  std::vector<struct ObsObj> Objs;
  try {Objs.resize(nobj);} catch(std::exception& e) {myexception(e);}
#pragma omp parallel for shared(Objs)
  for (int nn=0; nn<nobj; ++nn) {
    double rr=1e-10,nhat[3],cpos[3],cvel[3];
    struct ObsObj O;
    O.indx = nn;
    // Assume loop-unrolling is enabled.
    for (int idim=0; idim<3; ++idim)	// Do the coordinate transformation
      if ( (ic&(1<<idim))>0 ) {		// for viewing from a corner.
        cpos[idim] = 1.0 - pos[3*nn+idim] + offset[idim];
        cvel[idim] = 0.0 - vel[3*nn+idim];
      }
      else {
        cpos[idim] = pos[3*nn+idim] + offset[idim];
        cvel[idim] = vel[3*nn+idim];
      }
    for (int idim=0; idim<3; ++idim)
      rr += cpos[idim]*cpos[idim];
    rr   = sqrt(rr);
    O.zr = zchi(rr*boxside,-1.0);		// Real-space "redshift".
    // Now change cpos, based on cvel, to account for the
    // difference in redshift between zcen and now.
    // Assume this is small enough one iteration is enough.
    double dlna=log( (1+zcen)/(1+O.zr) );
    rr = 1e-10;
    for (int idim=0; idim<3; ++idim) {
      cpos[idim] = periodic(cpos[idim]+cvel[idim]*dlna);
      rr += cpos[idim]*cpos[idim];
    }
    rr   = sqrt(rr);
    O.zr = zchi(rr*boxside,-1.0);		// Real-space "redshift".
    for (int idim=0; idim<3; ++idim)
      nhat[idim] = cpos[idim]/rr;
    double vlos=0;
    for (int idim=0; idim<3; ++idim)
      vlos += cvel[idim]*nhat[idim];
    for (int idim=0; idim<3; ++idim)
      cpos[idim] += vlos*nhat[idim];
    rr=1e-10;
    for (int idim=0; idim<3; ++idim)
      rr += cpos[idim]*cpos[idim];
    rr   = sqrt(rr);
    O.zz = zchi(rr*boxside,-1.0);		// Obs redshift.
    if (O.zz>zmin && O.zz<zmax) {
      O.ra = 180/M_PI*atan2(cpos[1],cpos[0]);	// RA  in degrees.
      if (O.ra<0) O.ra+=360.0;			// Periodically wrap.
      O.dec = 90-180/M_PI*acos(cpos[2]/rr);	// DEC in degrees.
      O.dmod= 25+5*log10(rr*boxside/hubble);	// Distance modulus.
      // This next step can be slow if the mask isn't pixelized.
      O.wt  = M.getweight((90.-O.dec)*M_PI/180.,O.ra*M_PI/180.);
    }
    else {
      O.ra = O.dec = O.dmod = 0;
      O.wt =-1;
    }
    // Put it into the array -- do it this way rather than using
    // push_back so we can do OpenMP parallel.
    Objs[nn] = O;
  }
  return(Objs);
}

void	write_objs(const std::vector<struct ObsObj>& Objs,
                   const char fname[]) {
  // Generate a FileHandler file containing the relevant fields in Objs.
  const int nobj = Objs.size();
  std::vector<float> fvec;
  std::vector<int>   ivec;
  try {
    fvec.resize(nobj);
    ivec.resize(nobj);
  } catch(std::exception& e) {myexception(e);}
  std::vector<long> ndims(1);  ndims[0]=nobj;
  // Do RA, DEC, Z, ZTRUE, DMOD, INDX.
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].ra;
  FileHandler::write_float(fname,  "RA",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].dec;
  FileHandler::write_float(fname, "DEC",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].zz;
  FileHandler::write_float(fname,   "Z",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].zr;
  FileHandler::write_float(fname,  "ZR",fvec,ndims);
  for (int i=0; i<nobj; ++i) fvec[i]=Objs[i].dmod;
  FileHandler::write_float(fname,"DMOD",fvec,ndims);
  fvec.resize(0);
  for (int i=0; i<nobj; ++i) ivec[i]=Objs[i].indx;
  FileHandler::write_int(fname,"INDX",ivec,ndims);
}

int	main(int argc, char **argv)
{
  if (argc!=9) {
    std::cout<<"Usage: box2sky "
             <<"<OmM> <hub> <boxside> <zcen> <zmin> <zmax> <mask> <fbase>"
             <<std::endl;
    std::cout<<"--Box side should be in Mpc/h."<<std::endl;
    std::cout<<"--Mask should be an ascii, Mangle ply file."<<std::endl;
    myexit(1);
  }
  double OmM = atof(argv[1]);
  double hub = atof(argv[2]);
  double box = atof(argv[3]);
  double zcen= atof(argv[4]);
  double zmin= atof(argv[5]);
  double zmax= atof(argv[6]);
  std::cout<<"# Mock making code running on "<<omp_get_max_threads()
           <<" threads."<<std::endl;
  std::cout<<"# Using OmM="<<OmM<<", hub="<<hub<<" and box="<<box<<"Mpc/h."
           <<std::endl;
  std::cout<<"# Output is taken to be at z="<<zcen<<std::endl;
  std::cout<<"# Will cut output to "<<zmin<<"<z<"<<zmax<<std::endl;
  std::cout.flush();

  // Load the cosmology and the Mangle mask.
  Cosmology         C(OmM,hub);
  Spline<double> zchi=C.z_of_chi();
  Mangle::MaskClass M(argv[7]);
  std::cout<<"# Read mask containing "<<M.npolygons()
           <<" polygons from "<<argv[7]<<std::endl;
  std::cout.flush();

  // Read the phase space data for the objects.
  std::vector<long> ndims;
  std::vector<float> pos=FileHandler::read_float(argv[8],"pos",ndims);
  if (ndims.size()!=2 || ndims[1]!=3) {
    std::cout<<"Expecting to read (Nobj,3) array from "<<argv[8]<<"[pos]"
             <<std::endl;
    std::cout<<"Instead got dimensions: "<<std::endl;
    for (int i=0; i<ndims.size(); ++i)
      std::cout<<"  ndims["<<i<<"]="<<ndims[i]<<std::endl;
  }
  std::vector<float> vel=FileHandler::read_float(argv[8],"vel",ndims);
  if (ndims.size()!=2 || ndims[1]!=3) {
    std::cout<<"Expecting to read (Nobj,3) array from "<<argv[8]<<"[vel]"
             <<std::endl;
    std::cout<<"Instead got dimensions: "<<std::endl;
    for (int i=0; i<ndims.size(); ++i)
      std::cout<<"  ndims["<<i<<"]="<<ndims[i]<<std::endl;
  }
  std::cout<<"# Read pos/vel information for "<<ndims[0]
           <<" objects from "<<argv[8]<<std::endl;
  std::cout.flush();

  // Now we can subsample these galaxies by the above ratio and apply the mask.
  // The mask checking step can be slow, especially if the mask is not
  // pixelized, but by doing it this way we keep data size manageable.
  for (int ic=0; ic<8; ++ic) {	// For each octant...
    std::vector<struct ObsObj> Objs;
    try {Objs.reserve(Nres);} catch(std::exception& e) {myexception(e);}
    // Accumulate objects from all of the replications.
    double offset[3];
    for (int ix=-Nrepl; ix<=Nrepl; ++ix) {
      offset[0] = (double)ix;
      for (int iy=-Nrepl; iy<=Nrepl; ++iy) {
        offset[1] = (double)iy;
        for (int iz=-Nrepl; iz<=Nrepl; ++iz) {
          offset[2] = (double)iz;
          try {
            std::vector<struct ObsObj> O;
            O = convert2obs(pos,vel,zchi,box,hub,offset,ic,zcen,zmin,zmax,M);
            for (int i=0; i<O.size(); ++i)
              if (O[i].wt>0) {Objs.push_back(O[i]);}
          } catch(std::exception& e) {myexception(e);}
        }
      }
    }
    // Now write the results to file, labeled by octant.
    std::ostringstream ss;  ss<<argv[8]<<"_oct"<<ic;
    write_objs(Objs,ss.str().c_str());
  }

  std::cout<<"# Finished."<<std::endl;std::cout.flush();
  return(0);
}
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Visualization Toolkit
Module:    vtkOrderStatistics.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
  -------------------------------------------------------------------------*/

#include "vtkToolkits.h"

#include "vtkOrderStatistics.h"
#include "vtkStatisticsAlgorithmPrivate.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"

#include <vtksys/stl/vector>
#include <vtksys/stl/map>
#include <vtksys/stl/set>
#include <vtksys/ios/sstream>

vtkStandardNewMacro(vtkOrderStatistics);

// ----------------------------------------------------------------------
vtkOrderStatistics::vtkOrderStatistics()
{
  this->QuantileDefinition = vtkOrderStatistics::InverseCDFAveragedSteps;
  this->NumberOfIntervals = 4; // By default, calculate 5-points statistics

  this->AssessNames->SetNumberOfValues( 1 );
  this->AssessNames->SetValue( 0, "Quantile" );
}

// ----------------------------------------------------------------------
vtkOrderStatistics::~vtkOrderStatistics()
{
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::PrintSelf( ostream &os, vtkIndent indent )
{
  this->Superclass::PrintSelf( os, indent );
  os << indent << "NumberOfIntervals: " << this->NumberOfIntervals << endl;
  os << indent << "QuantileDefinition: " << this->QuantileDefinition << endl;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::SetQuantileDefinition( int qd )
{
  switch ( qd )
    {
    case vtkOrderStatistics::InverseCDF:
      break;
    case vtkOrderStatistics::InverseCDFAveragedSteps:
      break;
    default:
      vtkWarningMacro( "Incorrect type of quantile definition: "
                       <<qd
                       <<". Ignoring it." );
      return;
    }

  this->QuantileDefinition =  static_cast<vtkOrderStatistics::QuantileDefinitionType>( qd );
  this->Modified();

  return;
}

// ----------------------------------------------------------------------
bool vtkOrderStatistics::SetParameter( const char* parameter,
                                       int vtkNotUsed( index ),
                                       vtkVariant value )
{
  if ( ! strcmp( parameter, "NumberOfIntervals" ) )
    {
    this->SetNumberOfIntervals( value.ToInt() );

    return true;
    }

  if ( ! strcmp( parameter, "QuantileDefinition" ) )
    {
    this->SetQuantileDefinition( value.ToInt() );

    return true;
    }

  return false;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Learn( vtkTable* inData,
                                vtkTable* vtkNotUsed( inParameters ),
                                vtkMultiBlockDataSet* outMeta )
{
  if ( ! inData )
    {
    return;
    }

  if ( ! outMeta )
    {
    return;
    }

  // The primary statistics table
  vtkTable* primaryTab = vtkTable::New();

  vtkStringArray* stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable" );
  primaryTab->AddColumn( stringCol );
  stringCol->Delete();

  vtkIdTypeArray* idTypeCol = vtkIdTypeArray::New();
  idTypeCol->SetName( "Cardinality" );
  primaryTab->AddColumn( idTypeCol );
  idTypeCol->Delete();

  vtkVariantArray* variantCol;
  double dq = 1. / static_cast<double>( this->NumberOfIntervals );
  for ( int i = 0; i <= this->NumberOfIntervals; ++ i )
    {
    variantCol = vtkVariantArray::New();
    div_t q = div( i << 2, this->NumberOfIntervals );

    if ( q.rem )
      {
      variantCol->SetName( vtkStdString( vtkVariant( i * dq ).ToString() + "-quantile" ).c_str() );
      }
    else
      {
      switch ( q.quot )
        {
        case 0:
          variantCol->SetName( "Minimum" );
          break;
        case 1:
          variantCol->SetName( "First Quartile" );
          break;
        case 2:
          variantCol->SetName( "Median" );
          break;
        case 3:
          variantCol->SetName( "Third Quartile" );
          break;
        case 4:
          variantCol->SetName( "Maximum" );
          break;
        default:
          variantCol->SetName( vtkStdString( vtkVariant( i * dq ).ToString() + "-quantile" ).c_str() );
          break;
        }
      }
    primaryTab->AddColumn( variantCol );
    variantCol->Delete();
    }

  // Loop over requests
  vtkIdType nRow = inData->GetNumberOfRows();
  for ( vtksys_stl::set<vtksys_stl::set<vtkStdString> >::iterator rit = this->Internals->Requests.begin();
        rit != this->Internals->Requests.end(); ++ rit )
    {
    // Each request contains only one column of interest (if there are others, they are ignored)
    vtksys_stl::set<vtkStdString>::const_iterator it = rit->begin();
    vtkStdString col = *it;
    if ( ! inData->GetColumnByName( col ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << col.c_str()
                       << ". Ignoring it." );
      continue;
      }

    vtkVariantArray* row = vtkVariantArray::New();

    // A row contains: variable name, cardinality, and NumberOfIntervals + 1 quantile values
    row->SetNumberOfValues( this->NumberOfIntervals + 3 );

    int i = 0;
    row->SetValue( i ++, col );
    row->SetValue( i ++, nRow );

    vtksys_stl::vector<double> quantileThresholds;
    double dh = nRow / static_cast<double>( this->NumberOfIntervals );
    for ( int j = 0; j < this->NumberOfIntervals; ++ j )
      {
      quantileThresholds.push_back( j * dh );
      }

    // Try to downcast column to eith data or string arrays for efficient data access
    vtkAbstractArray* arr = inData->GetColumnByName( col );

    if ( arr->IsA("vtkDataArray") )
      {
      vtkDataArray* darr = vtkDataArray::SafeDownCast( arr );

      vtksys_stl::map<double,vtkIdType> distr;
      for ( vtkIdType r = 0; r < nRow; ++ r )
        {
        ++ distr[darr->GetTuple1( r )];
        }

      vtkIdType sum = 0;
      vtksys_stl::vector<double>::iterator qit = quantileThresholds.begin();
      for ( vtksys_stl::map<double,vtkIdType>::iterator mit = distr.begin();
            mit != distr.end(); ++ mit  )
        {
        for ( sum += mit->second; qit != quantileThresholds.end() && sum >= *qit; ++ qit )
          {
          // Mid-point interpolation is available for numeric types only
          if ( sum == *qit
               && this->QuantileDefinition == vtkOrderStatistics::InverseCDFAveragedSteps )
            {
            vtksys_stl::map<double,vtkIdType>::iterator nit = mit;
            row->SetValue( i ++, ( (++ nit)->first + mit->first ) * .5 );
            }
          else
            {
            row->SetValue( i ++, mit->first );
            }
          }
        }

      row->SetValue( i, distr.rbegin()->first );
      primaryTab->InsertNextRow( row );
      }
    else if ( arr->IsA("vtkStringArray") )
      {
      vtkStringArray* sarr = vtkStringArray::SafeDownCast( arr );
      vtksys_stl::map<vtkStdString,vtkIdType> distr;
      for ( vtkIdType r = 0; r < nRow; ++ r )
        {
        ++ distr[sarr->GetValue( r )];
        }

      vtkIdType sum = 0;
      vtksys_stl::vector<double>::iterator qit = quantileThresholds.begin();
      for ( vtksys_stl::map<vtkStdString,vtkIdType>::iterator mit = distr.begin();
            mit != distr.end(); ++ mit  )
        {
        for ( sum += mit->second; qit != quantileThresholds.end() && sum >= *qit; ++ qit )
          {
          row->SetValue( i ++, mit->first );
          }
        }

      row->SetValue( i, distr.rbegin()->first );
      primaryTab->InsertNextRow( row );
      }
    else // column is of type vtkVariantArray, which is not supported by this filter
      {
      vtkWarningMacro("Type vtkVariantArray of column "
                      <<col.c_str()
                      << " not supported. Ignoring it." );
      }

    row->Delete();
    }

  // Finally set first block of output meta port to primary statistics table
  outMeta->SetNumberOfBlocks( 1 );
  outMeta->GetMetaData( static_cast<unsigned>( 0 ) )->Set( vtkCompositeDataSet::NAME(), "Primary Statistics" );
  outMeta->SetBlock( 0, primaryTab );

  // Clean up
  primaryTab->Delete();

  return;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Derive( vtkMultiBlockDataSet* vtkNotUsed( inMeta ) )
{
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Test( vtkTable* inData,
                               vtkMultiBlockDataSet* inMeta,
                               vtkTable* outMeta )
{
  if ( ! inMeta )
    {
    return;
    }

  vtkTable* primaryTab = vtkTable::SafeDownCast( inMeta->GetBlock( 0 ) );
  if ( ! primaryTab )
    {
    return;
    }

  if ( ! outMeta )
    {
    return;
    }

}

// ----------------------------------------------------------------------
class TableColumnBucketingFunctor : public vtkStatisticsAlgorithm::AssessFunctor
{
public:
  vtkAbstractArray* Data;
  vtkVariantArray* Quantiles;

  TableColumnBucketingFunctor( vtkAbstractArray* vals,
                               vtkVariantArray* quantiles )
  {
    this->Data = vals;
    this->Quantiles = quantiles;
  }
  virtual ~TableColumnBucketingFunctor() { }
  virtual void operator() ( vtkVariantArray* result,
                            vtkIdType id )
  {
    vtkVariant x = this->Data->GetVariantValue( id );

    if ( x < this->Quantiles->GetValue( 2 ) ) // Value #0 is the variable name and #1 is the cardinality
      {
      // x is smaller than lower bound
      result->SetNumberOfValues( 1 );
      result->SetValue( 0, 0 );

      return;
      }

    vtkIdType n = this->Quantiles->GetNumberOfValues() + 2;
    vtkIdType q = 3;
    while ( q < n && x > this->Quantiles->GetValue( q ) )
      {
      ++ q;
      }

    result->SetNumberOfValues( 1 );
    result->SetValue( 0, q - 2 );
  }
};

// ----------------------------------------------------------------------
void vtkOrderStatistics::SelectAssessFunctor( vtkTable* outData,
                                              vtkDataObject* inMetaDO,
                                              vtkStringArray* rowNames,
                                              AssessFunctor*& dfunc )
{
  vtkMultiBlockDataSet* inMeta = vtkMultiBlockDataSet::SafeDownCast( inMetaDO );
  if ( ! inMeta
       || inMeta->GetNumberOfBlocks() < 1 )
    {
    return;
    }

  vtkTable* primaryTab = vtkTable::SafeDownCast( inMeta->GetBlock( 0 ) );
  if ( ! primaryTab )
    {
    return;
    }

  vtkStdString varName = rowNames->GetValue( 0 );

  // Downcast meta columns to string arrays for efficient data access
  vtkStringArray* vars = vtkStringArray::SafeDownCast( primaryTab->GetColumnByName( "Variable" ) );
  if ( ! vars )
    {
    dfunc = 0;
    return;
    }

  // Loop over parameters table until the requested variable is found
  vtkIdType nRowP = primaryTab->GetNumberOfRows();
  for ( int r = 0; r < nRowP; ++ r )
    {
    if ( vars->GetValue( r ) == varName )
      {
      // Grab the data for the requested variable
      vtkAbstractArray* vals = outData->GetColumnByName( varName );
      if ( ! vals )
        {
        dfunc = 0;
        return;
        }

      dfunc = new TableColumnBucketingFunctor( vals, primaryTab->GetRow( r ) );
      return;
      }
    }

  // The variable of interest was not found in the parameter table
  dfunc = 0;
}
<commit_msg>STYLE: doc<commit_after>/*=========================================================================

Program:   Visualization Toolkit
Module:    vtkOrderStatistics.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
  -------------------------------------------------------------------------*/

#include "vtkToolkits.h"

#include "vtkOrderStatistics.h"
#include "vtkStatisticsAlgorithmPrivate.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"

#include <vtksys/stl/vector>
#include <vtksys/stl/map>
#include <vtksys/stl/set>
#include <vtksys/ios/sstream>

vtkStandardNewMacro(vtkOrderStatistics);

// ----------------------------------------------------------------------
vtkOrderStatistics::vtkOrderStatistics()
{
  this->QuantileDefinition = vtkOrderStatistics::InverseCDFAveragedSteps;
  this->NumberOfIntervals = 4; // By default, calculate 5-points statistics

  this->AssessNames->SetNumberOfValues( 1 );
  this->AssessNames->SetValue( 0, "Quantile" );
}

// ----------------------------------------------------------------------
vtkOrderStatistics::~vtkOrderStatistics()
{
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::PrintSelf( ostream &os, vtkIndent indent )
{
  this->Superclass::PrintSelf( os, indent );
  os << indent << "NumberOfIntervals: " << this->NumberOfIntervals << endl;
  os << indent << "QuantileDefinition: " << this->QuantileDefinition << endl;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::SetQuantileDefinition( int qd )
{
  switch ( qd )
    {
    case vtkOrderStatistics::InverseCDF:
      break;
    case vtkOrderStatistics::InverseCDFAveragedSteps:
      break;
    default:
      vtkWarningMacro( "Incorrect type of quantile definition: "
                       <<qd
                       <<". Ignoring it." );
      return;
    }

  this->QuantileDefinition =  static_cast<vtkOrderStatistics::QuantileDefinitionType>( qd );
  this->Modified();

  return;
}

// ----------------------------------------------------------------------
bool vtkOrderStatistics::SetParameter( const char* parameter,
                                       int vtkNotUsed( index ),
                                       vtkVariant value )
{
  if ( ! strcmp( parameter, "NumberOfIntervals" ) )
    {
    this->SetNumberOfIntervals( value.ToInt() );

    return true;
    }

  if ( ! strcmp( parameter, "QuantileDefinition" ) )
    {
    this->SetQuantileDefinition( value.ToInt() );

    return true;
    }

  return false;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Learn( vtkTable* inData,
                                vtkTable* vtkNotUsed( inParameters ),
                                vtkMultiBlockDataSet* outMeta )
{
  if ( ! inData )
    {
    return;
    }

  if ( ! outMeta )
    {
    return;
    }

  // The primary statistics table
  vtkTable* primaryTab = vtkTable::New();

  vtkStringArray* stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable" );
  primaryTab->AddColumn( stringCol );
  stringCol->Delete();

  vtkIdTypeArray* idTypeCol = vtkIdTypeArray::New();
  idTypeCol->SetName( "Cardinality" );
  primaryTab->AddColumn( idTypeCol );
  idTypeCol->Delete();

  vtkVariantArray* variantCol;
  double dq = 1. / static_cast<double>( this->NumberOfIntervals );
  for ( int i = 0; i <= this->NumberOfIntervals; ++ i )
    {
    variantCol = vtkVariantArray::New();
    div_t q = div( i << 2, this->NumberOfIntervals );

    if ( q.rem )
      {
      variantCol->SetName( vtkStdString( vtkVariant( i * dq ).ToString() + "-quantile" ).c_str() );
      }
    else
      {
      switch ( q.quot )
        {
        case 0:
          variantCol->SetName( "Minimum" );
          break;
        case 1:
          variantCol->SetName( "First Quartile" );
          break;
        case 2:
          variantCol->SetName( "Median" );
          break;
        case 3:
          variantCol->SetName( "Third Quartile" );
          break;
        case 4:
          variantCol->SetName( "Maximum" );
          break;
        default:
          variantCol->SetName( vtkStdString( vtkVariant( i * dq ).ToString() + "-quantile" ).c_str() );
          break;
        }
      }
    primaryTab->AddColumn( variantCol );
    variantCol->Delete();
    }

  // Loop over requests
  vtkIdType nRow = inData->GetNumberOfRows();
  for ( vtksys_stl::set<vtksys_stl::set<vtkStdString> >::iterator rit = this->Internals->Requests.begin();
        rit != this->Internals->Requests.end(); ++ rit )
    {
    // Each request contains only one column of interest (if there are others, they are ignored)
    vtksys_stl::set<vtkStdString>::const_iterator it = rit->begin();
    vtkStdString col = *it;
    if ( ! inData->GetColumnByName( col ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << col.c_str()
                       << ". Ignoring it." );
      continue;
      }

    vtkVariantArray* row = vtkVariantArray::New();

    // A row contains: variable name, cardinality, and NumberOfIntervals + 1 quantile values
    row->SetNumberOfValues( this->NumberOfIntervals + 3 );

    int i = 0;
    row->SetValue( i ++, col );
    row->SetValue( i ++, nRow );

    vtksys_stl::vector<double> quantileThresholds;
    double dh = nRow / static_cast<double>( this->NumberOfIntervals );
    for ( int j = 0; j < this->NumberOfIntervals; ++ j )
      {
      quantileThresholds.push_back( j * dh );
      }

    // Try to downcast column to eith data or string arrays for efficient data access
    vtkAbstractArray* arr = inData->GetColumnByName( col );

    if ( arr->IsA("vtkDataArray") )
      {
      vtkDataArray* darr = vtkDataArray::SafeDownCast( arr );

      vtksys_stl::map<double,vtkIdType> distr;
      for ( vtkIdType r = 0; r < nRow; ++ r )
        {
        ++ distr[darr->GetTuple1( r )];
        }

      vtkIdType sum = 0;
      vtksys_stl::vector<double>::iterator qit = quantileThresholds.begin();
      for ( vtksys_stl::map<double,vtkIdType>::iterator mit = distr.begin();
            mit != distr.end(); ++ mit  )
        {
        for ( sum += mit->second; qit != quantileThresholds.end() && sum >= *qit; ++ qit )
          {
          // Mid-point interpolation is available for numeric types only
          if ( sum == *qit
               && this->QuantileDefinition == vtkOrderStatistics::InverseCDFAveragedSteps )
            {
            vtksys_stl::map<double,vtkIdType>::iterator nit = mit;
            row->SetValue( i ++, ( (++ nit)->first + mit->first ) * .5 );
            }
          else
            {
            row->SetValue( i ++, mit->first );
            }
          }
        }

      row->SetValue( i, distr.rbegin()->first );
      primaryTab->InsertNextRow( row );
      }
    else if ( arr->IsA("vtkStringArray") )
      {
      vtkStringArray* sarr = vtkStringArray::SafeDownCast( arr );
      vtksys_stl::map<vtkStdString,vtkIdType> distr;
      for ( vtkIdType r = 0; r < nRow; ++ r )
        {
        ++ distr[sarr->GetValue( r )];
        }

      vtkIdType sum = 0;
      vtksys_stl::vector<double>::iterator qit = quantileThresholds.begin();
      for ( vtksys_stl::map<vtkStdString,vtkIdType>::iterator mit = distr.begin();
            mit != distr.end(); ++ mit  )
        {
        for ( sum += mit->second; qit != quantileThresholds.end() && sum >= *qit; ++ qit )
          {
          row->SetValue( i ++, mit->first );
          }
        }

      row->SetValue( i, distr.rbegin()->first );
      primaryTab->InsertNextRow( row );
      }
    else // column is of type vtkVariantArray, which is not supported by this filter
      {
      vtkWarningMacro("Type vtkVariantArray of column "
                      <<col.c_str()
                      << " not supported. Ignoring it." );
      }

    row->Delete();
    }

  // Finally set first block of output meta port to primary statistics table
  outMeta->SetNumberOfBlocks( 1 );
  outMeta->GetMetaData( static_cast<unsigned>( 0 ) )->Set( vtkCompositeDataSet::NAME(), "Primary Statistics" );
  outMeta->SetBlock( 0, primaryTab );

  // Clean up
  primaryTab->Delete();

  return;
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Derive( vtkMultiBlockDataSet* vtkNotUsed( inMeta ) )
{
  // There are no derived statistics for this engine.
}

// ----------------------------------------------------------------------
void vtkOrderStatistics::Test( vtkTable* inData,
                               vtkMultiBlockDataSet* inMeta,
                               vtkTable* outMeta )
{
  if ( ! inMeta )
    {
    return;
    }

  vtkTable* primaryTab = vtkTable::SafeDownCast( inMeta->GetBlock( 0 ) );
  if ( ! primaryTab )
    {
    return;
    }

  if ( ! outMeta )
    {
    return;
    }

}

// ----------------------------------------------------------------------
class TableColumnBucketingFunctor : public vtkStatisticsAlgorithm::AssessFunctor
{
public:
  vtkAbstractArray* Data;
  vtkVariantArray* Quantiles;

  TableColumnBucketingFunctor( vtkAbstractArray* vals,
                               vtkVariantArray* quantiles )
  {
    this->Data = vals;
    this->Quantiles = quantiles;
  }
  virtual ~TableColumnBucketingFunctor() { }
  virtual void operator() ( vtkVariantArray* result,
                            vtkIdType id )
  {
    vtkVariant x = this->Data->GetVariantValue( id );

    if ( x < this->Quantiles->GetValue( 2 ) ) // Value #0 is the variable name and #1 is the cardinality
      {
      // x is smaller than lower bound
      result->SetNumberOfValues( 1 );
      result->SetValue( 0, 0 );

      return;
      }

    vtkIdType n = this->Quantiles->GetNumberOfValues() + 2;
    vtkIdType q = 3;
    while ( q < n && x > this->Quantiles->GetValue( q ) )
      {
      ++ q;
      }

    result->SetNumberOfValues( 1 );
    result->SetValue( 0, q - 2 );
  }
};

// ----------------------------------------------------------------------
void vtkOrderStatistics::SelectAssessFunctor( vtkTable* outData,
                                              vtkDataObject* inMetaDO,
                                              vtkStringArray* rowNames,
                                              AssessFunctor*& dfunc )
{
  vtkMultiBlockDataSet* inMeta = vtkMultiBlockDataSet::SafeDownCast( inMetaDO );
  if ( ! inMeta
       || inMeta->GetNumberOfBlocks() < 1 )
    {
    return;
    }

  vtkTable* primaryTab = vtkTable::SafeDownCast( inMeta->GetBlock( 0 ) );
  if ( ! primaryTab )
    {
    return;
    }

  vtkStdString varName = rowNames->GetValue( 0 );

  // Downcast meta columns to string arrays for efficient data access
  vtkStringArray* vars = vtkStringArray::SafeDownCast( primaryTab->GetColumnByName( "Variable" ) );
  if ( ! vars )
    {
    dfunc = 0;
    return;
    }

  // Loop over parameters table until the requested variable is found
  vtkIdType nRowP = primaryTab->GetNumberOfRows();
  for ( int r = 0; r < nRowP; ++ r )
    {
    if ( vars->GetValue( r ) == varName )
      {
      // Grab the data for the requested variable
      vtkAbstractArray* vals = outData->GetColumnByName( varName );
      if ( ! vals )
        {
        dfunc = 0;
        return;
        }

      dfunc = new TableColumnBucketingFunctor( vals, primaryTab->GetRow( r ) );
      return;
      }
    }

  // The variable of interest was not found in the parameter table
  dfunc = 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: vclprocessor2d.hxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: aw $ $Date: 2008-06-24 15:30:17 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX
#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX

#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/color/bcolormodifier.hxx>
#include <svtools/optionsdrawinglayer.hxx>

//////////////////////////////////////////////////////////////////////////////
// predefines
class OutputDevice;

namespace drawinglayer { namespace primitive2d {
    class TextSimplePortionPrimitive2D;
    class PolygonHairlinePrimitive2D;
    class BitmapPrimitive2D;
    class FillBitmapPrimitive2D;
    class PolyPolygonGradientPrimitive2D;
    class PolyPolygonColorPrimitive2D;
    class MetafilePrimitive2D;
    class MaskPrimitive2D;
    class AlphaPrimitive2D;
    class TransformPrimitive2D;
    class MarkerArrayPrimitive2D;
    class PointArrayPrimitive2D;
    class ModifiedColorPrimitive2D;
    class WrongSpellPrimitive2D;
    class PolygonStrokePrimitive2D;
    class ControlPrimitive2D;
    class PagePreviewPrimitive2D;
    class ChartPrimitive2D;
}}

//////////////////////////////////////////////////////////////////////////////

namespace drawinglayer
{
    namespace processor2d
    {
        class VclProcessor2D : public BaseProcessor2D
        {
        protected:
            // the destination OutDev
            OutputDevice*                                           mpOutputDevice;

            // the modifiedColorPrimitive stack
            basegfx::BColorModifierStack                            maBColorModifierStack;

            // the current transformation. Since VCL pixel renderer transforms to pixels
            // and VCL MetaFile renderer to World (logic) coordinates, the local
            // ViewInformation2D cannot directly be used, but needs to be kept up to date
            basegfx::B2DHomMatrix                                   maCurrentTransformation;

            // SvtOptionsDrawinglayer incarnation to react on diverse settings
            const SvtOptionsDrawinglayer                            maDrawinglayerOpt;

            // stack value (increment and decrement) to count how deep we are in
            // PolygonStrokePrimitive2D's decompositions (normally only one)
            sal_uInt32                                              mnPolygonStrokePrimitive2D;

            //////////////////////////////////////////////////////////////////////////////
            // common VCL rendering support

            void RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
            void RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate);
            void RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate);
            void RenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D& rFillBitmapCandidate);
            void RenderPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate);
            void RenderPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate);
            void RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rPolygonCandidate);
            void RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate);
            void RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate);
            void RenderAlphaPrimitive2D(const primitive2d::AlphaPrimitive2D& rTransCandidate);
            void RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate);
            void RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate);
            void RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate);
            void RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate);
            void RenderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate);
            void RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);
            void RenderChartPrimitive2D(const primitive2d::ChartPrimitive2D& rChartCandidate);

            /////////////////////////////////////////////////////////////////////////////
            // DrawMode adaption support
            void adaptLineToFillDrawMode() const;
            void adaptTextToFillDrawMode() const;

            /////////////////////////////////////////////////////////////////////////////
            // FormControl support
            basegfx::B2DPoint PositionAndSizeControl(const primitive2d::ControlPrimitive2D& rControlPrimitive2D);

        public:
            // constructor/destructor
            VclProcessor2D(
                const geometry::ViewInformation2D& rViewInformation,
                OutputDevice& rOutDev);
            virtual ~VclProcessor2D();

            // access to Drawinglayer configuration options
            const SvtOptionsDrawinglayer& getOptionsDrawinglayer() const { return maDrawinglayerOpt; }
        };
    } // end of namespace processor2d
} // end of namespace drawinglayer

//////////////////////////////////////////////////////////////////////////////

#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX

// eof
<commit_msg>#i10000# undo last change (AW)<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: vclprocessor2d.hxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: aw $ $Date: 2008-06-24 15:30:17 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX
#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX

#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/color/bcolormodifier.hxx>
#include <svtools/optionsdrawinglayer.hxx>

//////////////////////////////////////////////////////////////////////////////
// predefines
class OutputDevice;

namespace drawinglayer { namespace primitive2d {
    class TextSimplePortionPrimitive2D;
    class PolygonHairlinePrimitive2D;
    class BitmapPrimitive2D;
    class FillBitmapPrimitive2D;
    class PolyPolygonGradientPrimitive2D;
    class PolyPolygonColorPrimitive2D;
    class MetafilePrimitive2D;
    class MaskPrimitive2D;
    class AlphaPrimitive2D;
    class TransformPrimitive2D;
    class MarkerArrayPrimitive2D;
    class PointArrayPrimitive2D;
    class ModifiedColorPrimitive2D;
    class PolygonStrokePrimitive2D;
    class ControlPrimitive2D;
    class PagePreviewPrimitive2D;
}}

//////////////////////////////////////////////////////////////////////////////

namespace drawinglayer
{
    namespace processor2d
    {
        class VclProcessor2D : public BaseProcessor2D
        {
        protected:
            // the destination OutDev
            OutputDevice*                                           mpOutputDevice;

            // the modifiedColorPrimitive stack
            basegfx::BColorModifierStack                            maBColorModifierStack;

            // the current transformation. Since VCL pixel renderer transforms to pixels
            // and VCL MetaFile renderer to World (logic) coordinates, the local
            // ViewInformation2D cannot directly be used, but needs to be kept up to date
            basegfx::B2DHomMatrix                                   maCurrentTransformation;

            // SvtOptionsDrawinglayer incarnation to react on diverse settings
            const SvtOptionsDrawinglayer                            maDrawinglayerOpt;

            // stack value (increment and decrement) to count how deep we are in
            // PolygonStrokePrimitive2D's decompositions (normally only one)
            sal_uInt32                                              mnPolygonStrokePrimitive2D;

            //////////////////////////////////////////////////////////////////////////////
            // common VCL rendering support

            void RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
            void RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate);
            void RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate);
            void RenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D& rFillBitmapCandidate);
            void RenderPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate);
            void RenderPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate);
            void RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rPolygonCandidate);
            void RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate);
            void RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate);
            void RenderAlphaPrimitive2D(const primitive2d::AlphaPrimitive2D& rTransCandidate);
            void RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate);
            void RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate);
            void RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate);
            void RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate);
            void RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);

            /////////////////////////////////////////////////////////////////////////////
            // DrawMode adaption support
            void adaptLineToFillDrawMode() const;
            void adaptTextToFillDrawMode() const;

            /////////////////////////////////////////////////////////////////////////////
            // FormControl support
            basegfx::B2DPoint PositionAndSizeControl(const primitive2d::ControlPrimitive2D& rControlPrimitive2D);

        public:
            // constructor/destructor
            VclProcessor2D(
                const geometry::ViewInformation2D& rViewInformation,
                OutputDevice& rOutDev);
            virtual ~VclProcessor2D();

            // access to Drawinglayer configuration options
            const SvtOptionsDrawinglayer& getOptionsDrawinglayer() const { return maDrawinglayerOpt; }
        };
    } // end of namespace processor2d
} // end of namespace drawinglayer

//////////////////////////////////////////////////////////////////////////////

#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLPROCESSOR2D_HXX

// eof
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "LocalDce.h"

#include <iostream>
#include <array>
#include <unordered_set>
#include <vector>

#include <boost/dynamic_bitset.hpp>

#include "ControlFlow.h"
#include "DexClass.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "IRInstruction.h"
#include "DexUtil.h"
#include "Resolver.h"
#include "Transform.h"
#include "ParallelWalkers.h"

namespace {

constexpr const char* METRIC_DEAD_INSTRUCTIONS = "num_dead_instructions";
constexpr const char* METRIC_UNREACHABLE_INSTRUCTIONS =
    "num_unreachable_instructions";

/*
 * These instructions have observable side effects so must always be considered
 * live, regardless of whether their output is consumed by another instruction.
 */
static bool has_side_effects(DexOpcode opc) {
  switch (opc) {
  case OPCODE_RETURN_VOID:
  case OPCODE_RETURN:
  case OPCODE_RETURN_WIDE:
  case OPCODE_RETURN_OBJECT:
  case OPCODE_MONITOR_ENTER:
  case OPCODE_MONITOR_EXIT:
  case OPCODE_CHECK_CAST:
  case OPCODE_FILL_ARRAY_DATA:
  case OPCODE_THROW:
  case OPCODE_GOTO:
  case OPCODE_GOTO_16:
  case OPCODE_GOTO_32:
  case OPCODE_PACKED_SWITCH:
  case OPCODE_SPARSE_SWITCH:
  case OPCODE_IF_EQ:
  case OPCODE_IF_NE:
  case OPCODE_IF_LT:
  case OPCODE_IF_GE:
  case OPCODE_IF_GT:
  case OPCODE_IF_LE:
  case OPCODE_IF_EQZ:
  case OPCODE_IF_NEZ:
  case OPCODE_IF_LTZ:
  case OPCODE_IF_GEZ:
  case OPCODE_IF_GTZ:
  case OPCODE_IF_LEZ:
  case OPCODE_APUT:
  case OPCODE_APUT_WIDE:
  case OPCODE_APUT_OBJECT:
  case OPCODE_APUT_BOOLEAN:
  case OPCODE_APUT_BYTE:
  case OPCODE_APUT_CHAR:
  case OPCODE_APUT_SHORT:
  case OPCODE_IPUT:
  case OPCODE_IPUT_WIDE:
  case OPCODE_IPUT_OBJECT:
  case OPCODE_IPUT_BOOLEAN:
  case OPCODE_IPUT_BYTE:
  case OPCODE_IPUT_CHAR:
  case OPCODE_IPUT_SHORT:
  case OPCODE_SPUT:
  case OPCODE_SPUT_WIDE:
  case OPCODE_SPUT_OBJECT:
  case OPCODE_SPUT_BOOLEAN:
  case OPCODE_SPUT_BYTE:
  case OPCODE_SPUT_CHAR:
  case OPCODE_SPUT_SHORT:
  case OPCODE_INVOKE_VIRTUAL:
  case OPCODE_INVOKE_SUPER:
  case OPCODE_INVOKE_DIRECT:
  case OPCODE_INVOKE_STATIC:
  case OPCODE_INVOKE_INTERFACE:
  case OPCODE_INVOKE_VIRTUAL_RANGE:
  case OPCODE_INVOKE_SUPER_RANGE:
  case OPCODE_INVOKE_DIRECT_RANGE:
  case OPCODE_INVOKE_STATIC_RANGE:
  case OPCODE_INVOKE_INTERFACE_RANGE:
  case FOPCODE_PACKED_SWITCH:
  case FOPCODE_SPARSE_SWITCH:
  case FOPCODE_FILLED_ARRAY:
  case IOPCODE_LOAD_PARAM:
  case IOPCODE_LOAD_PARAM_OBJECT:
  case IOPCODE_LOAD_PARAM_WIDE:
    return true;
  default:
    return false;
  }
  not_reached();
}

template <typename... T>
std::string show(const boost::dynamic_bitset<T...>& bits) {
  std::string ret;
  to_string(bits, ret);
  return ret;
}

void remove_empty_try_regions(IRCode* code) {
  // comb the method looking for superfluous try sections that do not enclose
  // throwing opcodes; remove them. note that try sections should never be
  // nested, otherwise this won't produce the right result.
  bool encloses_throw{false};
  MethodItemEntry* try_start{nullptr};
  for (auto& mie : *code) {
    if (mie.type == MFLOW_TRY) {
      auto tentry = mie.tentry;
      if (tentry->type == TRY_START) {
        encloses_throw = false;
        try_start = &mie;
      } else if (!encloses_throw /* && tentry->type == TRY_END */) {
        try_start->type = MFLOW_FALLTHROUGH;
        try_start = nullptr;
        mie.type = MFLOW_FALLTHROUGH;
      }
    } else if (mie.type == MFLOW_OPCODE) {
      auto op = mie.insn->opcode();
      encloses_throw =
          encloses_throw || opcode::may_throw(op) || op == OPCODE_THROW;
    }
  }
}

/*
 * Update the liveness vector given that `inst` is live.
 */
void update_liveness(const IRInstruction* inst,
                     boost::dynamic_bitset<>& bliveness) {
  // The destination register is killed, so it isn't live before this.
  if (inst->dests_size()) {
    bliveness.reset(inst->dest());
    if (inst->dest_is_wide()) {
      bliveness.reset(inst->dest() + 1);
    }
  }
  // The destination of an `invoke` is its return value, which is encoded as
  // the max position in the bitvector.
  if (is_invoke(inst->opcode()) || is_filled_new_array(inst->opcode())) {
    bliveness.reset(bliveness.size() - 1);
  }
  // Source registers are live.
  for (size_t i = 0; i < inst->srcs_size(); i++) {
    bliveness.set(inst->src(i));
  }
  // `invoke-range` instructions need special handling since their sources
  // are encoded as a range.
  if (opcode::has_range(inst->opcode())) {
    for (size_t i = 0; i < inst->range_size(); i++) {
      bliveness.set(inst->range_base() + i);
    }
  }
  // The source of a `move-result` is the return value of the prior call,
  // which is encoded as the max position in the bitvector.
  if (is_move_result(inst->opcode())) {
    bliveness.set(bliveness.size() - 1);
  }
}
} // namespace

////////////////////////////////////////////////////////////////////////////////

void LocalDce::dce(DexMethod* method) {
  auto code = method->get_code();
  code->build_cfg();
  auto& cfg = code->cfg();
  auto blocks = postorder_sort(cfg.blocks());
  auto regs = method->get_code()->get_registers_size();
  std::vector<boost::dynamic_bitset<>> liveness(
      cfg.blocks().size(), boost::dynamic_bitset<>(regs + 1));
  bool changed;
  std::vector<FatMethod::iterator> dead_instructions;

  TRACE(DCE, 5, "%s\n", SHOW(method));
  TRACE(DCE, 5, "%s", SHOW(cfg));

  // Iterate liveness analysis to a fixed point.
  do {
    changed = false;
    dead_instructions.clear();
    for (auto& b : blocks) {
      auto prev_liveness = liveness.at(b->id());
      auto& bliveness = liveness.at(b->id());
      bliveness.reset();
      TRACE(DCE, 5, "B%lu: %s\n", b->id(), show(bliveness).c_str());

      // Compute live-out for this block from its successors.
      for (auto& s : b->succs()) {
        if (s->id() == b->id()) {
          bliveness |= prev_liveness;
        }
        TRACE(DCE, 5, "  S%lu: %s\n", s->id(), show(liveness[s->id()]).c_str());
        bliveness |= liveness[s->id()];
      }

      // Compute live-in for this block by walking its instruction list in
      // reverse and applying the liveness rules.
      for (auto it = b->rbegin(); it != b->rend(); ++it) {
        if (it->type != MFLOW_OPCODE) {
          continue;
        }
        bool required = is_required(it->insn, bliveness);
        if (required) {
          update_liveness(it->insn, bliveness);
        } else {
          dead_instructions.push_back(std::prev(it.base()));
        }
        TRACE(CFG,
              5,
              "%s\n%s\n",
              show(it->insn).c_str(),
              show(bliveness).c_str());
      }
      if (bliveness != prev_liveness) {
        changed = true;
      }
    }
  } while (changed);

  // Remove dead instructions.
  TRACE(DCE, 2, "%s\n", SHOW(method));
  for (auto dead : dead_instructions) {
    TRACE(DCE, 2, "DEAD: %s\n", SHOW(dead->insn));
    code->remove_opcode(dead);
  }
  m_stats.dead_instruction_count += dead_instructions.size();

  // if we deleted an instruction that may throw, we'll need to remove any
  // EDGE_THROW edges in the CFG... ideally we would just prune that edge,
  // but we can do a conservative and inefficient hack for now and just
  // rebuild the entire graph
  if (dead_instructions.size() > 0) {
    code->build_cfg();
  }

  m_stats.unreachable_instruction_count +=
      transform::remove_unreachable_blocks(code);
  remove_empty_try_regions(code);

  TRACE(DCE, 5, "=== Post-DCE CFG ===\n");
  TRACE(DCE, 5, "%s", SHOW(code->cfg()));
}

/*
 * An instruction is required (i.e., live) if it has side effects or if its
 * destination register is live.
 */
bool LocalDce::is_required(IRInstruction* inst,
                           const boost::dynamic_bitset<>& bliveness) {
  if (has_side_effects(inst->opcode())) {
    if (is_invoke(inst->opcode())) {
      const auto meth =
          resolve_method(inst->get_method(), opcode_to_search(inst));
      if (!is_pure(inst->get_method(), meth)) {
        return true;
      }
      return bliveness.test(bliveness.size() - 1);
    }
    return true;
  } else if (inst->dests_size()) {
    bool result = bliveness.test(inst->dest());
    if (inst->dest_is_wide()) {
      result |= bliveness.test(inst->dest() + 1);
    }
    return result;
  } else if (is_filled_new_array(inst->opcode())) {
    // filled-new-array passes its dest via the return-value slot, but isn't
    // inherently live like the invoke-* instructions.
    return bliveness.test(bliveness.size() - 1);
  }
  return false;
}

bool LocalDce::is_pure(DexMethodRef* ref, DexMethod* meth) {
  if (meth != nullptr && assumenosideeffects(meth)) {
    return true;
  }
  return m_pure_methods.find(ref) != m_pure_methods.end();
}

void LocalDcePass::run(DexMethod* m) {
  LocalDce().dce(m);
}

void LocalDcePass::run_pass(DexStoresVector& stores,
                            ConfigFiles& cfg,
                            PassManager& mgr) {
  if (mgr.no_proguard_rules()) {
    TRACE(DCE, 1,
        "LocalDcePass not run because no ProGuard configuration was provided.");
    return;
  }
  auto scope = build_class_scope(stores);
  auto stats = walk_methods_parallel<std::nullptr_t, LocalDce::Stats>(
      scope,
      [&](std::nullptr_t, DexMethod* m) {
        auto* code = m->get_code();
        if (code == nullptr) {
          return LocalDce::Stats();
        }
        LocalDce ldce;
        ldce.dce(m);
        return ldce.get_stats();
      },
      [](LocalDce::Stats a, LocalDce::Stats b) {
        a.dead_instruction_count += b.dead_instruction_count;
        a.unreachable_instruction_count += b.unreachable_instruction_count;
        return a;
      },
      [](int) { return nullptr; });
  mgr.incr_metric(METRIC_DEAD_INSTRUCTIONS, stats.dead_instruction_count);
  mgr.incr_metric(METRIC_UNREACHABLE_INSTRUCTIONS,
                  stats.unreachable_instruction_count);
}

static LocalDcePass s_pass;
<commit_msg>localdce: Adapt to move-result-pseudo instructions [2/N]<commit_after>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "LocalDce.h"

#include <iostream>
#include <array>
#include <unordered_set>
#include <vector>

#include <boost/dynamic_bitset.hpp>

#include "ControlFlow.h"
#include "DexClass.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "IRInstruction.h"
#include "DexUtil.h"
#include "Resolver.h"
#include "Transform.h"
#include "ParallelWalkers.h"

namespace {

constexpr const char* METRIC_DEAD_INSTRUCTIONS = "num_dead_instructions";
constexpr const char* METRIC_UNREACHABLE_INSTRUCTIONS =
    "num_unreachable_instructions";

/*
 * These instructions have observable side effects so must always be considered
 * live, regardless of whether their output is consumed by another instruction.
 */
static bool has_side_effects(DexOpcode opc) {
  switch (opc) {
  case OPCODE_RETURN_VOID:
  case OPCODE_RETURN:
  case OPCODE_RETURN_WIDE:
  case OPCODE_RETURN_OBJECT:
  case OPCODE_MONITOR_ENTER:
  case OPCODE_MONITOR_EXIT:
  case OPCODE_CHECK_CAST:
  case OPCODE_FILL_ARRAY_DATA:
  case OPCODE_THROW:
  case OPCODE_GOTO:
  case OPCODE_GOTO_16:
  case OPCODE_GOTO_32:
  case OPCODE_PACKED_SWITCH:
  case OPCODE_SPARSE_SWITCH:
  case OPCODE_IF_EQ:
  case OPCODE_IF_NE:
  case OPCODE_IF_LT:
  case OPCODE_IF_GE:
  case OPCODE_IF_GT:
  case OPCODE_IF_LE:
  case OPCODE_IF_EQZ:
  case OPCODE_IF_NEZ:
  case OPCODE_IF_LTZ:
  case OPCODE_IF_GEZ:
  case OPCODE_IF_GTZ:
  case OPCODE_IF_LEZ:
  case OPCODE_APUT:
  case OPCODE_APUT_WIDE:
  case OPCODE_APUT_OBJECT:
  case OPCODE_APUT_BOOLEAN:
  case OPCODE_APUT_BYTE:
  case OPCODE_APUT_CHAR:
  case OPCODE_APUT_SHORT:
  case OPCODE_IPUT:
  case OPCODE_IPUT_WIDE:
  case OPCODE_IPUT_OBJECT:
  case OPCODE_IPUT_BOOLEAN:
  case OPCODE_IPUT_BYTE:
  case OPCODE_IPUT_CHAR:
  case OPCODE_IPUT_SHORT:
  case OPCODE_SPUT:
  case OPCODE_SPUT_WIDE:
  case OPCODE_SPUT_OBJECT:
  case OPCODE_SPUT_BOOLEAN:
  case OPCODE_SPUT_BYTE:
  case OPCODE_SPUT_CHAR:
  case OPCODE_SPUT_SHORT:
  case OPCODE_INVOKE_VIRTUAL:
  case OPCODE_INVOKE_SUPER:
  case OPCODE_INVOKE_DIRECT:
  case OPCODE_INVOKE_STATIC:
  case OPCODE_INVOKE_INTERFACE:
  case OPCODE_INVOKE_VIRTUAL_RANGE:
  case OPCODE_INVOKE_SUPER_RANGE:
  case OPCODE_INVOKE_DIRECT_RANGE:
  case OPCODE_INVOKE_STATIC_RANGE:
  case OPCODE_INVOKE_INTERFACE_RANGE:
  case FOPCODE_PACKED_SWITCH:
  case FOPCODE_SPARSE_SWITCH:
  case FOPCODE_FILLED_ARRAY:
  case IOPCODE_LOAD_PARAM:
  case IOPCODE_LOAD_PARAM_OBJECT:
  case IOPCODE_LOAD_PARAM_WIDE:
    return true;
  default:
    return false;
  }
  not_reached();
}

template <typename... T>
std::string show(const boost::dynamic_bitset<T...>& bits) {
  std::string ret;
  to_string(bits, ret);
  return ret;
}

void remove_empty_try_regions(IRCode* code) {
  // comb the method looking for superfluous try sections that do not enclose
  // throwing opcodes; remove them. note that try sections should never be
  // nested, otherwise this won't produce the right result.
  bool encloses_throw{false};
  MethodItemEntry* try_start{nullptr};
  for (auto& mie : *code) {
    if (mie.type == MFLOW_TRY) {
      auto tentry = mie.tentry;
      if (tentry->type == TRY_START) {
        encloses_throw = false;
        try_start = &mie;
      } else if (!encloses_throw /* && tentry->type == TRY_END */) {
        try_start->type = MFLOW_FALLTHROUGH;
        try_start = nullptr;
        mie.type = MFLOW_FALLTHROUGH;
      }
    } else if (mie.type == MFLOW_OPCODE) {
      auto op = mie.insn->opcode();
      encloses_throw =
          encloses_throw || opcode::may_throw(op) || op == OPCODE_THROW;
    }
  }
}

/*
 * Update the liveness vector given that `inst` is live.
 */
void update_liveness(const IRInstruction* inst,
                     boost::dynamic_bitset<>& bliveness) {
  // The destination register is killed, so it isn't live before this.
  if (inst->dests_size()) {
    bliveness.reset(inst->dest());
    if (inst->dest_is_wide()) {
      bliveness.reset(inst->dest() + 1);
    }
  }
  auto op = inst->opcode();
  // The destination of an `invoke` is its return value, which is encoded as
  // the max position in the bitvector.
  if (is_invoke(op) || is_filled_new_array(op) ||
      inst->has_move_result_pseudo()) {
    bliveness.reset(bliveness.size() - 1);
  }
  // Source registers are live.
  for (size_t i = 0; i < inst->srcs_size(); i++) {
    bliveness.set(inst->src(i));
  }
  // `invoke-range` instructions need special handling since their sources
  // are encoded as a range.
  if (opcode::has_range(op)) {
    for (size_t i = 0; i < inst->range_size(); i++) {
      bliveness.set(inst->range_base() + i);
    }
  }
  // The source of a `move-result` is the return value of the prior call,
  // which is encoded as the max position in the bitvector.
  if (is_move_result(op) || opcode::is_move_result_pseudo(op)) {
    bliveness.set(bliveness.size() - 1);
  }
}

} // namespace

////////////////////////////////////////////////////////////////////////////////

void LocalDce::dce(DexMethod* method) {
  auto code = method->get_code();
  code->build_cfg();
  auto& cfg = code->cfg();
  auto blocks = postorder_sort(cfg.blocks());
  auto regs = method->get_code()->get_registers_size();
  std::vector<boost::dynamic_bitset<>> liveness(
      cfg.blocks().size(), boost::dynamic_bitset<>(regs + 1));
  bool changed;
  std::vector<FatMethod::iterator> dead_instructions;

  TRACE(DCE, 5, "%s\n", SHOW(method));
  TRACE(DCE, 5, "%s", SHOW(cfg));

  // Iterate liveness analysis to a fixed point.
  do {
    changed = false;
    dead_instructions.clear();
    for (auto& b : blocks) {
      auto prev_liveness = liveness.at(b->id());
      auto& bliveness = liveness.at(b->id());
      bliveness.reset();
      TRACE(DCE, 5, "B%lu: %s\n", b->id(), show(bliveness).c_str());

      // Compute live-out for this block from its successors.
      for (auto& s : b->succs()) {
        if(s->id() == b->id()) {
          bliveness |= prev_liveness;
        }
        TRACE(DCE,
              5,
              "  S%lu: %s\n",
              s->id(),
              show(liveness[s->id()]).c_str());
        bliveness |= liveness[s->id()];
      }

      // Compute live-in for this block by walking its instruction list in
      // reverse and applying the liveness rules.
      for (auto it = b->rbegin(); it != b->rend(); ++it) {
        if (it->type != MFLOW_OPCODE) {
          continue;
        }
        bool required = is_required(it->insn, bliveness);
        if (required) {
          update_liveness(it->insn, bliveness);
        } else {
          // move-result-pseudo instructions will be automatically removed
          // when their primary instruction is deleted.
          if (!opcode::is_move_result_pseudo(it->insn->opcode())) {
            dead_instructions.push_back(std::prev(it.base()));
          }
        }
        TRACE(CFG,
              5,
              "%s\n%s\n",
              show(it->insn).c_str(),
              show(bliveness).c_str());
      }
      if (bliveness != prev_liveness) {
        changed = true;
      }
    }
  } while (changed);

  // Remove dead instructions.
  TRACE(DCE, 2, "%s\n", SHOW(method));
  for (auto dead : dead_instructions) {
    TRACE(DCE, 2, "DEAD: %s\n", SHOW(dead->insn));
    code->remove_opcode(dead);
  }
  m_stats.dead_instruction_count += dead_instructions.size();

  // if we deleted an instruction that may throw, we'll need to remove any
  // EDGE_THROW edges in the CFG... ideally we would just prune that edge,
  // but we can do a conservative and inefficient hack for now and just
  // rebuild the entire graph
  if (dead_instructions.size() > 0) {
    code->build_cfg();
  }

  m_stats.unreachable_instruction_count +=
      transform::remove_unreachable_blocks(code);
  remove_empty_try_regions(code);

  TRACE(DCE, 5, "=== Post-DCE CFG ===\n");
  TRACE(DCE, 5, "%s", SHOW(code->cfg()));
}

/*
 * An instruction is required (i.e., live) if it has side effects or if its
 * destination register is live.
 */
bool LocalDce::is_required(IRInstruction* inst,
                           const boost::dynamic_bitset<>& bliveness) {
  if (has_side_effects(inst->opcode())) {
    if (is_invoke(inst->opcode())) {
      const auto meth =
          resolve_method(inst->get_method(), opcode_to_search(inst));
      if (!is_pure(inst->get_method(), meth)) {
        return true;
      }
      return bliveness.test(bliveness.size() - 1);
    }
    return true;
  } else if (inst->dests_size()) {
    bool result = bliveness.test(inst->dest());
    if (inst->dest_is_wide()) {
      result |= bliveness.test(inst->dest() + 1);
    }
    return result;
  } else if (is_filled_new_array(inst->opcode()) ||
             inst->has_move_result_pseudo()) {
    // These instructions pass their dests via the return-value slot, but
    // aren't inherently live like the invoke-* instructions.
    return bliveness.test(bliveness.size() - 1);
  }
  return false;
}

bool LocalDce::is_pure(DexMethodRef* ref, DexMethod* meth) {
  if (meth != nullptr && assumenosideeffects(meth)) {
    return true;
  }
  return m_pure_methods.find(ref) != m_pure_methods.end();
}

void LocalDcePass::run(DexMethod* m) {
  LocalDce().dce(m);
}

void LocalDcePass::run_pass(DexStoresVector& stores,
                            ConfigFiles& cfg,
                            PassManager& mgr) {
  if (mgr.no_proguard_rules()) {
    TRACE(DCE, 1,
        "LocalDcePass not run because no ProGuard configuration was provided.");
    return;
  }
  auto scope = build_class_scope(stores);
  auto stats = walk_methods_parallel<std::nullptr_t, LocalDce::Stats>(
      scope,
      [&](std::nullptr_t, DexMethod* m) {
        auto* code = m->get_code();
        if (code == nullptr) {
          return LocalDce::Stats();
        }
        LocalDce ldce;
        ldce.dce(m);
        return ldce.get_stats();
      },
      [](LocalDce::Stats a, LocalDce::Stats b) {
        a.dead_instruction_count += b.dead_instruction_count;
        a.unreachable_instruction_count += b.unreachable_instruction_count;
        return a;
      },
      [](int) { return nullptr; });
  mgr.incr_metric(METRIC_DEAD_INSTRUCTIONS, stats.dead_instruction_count);
  mgr.incr_metric(METRIC_UNREACHABLE_INSTRUCTIONS,
                  stats.unreachable_instruction_count);
}

static LocalDcePass s_pass;
<|endoftext|>


<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "Copyright (c) 2012-2013 Tokutek Inc.  All rights reserved."
#ident "$Id$"

#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "backup_test_helpers.h"
#include "backup_internal.h"
#include "backup_callbacks.h"

const char * const DEFAULT_TERM = "\033[0m";
const char * const RED_TERM = "\033[31m";
const char * const GREEN_TERM = "\033[32m";

void pass(void) {
    printf(GREEN_TERM);
    printf("[PASS]");
    printf(DEFAULT_TERM);
}

void fail(void) {
    printf(RED_TERM);
    printf("[FAIL]");
    printf(DEFAULT_TERM);
}

int systemf(const char *formatstring, ...) {
    va_list ap;
    va_start(ap, formatstring);
    char string[1000];
    int r = vsnprintf(string, sizeof(string), formatstring, ap);
    va_end(ap);
    assert(r<(int)sizeof(string));
    return system(string);
}

int openf(int flags, int mode, const char *formatstring, ...) {
    va_list ap;
    va_start(ap, formatstring);
    char string[PATH_MAX];
    int r = vsnprintf(string, sizeof(string), formatstring, ap);
    assert(r<=(int)sizeof(string));
    return open(string, flags, mode);
}

void setup_destination(void) {
    char *dst = get_dst();
    systemf("rm -rf %s", dst);
    systemf("mkdir %s",  dst);
    free(dst);
}

void setup_source(void) {
    char *src = get_src();
    systemf("rm -rf %s", src);
    systemf("mkdir %s",  src);
    free(src);
}

void setup_dirs(void) {
    char *src = get_src();
    systemf("touch %s/foo", src);
    systemf("echo hello > %s/bar.data", src);
    systemf("mkdir %s/subdir", src);
    systemf("echo there > %s/subdir/sub.data", src);
    free(src);
}

void cleanup_dirs(void) {
    {
        char *dst = get_dst();
        systemf("rm -rf %s", dst);
        free(dst);
    }
    {
        char *src = get_src();
        systemf("rm -rf %s", src);
        free(src);
    }
}

struct backup_thread_extra_t {
    char *             src_dir;
    char *             dst_dir;
    backup_poll_fun_t  poll_fun;
    void*              poll_extra;
    backup_error_fun_t error_fun;
    void*              error_extra;
    int                expect_return_result;
};
static void* start_backup_thread_fun(void *backup_extra_v) {
    backup_thread_extra_t *backup_extra = (backup_thread_extra_t*)backup_extra_v;
    const char *srcs[1] = {backup_extra->src_dir};
    const char *dsts[1] = {backup_extra->dst_dir};
    int r = tokubackup_create_backup(srcs, dsts, 1,
                                     backup_extra->poll_fun,  backup_extra->poll_extra,
                                     backup_extra->error_fun, backup_extra->error_extra);
    if (r!=backup_extra->expect_return_result) {
        printf("%s:%d Got error %d, Expected %d\n", __FILE__, __LINE__, r, backup_extra->expect_return_result);
    }
    assert(r==backup_extra->expect_return_result);
    if (backup_extra->src_dir) free(backup_extra->src_dir);
    if (backup_extra->dst_dir) free(backup_extra->dst_dir);
    return backup_extra_v;
}

void start_backup_thread_with_funs(pthread_t *thread,
                                   char *src_dir, char *dst_dir,
                                   backup_poll_fun_t poll_fun, void *poll_extra,
                                   backup_error_fun_t error_fun, void *error_extra,
                                   int expect_return_result) {
    backup_thread_extra_t *p = new backup_thread_extra_t;
    *p = {src_dir, dst_dir, poll_fun, poll_extra, error_fun, error_extra, expect_return_result};
    int r = pthread_create(thread, NULL, start_backup_thread_fun, p);
    assert(r==0);
}

int simple_poll_fun(float progress, const char *progress_string, void *poll_extra) {
    assert(progress>=0 && progress <= 1);
    assert(progress_string!=NULL);
    assert(poll_extra==NULL);
    return 0;
}

static void expect_no_error_fun(int error_number, const char *error_string, void *backup_extra __attribute__((__unused__))) {
    fprintf(stderr, "Error, I expected no error function to run, but received error #%d: %s\n", error_number, error_string);
    abort();
}

void start_backup_thread(pthread_t *thread) {
    char *src = get_src();
    char *dst = get_dst();
    start_backup_thread_with_funs(thread, src, dst, simple_poll_fun, NULL, expect_no_error_fun, NULL, BACKUP_SUCCESS);
}

void finish_backup_thread(pthread_t thread) {
    void *retval;
    int r = pthread_join(thread, &retval);
    assert(r==0);
    assert(retval!=NULL);
    free(retval);
}

static const char *test_name = NULL;

char *get_dst(void) {
    assert(test_name);
    size_t size = strlen(test_name)+100;
    char s[size];
    int r = snprintf(s, sizeof(s), "%s.backup", test_name);
    assert(r<(int)size);
    char *result = strdup(s);
    assert(result);
    return result;
}

char *get_src(void) {
    assert(test_name);
    size_t size = strlen(test_name)+100;
    char s[size];
    int r = snprintf(s, size, "%s.source", test_name);
    assert(r<(int)size);
    char *result = strdup(s);
    assert(result);
    return result;
}

//////////////////////////////////////////////////////////////////
//
// Dummy callbacks:
//
int dummy_poll(float progress, const char *string, void *extra)
{
    if(extra)
    progress++;
    string++;
    return 0;
}

void dummy_error(int error, const char *string, void *extra)
{
    if(extra)
    error++;
    string++;
}

unsigned long dummy_throttle(void)
{
    return ULONG_MAX;
}

///
int main (int argc, const char *argv[]) {
    const char *new_argv[argc];
    int  new_argc = 0;
    int  argnum   = 0;

    new_argv[new_argc++] = argv[argnum++];  // copy command

    while (argnum < argc) {
#define TESTNAME "--testname"
        if (0==strcmp(argv[argnum], TESTNAME)) {
            assert(argnum+1 < argc);
            test_name = argv[argnum+1];
            argnum+=2;
        } else if (0==strncmp(argv[argnum], TESTNAME "=", sizeof(TESTNAME))) {
            test_name = argv[argnum]+sizeof(TESTNAME);
            argnum++;
        } else {
            new_argv[new_argc++] = argv[argnum];
        }
    }
    if (test_name==NULL) test_name=argv[0]; // make the function work with no arguments.
    return test_main(new_argc, new_argv);
}
<commit_msg>refs #6185 Fixed mismatched free (should have been delete).<commit_after>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "Copyright (c) 2012-2013 Tokutek Inc.  All rights reserved."
#ident "$Id$"

#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "backup_test_helpers.h"
#include "backup_internal.h"
#include "backup_callbacks.h"

const char * const DEFAULT_TERM = "\033[0m";
const char * const RED_TERM = "\033[31m";
const char * const GREEN_TERM = "\033[32m";

void pass(void) {
    printf(GREEN_TERM);
    printf("[PASS]");
    printf(DEFAULT_TERM);
}

void fail(void) {
    printf(RED_TERM);
    printf("[FAIL]");
    printf(DEFAULT_TERM);
}

int systemf(const char *formatstring, ...) {
    va_list ap;
    va_start(ap, formatstring);
    char string[1000];
    int r = vsnprintf(string, sizeof(string), formatstring, ap);
    va_end(ap);
    assert(r<(int)sizeof(string));
    return system(string);
}

int openf(int flags, int mode, const char *formatstring, ...) {
    va_list ap;
    va_start(ap, formatstring);
    char string[PATH_MAX];
    int r = vsnprintf(string, sizeof(string), formatstring, ap);
    assert(r<=(int)sizeof(string));
    return open(string, flags, mode);
}

void setup_destination(void) {
    char *dst = get_dst();
    systemf("rm -rf %s", dst);
    systemf("mkdir %s",  dst);
    free(dst);
}

void setup_source(void) {
    char *src = get_src();
    systemf("rm -rf %s", src);
    systemf("mkdir %s",  src);
    free(src);
}

void setup_dirs(void) {
    char *src = get_src();
    systemf("touch %s/foo", src);
    systemf("echo hello > %s/bar.data", src);
    systemf("mkdir %s/subdir", src);
    systemf("echo there > %s/subdir/sub.data", src);
    free(src);
}

void cleanup_dirs(void) {
    {
        char *dst = get_dst();
        systemf("rm -rf %s", dst);
        free(dst);
    }
    {
        char *src = get_src();
        systemf("rm -rf %s", src);
        free(src);
    }
}

struct backup_thread_extra_t {
    char *             src_dir;
    char *             dst_dir;
    backup_poll_fun_t  poll_fun;
    void*              poll_extra;
    backup_error_fun_t error_fun;
    void*              error_extra;
    int                expect_return_result;
};
static void* start_backup_thread_fun(void *backup_extra_v) {
    backup_thread_extra_t *backup_extra = (backup_thread_extra_t*)backup_extra_v;
    const char *srcs[1] = {backup_extra->src_dir};
    const char *dsts[1] = {backup_extra->dst_dir};
    int r = tokubackup_create_backup(srcs, dsts, 1,
                                     backup_extra->poll_fun,  backup_extra->poll_extra,
                                     backup_extra->error_fun, backup_extra->error_extra);
    if (r!=backup_extra->expect_return_result) {
        printf("%s:%d Got error %d, Expected %d\n", __FILE__, __LINE__, r, backup_extra->expect_return_result);
    }
    assert(r==backup_extra->expect_return_result);
    if (backup_extra->src_dir) free(backup_extra->src_dir);
    if (backup_extra->dst_dir) free(backup_extra->dst_dir);
    return backup_extra_v;
}

void start_backup_thread_with_funs(pthread_t *thread,
                                   char *src_dir, char *dst_dir,
                                   backup_poll_fun_t poll_fun, void *poll_extra,
                                   backup_error_fun_t error_fun, void *error_extra,
                                   int expect_return_result) {
    backup_thread_extra_t *p = new backup_thread_extra_t;
    *p = {src_dir, dst_dir, poll_fun, poll_extra, error_fun, error_extra, expect_return_result};
    int r = pthread_create(thread, NULL, start_backup_thread_fun, p);
    assert(r==0);
}

int simple_poll_fun(float progress, const char *progress_string, void *poll_extra) {
    assert(progress>=0 && progress <= 1);
    assert(progress_string!=NULL);
    assert(poll_extra==NULL);
    return 0;
}

static void expect_no_error_fun(int error_number, const char *error_string, void *backup_extra __attribute__((__unused__))) {
    fprintf(stderr, "Error, I expected no error function to run, but received error #%d: %s\n", error_number, error_string);
    abort();
}

void start_backup_thread(pthread_t *thread) {
    char *src = get_src();
    char *dst = get_dst();
    start_backup_thread_with_funs(thread, src, dst, simple_poll_fun, NULL, expect_no_error_fun, NULL, BACKUP_SUCCESS);
}

void finish_backup_thread(pthread_t thread) {
    void *retval;
    int r = pthread_join(thread, &retval);
    assert(r==0);
    assert(retval!=NULL);
    delete(retval);
}

static const char *test_name = NULL;

char *get_dst(void) {
    assert(test_name);
    size_t size = strlen(test_name)+100;
    char s[size];
    int r = snprintf(s, sizeof(s), "%s.backup", test_name);
    assert(r<(int)size);
    char *result = strdup(s);
    assert(result);
    return result;
}

char *get_src(void) {
    assert(test_name);
    size_t size = strlen(test_name)+100;
    char s[size];
    int r = snprintf(s, size, "%s.source", test_name);
    assert(r<(int)size);
    char *result = strdup(s);
    assert(result);
    return result;
}

//////////////////////////////////////////////////////////////////
//
// Dummy callbacks:
//
int dummy_poll(float progress, const char *string, void *extra)
{
    if(extra)
    progress++;
    string++;
    return 0;
}

void dummy_error(int error, const char *string, void *extra)
{
    if(extra)
    error++;
    string++;
}

unsigned long dummy_throttle(void)
{
    return ULONG_MAX;
}

///
int main (int argc, const char *argv[]) {
    const char *new_argv[argc];
    int  new_argc = 0;
    int  argnum   = 0;

    new_argv[new_argc++] = argv[argnum++];  // copy command

    while (argnum < argc) {
#define TESTNAME "--testname"
        if (0==strcmp(argv[argnum], TESTNAME)) {
            assert(argnum+1 < argc);
            test_name = argv[argnum+1];
            argnum+=2;
        } else if (0==strncmp(argv[argnum], TESTNAME "=", sizeof(TESTNAME))) {
            test_name = argv[argnum]+sizeof(TESTNAME);
            argnum++;
        } else {
            new_argv[new_argc++] = argv[argnum];
        }
    }
    if (test_name==NULL) test_name=argv[0]; // make the function work with no arguments.
    return test_main(new_argc, new_argv);
}
<|endoftext|>