partitioner = function(md,...args) {
function zeros(...args) {
	var array = [];
	for (var i = 0; i < args[0]; ++i) {
		array.push(args.length == 1 ? 0 : zeros(args.slice(1)));
	}
	return array;
}
//PARTITIONER - partition mesh 
//
//   List of options to partitioner: 
//
//   package: 'chaco', 'metis' or 'scotch'
//   npart: number of partitions.
//   weighting: 'on' or 'off': default off
//   section:  1 by defaults(1=bisection, 2=quadrisection, 3=octasection)
//   recomputeadjacency:  'on' by default (set to 'off' to compute existing one)
//   Output: md.qmu.partition recover the partition vector
//   
//   Usage:
//      md=partitioner(md,'package','chaco','npart',100,'weighting','on');
//

//get options: 
options= new pairoptions(args.slice());

//set defaults
options.addfielddefault('package','chaco');
options.addfielddefault('npart',10);
options.addfielddefault('weighting','on');
options.addfielddefault('section',1);
options.addfielddefault('recomputeadjacency','on');

//get package: 
package=options.getfieldvalue('package');
npart=options.getfieldvalue('npart');
recomputeadjacency=options.getfieldvalue('recomputeadjacency');

if(md.mesh.dimension()===3) {
	//partitioning essentially happens in 2D. So partition in 2D, then 
    //extrude the partition vector vertically. 
	md3d=md; //save  for later
	md.mesh.elements=md.mesh.elements2d;
	md.mesh.x=md.mesh.x2d;
	md.mesh.y=md.mesh.y2d;
	md.mesh.numberofvertices=md.mesh.numberofvertices2d;
	md.mesh.numberofelements=md.mesh.numberofelements2d;
	md.qmu.vertex_weight=[];
	md.mesh.vertexconnectivity=[];
}

//adjacency matrix if needed:
if (recomputeadjacency.toLowerCase() === 'on') {
	md=adjacency(md);
} else {
	disp('skipping adjacency matrix computation as requested in the options');
}

if (package.toLowerCase() === 'chaco') {

	//  default method (from chaco.m)
	method=[[1], [1], [0], [0], [1], [1], [50], [0], [.001], [7654321]];
	method[0]=3;    //  global method (3=inertial (geometric))
	method[2]=0;    //  vertex weights (0=off, 1=on)

	//specify bisection
	method[5]=options.getfieldvalue('section');//  ndims (1=bisection, 2=quadrisection, 3=octasection)

    //are we using weights? 
    if (options.getfieldvalue('weighting').toLowerCase() === 'on') {
            weights=Math.floor(md.qmu.vertex_weight/math.min(md.qmu.vertex_weight));
            method[2]=1;
    } else { 
        weights=[];
    }

	//  partition into nparts
    if (md.mesh.classname() === 'mesh2d') {
        //TODO convert to column
		part=Chaco(md.qmu.adjacency,weights,[],md.mesh.x, md.mesh.y,zeros(md.mesh.numberofvertices,1),method,npart,[])+1; //index partitions from 1 up. like metis.
	} else {
        //TODO convert to column
		part=Chaco(md.qmu.adjacency,weights,[],md.mesh.x, md.mesh.y,md.mesh.z,method,npart,[])+1; //index partitions from 1 up. like metis.
	}
} else if (package.toLowerCase() === 'scotch') {
	//are we using weights? 
    if (options.getfieldvalue('weighting').toLowerCase() === 'on') {
		weights=floor(md.qmu.vertex_weight/min(md.qmu.vertex_weight));
	}
	maptab=Scotch(md.qmu.adjacency,[],weights,[],'cmplt',[npart]);

    part=maptab.filter(function(x, idx) { return idx });//index partitions from 1 up. like metis.
} else if (package.toLowerCase() === 'linear') {

    part = part.slice(0, md.mesh.numberofvertices);
} else if (package.toLowerCase() === 'metis') {
	[element_partitioning,part]=MeshPartition(md.mesh,md.qmu.numberofpartitions);
} else {
	console.error(['partitioner error message: could not find ' + package + ' partitioner']);
	//help partitioner
}

//extrude if we are in 3d
if (md.mesh.dimension() === 3) {
	md3d.qmu.vertex_weight=md.qmu.vertex_weight;
	md3d.qmu.adjacency=md.qmu.adjacency;
	md=md3d;

    //TODO convert to column vec
	part=project3d(md,'vector',part,'type','node');
}

md.qmu.partition=part;

return md;
};
