/**************************************************************************
Copyright (C) 2007, 2008 Thomas Finley, tfinley@gmail.com

This file is part of PyGLPK.

PyGLPK is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

PyGLPK is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyGLPK. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

#include "2to3.h"

#include "bar.h"
#include "structmember.h"
#include "util.h"
#include <string.h>

#define LP (self->py_bc->py_lp->lp)

//#define USE_BAR_GC // Cyclic GC on the bar incurs a roughly 10-20% cost.

static int Bar_traverse(BarObject *self, visitproc visit, void *arg) {
  Py_VISIT((PyObject*)self->py_bc);
  return 0;
}

static int Bar_clear(BarObject *self) {
  if (self->weakreflist != NULL) {
    PyObject_ClearWeakRefs((PyObject*)self);
  }
  Py_CLEAR(self->py_bc);
  return 0;
}

static void Bar_dealloc(BarObject *self) {
#ifdef USE_BAR_GC
  Bar_clear(self);
#else
  if (self->weakreflist != NULL) {
    PyObject_ClearWeakRefs((PyObject*)self);
  }
  Py_DECREF(self->py_bc);
#endif
  Py_TYPE(self)->tp_free((PyObject*)self);
}

/** Create a new bar collection object. */
BarObject *Bar_New(BarColObject *py_bc, int index) {
  BarObject *b = NULL;
  // Check whether the bar is valid.
  if (index < 0 || index >= BarCol_Size(py_bc)) {
    PyErr_SetString(PyExc_IndexError, "row or column index out of bounds");
    return b;
  }
  // Input all these fun structures and things.
#ifdef USE_BAR_GC
  b = (BarObject*)PyObject_GC_New(BarObject, &BarType);
#else
  b = (BarObject*)PyObject_New(BarObject, &BarType);
#endif
  Py_INCREF(py_bc);
  b->weakreflist = NULL;
  b->py_bc = py_bc;
  b->r = py_bc->r;
  b->index = index;
#ifdef USE_BAR_GC
  PyObject_GC_Track(b);
#endif
  return b;
}

static PyObject* Bar_Str(BarObject *self) {
  // Returns a string representation of this object.
  return PyString_FromFormat
    ("<%s, %s %d of %s %p>", Py_TYPE(self)->tp_name,
     Bar_Row(self)?"row":"col", Bar_Index(self),
     LPXType.tp_name, self->py_bc->py_lp);
}

int Bar_Valid(BarObject *self, int except) {
  if (self->index < BarCol_Size(self->py_bc)) return 1;
  // It's invalid!  Curses.
  if (except) {
    PyErr_SetString(PyExc_RuntimeError, "row or column no longer valid");
  }
  return 0;
}

PyObject *Bar_GetMatrix(BarObject *self) {
  int nnz, i;
  PyObject *retval;
  int (*get_mat)(glp_prob*,int,int[],double[]);

  if (!Bar_Valid(self, 1)) return NULL;

  get_mat = Bar_Row(self) ? glp_get_mat_row : glp_get_mat_col;
  i = Bar_Index(self)+1;
  nnz = get_mat(LP, i, NULL, NULL);
  retval = PyList_New(nnz);
  if (nnz==0 || retval==NULL) return retval;

  int*ind = (int*)calloc(nnz,sizeof(int));
  double*val = (double*)calloc(nnz,sizeof(double));
  nnz = get_mat(LP, i, ind-1, val-1);

  for (i=0; i<nnz; ++i) {
    PyList_SET_ITEM(retval, i, Py_BuildValue("id", ind[i]-1, val[i]));
  }
  free(ind);
  free(val);
  if (PyList_Sort(retval)) {
    Py_DECREF(retval);
    return NULL;
  }
  return retval;
}

int Bar_SetMatrix(BarObject *self, PyObject *newvals) {
  int len, *ind = NULL;
  double *val = NULL;
  if (!Bar_Valid(self, 1)) return -1;
  // Get the alternate length (e.g., col length if this is a row).
  len = (Bar_Row(self) ? glp_get_num_cols : glp_get_num_rows)(LP);
  // Now, attempt to convert the input item.
  if (newvals == NULL || newvals == Py_None) {
    len = 0;
  } else {
    PyObject*bc = Bar_Row(self) ?
      self->py_bc->py_lp->cols : self->py_bc->py_lp->rows;
    if (!util_extract_if(newvals, bc, &len, &ind, &val)) return -1;
  }
  // Input the stuff into the LP constraint matrix.
  (Bar_Row(self) ? glp_set_mat_row : glp_set_mat_col)
    (LP, Bar_Index(self)+1, len, ind-1, val-1);
  // Free the memory.
  if (len) {
    free(ind);
    free(val);
  }
  return 0;
}

static PyObject* Bar_richcompare(BarObject *v, PyObject *w, int op) {
  if (!Bar_Check(w)) {
    switch (op) {
    case Py_EQ: Py_RETURN_FALSE;
    case Py_NE: Py_RETURN_FALSE;
    default:
      Py_INCREF(Py_NotImplemented);
      return Py_NotImplemented;
    }
  }
  // Now we know it is a bar object.
  BarObject *x = (BarObject *)w;
  if (v->py_bc != x->py_bc) {
    // "Inherit" the judgement of our containing objects.
    return PyObject_RichCompare((PyObject*)v->py_bc, (PyObject*)x->py_bc, op);
  }
  // Now we know it is a bar object, and part of the same bar
  // collection no less.
  switch (op) {
  case Py_EQ: if (v->index==x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  case Py_NE: if (v->index!=x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  case Py_LE: if (v->index<=x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  case Py_GE: if (v->index>=x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  case Py_LT: if (v->index< x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  case Py_GT: if (v->index> x->index) Py_RETURN_TRUE; else Py_RETURN_FALSE;
  default:
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
  }
}

/********** ABSTRACT PROTOCOL FUNCTIONS *******/

/*int Bar_Size(BarObject* self) {
  return (Bar_Row(self) ? glp_get_num_rows : glp_get_num_cols)(LP); }
static PyObject* Bar_subscript(BarObject *self, PyObject *item) {
  printf("bar subscript\n"); Py_RETURN_NONE;}
static int Bar_ass_subscript(BarObject *self,PyObject *item,PyObject *value) {
 printf("bar ass subscript\n"); return 0; }
static PyObject* Bar_item(BarObject *self, int index) {
  printf("bar item\n"); Py_RETURN_NONE; }
static int Bar_ass_item(BarObject *self, int index, PyObject *v) {
  printf("bar ass item\n"); return 0; }*/

/****************** GET-SET-ERS ***************/
static PyObject* Bar_getname(BarObject *self, void *closure) {
  if (!Bar_Valid(self, 1)) return NULL;
  const char *name = (Bar_Row(self) ? glp_get_row_name : glp_get_col_name)
    (LP, Bar_Index(self)+1);
  if (name==NULL) Py_RETURN_NONE;
  return PyString_FromString(name);
}
static int Bar_setname(BarObject *self, PyObject *value, void *closure) {
  char *name;
  if (!Bar_Valid(self, 1)) return -1;
  if (value==NULL || value==Py_None) {
    (Bar_Row(self) ? glp_set_row_name : glp_set_col_name)
      (LP, Bar_Index(self)+1, NULL);
    return 0;
  }
  name = PyString_AsString(value);
  if (name==NULL) return -1;
  if (PyString_Size(value) > 255) {
    PyErr_SetString(PyExc_ValueError, "name may be at most 255 chars");
    return -1;
  }
  (Bar_Row(self) ? glp_set_row_name : glp_set_col_name)
    (LP, Bar_Index(self)+1, name);
  return 0;
}

static PyObject* Bar_getvalid(BarObject *self, void *closure) {
  return PyBool_FromLong(Bar_Valid(self, 0));
}

static PyObject* Bar_getbounds(BarObject *self, void *closure) {
  double lb, ub;
  int i;
  if (!Bar_Valid(self, 1)) return NULL;

  i = Bar_Index(self)+1;
  lb = (Bar_Row(self) ? glp_get_row_lb : glp_get_col_lb)(LP, i);
  ub = (Bar_Row(self) ? glp_get_row_ub : glp_get_col_ub)(LP, i);

  switch ((Bar_Row(self) ? glp_get_row_type : glp_get_col_type)(LP, i)) {
  case GLP_FR: return Py_BuildValue("OO", Py_None, Py_None);
  case GLP_LO: return Py_BuildValue("fO", lb, Py_None);
  case GLP_UP: return Py_BuildValue("Of", Py_None, ub);
  case GLP_DB: case GLP_FX: return Py_BuildValue("ff", lb, ub);
  }
  // We should never be here.
  PyErr_SetString(PyExc_SystemError, "unrecognized bound type");
  return NULL;
}
static int Bar_setbounds(BarObject *self, PyObject *value, void *closure) {
  int i;
  double lb=0.0, ub=0.0;
  PyObject *lo, *uo;

  void (*bounder)(glp_prob*,int,int,double,double) = NULL;
  if (!Bar_Valid(self, 1)) return -1;

  i = Bar_Index(self)+1;
  bounder = Bar_Row(self) ? glp_set_row_bnds : glp_set_col_bnds;

  if (value==NULL || value==Py_None) {
    // We want it unbounded and free.
    bounder(LP, i, GLP_FR, 0.0, 0.0);
    return 0;
  }

  if (PyNumber_Check(value)) {
    // We want an equality fixed bound.
    value = PyNumber_Float(value);
    if (!value) return -1;
    lb = PyFloat_AsDouble(value);
    Py_DECREF(value);
    if (PyErr_Occurred()) return -1;
    bounder(LP, i, GLP_FX, lb, lb);
    return 0;
  }

  char t_error[] = "bounds must be set to None, number, or pair of numbers";
  if (!PyTuple_Check(value) || PyTuple_GET_SIZE(value)!=2) {
    PyErr_SetString(PyExc_TypeError, t_error);
    return -1;
  }

  // Get the lower and upper object. These references are borrowed.
  lo = PyTuple_GetItem(value, 0);
  uo = PyTuple_GetItem(value, 1);

  if ((lo!=Py_None && !PyNumber_Check(lo)) ||
      (uo!=Py_None && !PyNumber_Check(uo))) {
    PyErr_SetString(PyExc_TypeError, t_error);
    return -1;
  }
  if (lo==Py_None) lo=NULL; else lb=PyFloat_AsDouble(lo);
  if (PyErr_Occurred()) return -1;
  if (uo==Py_None) uo=NULL; else ub=PyFloat_AsDouble(uo);
  if (PyErr_Occurred()) return -1;

  if (!lo && !uo)	bounder(LP, i, GLP_FR, 0.0, 0.0);
  else if (!uo)		bounder(LP, i, GLP_LO, lb, 0.0);
  else if (!lo)		bounder(LP, i, GLP_UP, 0.0, ub);
  else if (lb<=ub)	bounder(LP, i, lb==ub ? GLP_FX : GLP_DB, lb, ub);
  else {
    PyErr_SetString(PyExc_ValueError, "lower bound cannot exceed upper bound");
    return -1;
  }
  return 0;
}

static PyObject* Bar_getnumnonzero(BarObject *self, void *closure) {
  if (!Bar_Valid(self, 1)) return NULL;
  return PyInt_FromLong((Bar_Row(self) ? glp_get_mat_row : glp_get_mat_col)
			(LP, Bar_Index(self)+1, NULL, NULL));
}

static PyObject* Bar_getmatrix(BarObject *self, void *closure) {
  return Bar_GetMatrix(self);
}
static int Bar_setmatrix(BarObject *self, PyObject *value, void *closure) {
  return Bar_SetMatrix(self, value);
}

static PyObject* Bar_getisrow(BarObject *self, void *closure) {
  return PyBool_FromLong(Bar_Row(self));
}
static PyObject* Bar_getiscol(BarObject *self, void *closure) {
  return PyBool_FromLong(!(Bar_Row(self)));
}

static PyObject* Bar_getscale(BarObject *self, void *closure) {
  if (!Bar_Valid(self, 1)) return NULL;
  int index;
  double scale;
  index = Bar_Index(self);
  scale = (Bar_Row(self) ? glp_get_rii : glp_get_sjj)(LP,index+1);
  return PyFloat_FromDouble(scale);
}

static int Bar_setscale(BarObject *self, PyObject *value, void *closure){
  if (!Bar_Valid(self, 1)) return -1;
  if (value==NULL) {
    PyErr_SetString(PyExc_AttributeError, "cannot delete scale");
    return -1;
  }
  if (PyNumber_Check(value)) {
    int index;
    double scale;
    index = Bar_Index(self);
    scale = PyFloat_AsDouble(value);
    if (PyErr_Occurred()) return -1;
    if (scale <= 0.0) {
      PyErr_SetString(PyExc_ValueError, "scale factors must be positive");
      return -1;
    }
    (Bar_Row(self) ? glp_set_rii : glp_set_sjj)(LP, index+1, scale);
  } else {
    PyErr_SetString(PyExc_TypeError, "scale factors must be numeric");
    return -1;
  }
  return 0;
}

static PyObject* Bar_getstatus(BarObject *self, void *closure) {
  if (!Bar_Valid(self, 1)) return NULL;
  int index, status;
  index = Bar_Index(self);
  status = (Bar_Row(self) ? glp_get_row_stat : glp_get_col_stat)(LP,index+1);
  switch (status) {
  case GLP_BS: return PyString_FromString("bs");
  case GLP_NL: return PyString_FromString("nl");
  case GLP_NU: return PyString_FromString("nu");
  case GLP_NF: return PyString_FromString("nf");
  case GLP_NS: return PyString_FromString("ns");
  default:
    PyErr_Format(PyExc_RuntimeError, "unknown status %d detected", status);
    return NULL;
  }
}
static int Bar_setstatus(BarObject *self, PyObject *value, void *closure) {
  if (!Bar_Valid(self, 1)) return -1;
  if (value==NULL) {
    PyErr_SetString(PyExc_AttributeError, "cannot delete status");
    return -1;
  }
  char *sstr = PyString_AsString(value);
  if (sstr == NULL) return -1;
  if (sstr[0]==0 || sstr[1]==0 || sstr[2]!=0) {
    PyErr_SetString(PyExc_ValueError, "status strings must be length 2");
    return -1;
  }
  int status;
  // Whee...
  if      (!strncmp("bs", sstr, 2)) status=GLP_BS;
  else if (!strncmp("nl", sstr, 2)) status=GLP_NL;
  else if (!strncmp("nu", sstr, 2)) status=GLP_NU;
  else if (!strncmp("nf", sstr, 2)) status=GLP_NF;
  else if (!strncmp("ns", sstr, 2)) status=GLP_NS;
  else {
    PyErr_Format
      (PyExc_ValueError, "status string value '%s' unrecognized", sstr);
    return -1;
  }
  (Bar_Row(self) ? glp_set_row_stat : glp_set_col_stat)
    (LP, Bar_Index(self)+1, status);
  return 0;
}

static PyObject* Bar_getkind(BarObject *self, void *closure) {
  PyObject *retval=NULL;
  int kind = GLP_CV;
  if (!Bar_Valid(self, 1)) return NULL;
  if (!Bar_Row(self)) {
    kind = glp_get_col_kind(LP, Bar_Index(self)+1);
  }
  switch (kind) {
  case GLP_CV: retval = (PyObject*)&PyFloat_Type; break;
  case GLP_IV: retval = (PyObject*)&PyInt_Type;   break;
  case GLP_BV: retval = (PyObject*)&PyBool_Type;  break;
  default:
    PyErr_SetString(PyExc_RuntimeError,
		    "unexpected variable kind encountered");
    return NULL;
  }
  Py_INCREF(retval);
  return retval;
}
static int Bar_setkind(BarObject *self, PyObject *value, void *closure) {
  if (value==(PyObject*)&PyFloat_Type) {
    // Float indicates a continuous variables.
    if (Bar_Row(self)) return 0;
    glp_set_col_kind(LP, Bar_Index(self)+1, GLP_CV);
    return 0;
  } else if (value==(PyObject*)&PyInt_Type) {
    // Integer indicates an integer variable.
    if (Bar_Row(self)) {
      PyErr_SetString(PyExc_ValueError, "row variables cannot be integer");
      return -1;
    }
    glp_set_col_kind(LP, Bar_Index(self)+1, GLP_IV);
    return 0;
  } else if (value==(PyObject*)&PyBool_Type) {
    // Boolean indicates a binary variable.
    if (Bar_Row(self)) {
      PyErr_SetString(PyExc_ValueError, "row variables cannot be binary");
      return -1;
    }
    glp_set_col_kind(LP, Bar_Index(self)+1, GLP_BV);
    return 0;
  } else {
    PyErr_SetString(PyExc_ValueError,
		    "either the type float, int, or bool is required");
    return -1;
  }
}

/****************** THE DUAL/PRIMAL GETTING CODE **********/

// Indexed by (last_solver*4 + isdual*2 + isrow)
static double(*rowcol_primdual_funcptrs[])(glp_prob*,int) = {
  glp_get_col_prim, glp_get_row_prim, glp_get_col_dual, glp_get_row_dual,
  glp_ipt_col_prim, glp_ipt_row_prim, glp_ipt_col_dual, glp_ipt_row_dual,
  glp_mip_col_val,  glp_mip_row_val,   NULL,            NULL };

static PyObject* Bar_getvarval(BarObject *self, void *closure) {
  if (!Bar_Valid(self, 1)) return NULL;
  // Compute what we need to index to get the appropriate function pointer.
  int isrow = Bar_Row(self) ? 1 : 0;
  int isdual = closure==NULL ? 0 : 1;
  int last = self->py_bc->py_lp->last_solver;
  if (last < 0) last = 0; // If no solver called yet, assume simplex is OK.
  if (last > 2) {
    PyErr_Format(PyExc_RuntimeError,
		 "bad internal state for last solver identifier: %d", last);
    return NULL;
  }
  // Get and verify that function pointer.
  double(*valfunc)(glp_prob*,int) =
    rowcol_primdual_funcptrs[last*4 + isdual*2 + isrow];
  if (valfunc==NULL) {
    PyErr_SetString(PyExc_RuntimeError,
		    "dual values do not exist for MIP solver");
    return NULL;
  }
  // Get whatever sort of variable this is and return it.
  return PyFloat_FromDouble(valfunc(LP, Bar_Index(self)+1));
}

static PyObject* Bar_getspecvarval(BarObject *self,
				   double(*valfuncs[])(glp_prob*, int)) {
  if (!Bar_Valid(self, 1)) return NULL;
  double(*valfunc)(glp_prob*, int) = valfuncs[Bar_Row(self) ? 1 : 0];
  return PyFloat_FromDouble(valfunc(LP, Bar_Index(self)+1));
}

static PyObject* Bar_getspecvarvalm(BarObject *self,
				    double(*valfuncs[])(glp_prob*, int)) {
  if (!Bar_Valid(self, 1)) return NULL;
  if (glp_get_num_int(LP) == 0) {
    PyErr_SetString(PyExc_TypeError,
		    "MIP values require mixed integer problem");
    return NULL;
  }
  double(*valfunc)(glp_prob*, int) = valfuncs[Bar_Row(self) ? 1 : 0];
  return PyFloat_FromDouble(valfunc(LP, Bar_Index(self)+1));
}

/****************** OBJECT DEFINITION *********/

//static PySequenceMethods Bar_as_sequence = {
//  (lenfunc)Bar_Size,			/* sq_length */
//  0,					/* sq_concat */
//  0,					/* sq_repeat */
//  (ssizeargfunc)Bar_item,		/* sq_item */
//  0, //(intintargfunc)svector_slice,	/* sq_slice */
//  (ssizeobjargproc)Bar_ass_item,	/* sq_ass_item */
//  0,					/* sq_ass_slice */
//  0, //(objobjproc)svcontains,		/* sq_contains */
//};

//static PyMappingMethods Bar_as_mapping = {
//  (lenfunc)Bar_Size,			/* mp_length */
//  (binaryfunc)Bar_subscript,		/* mp_subscript */
//  (objobjargproc)Bar_ass_subscript	/* mp_ass_subscript */
//};

int Bar_InitType(PyObject *module) {
  int retval;
  if ((retval=util_add_type(module, &BarType))!=0) return retval;

  // These are used in setting the stat

  return 0;
}

PyDoc_STRVAR(index_doc,
"The index of the row or column this object refers to."
);

static PyMemberDef Bar_members[] = {
  {"index", T_INT, offsetof(BarObject, index), READONLY, index_doc},
  {NULL}
};

PyDoc_STRVAR(name_doc, "Row/column symbolic name, or None if unset." );

PyDoc_STRVAR(bounds_doc,
"The lower and upper bounds, where None signifies unboundedness."
);

PyDoc_STRVAR(valid_doc,
"Whether this row or column has a valid index in its LP."
);

PyDoc_STRVAR(matrix_doc,
"Non-zero constraint coefficients in this row/column vector as a list of\n"
"two-element (index, value) tuples."
);

PyDoc_STRVAR(nnz_doc,
"Number of non-zero constraint elements in this row/column."
);

PyDoc_STRVAR(isrow_doc, "Whether this is a row." );

PyDoc_STRVAR(iscol_doc, "Whether this is a column." );

PyDoc_STRVAR(scale_doc,
"The scale for the row or column. This is a factor which one may set to\n"
"improve conditioning in the problem. Most users will want to use the\n"
"LPX.scale() method rather than setting these directly. The resulting\n"
"constraint matrix is such that the entry at row i and column j is\n"
"(for the purpose of optimization) (ri)*(aij)*(sj) where ri and sj are the\n"
"row and column scaling factors, and aij is the entry of the constraint\n"
"matrix."
);

PyDoc_STRVAR(status_doc,
"Row/column basis status\n"
"\n"
"This is a two character string with the following possible values:\n"
"\n"
"bs\n"
"  This row/column is basic.\n"
"nl\n"
"  This row/column is non-basic.\n"
"nu\n"
"  This row/column is non-basic and set to the upper bound. On assignment,\n"
"  if this row/column is not double bounded, this is equivalent to 'nl'.\n"
"nf\n"
"  This row/column is non-basic and free. On assignment this is equivalent\n"
"  to 'nl'.\n"
"ns\n"
"  This row/column is non-basic and fixed. On assignment this is equivalent\n"
"  to 'nl'."
);

PyDoc_STRVAR(value_doc, "The value of this variable by the last solver.");

PyDoc_STRVAR(primal_doc,
"The primal value of this variable by the last solver."
);

PyDoc_STRVAR(dual_doc, "The dual value of this variable by the last solver." );

PyDoc_STRVAR(primal_s_doc,
"The primal value of this variable by the simplex solver."
);

PyDoc_STRVAR(dual_s_doc,
"The dual value of this variable by the simplex solver."
);

PyDoc_STRVAR(primal_i_doc,
"The primal value of this variable by the interior-point solver."
);

PyDoc_STRVAR(dual_i_doc,
"The dual value of this variable by the interior-point solver."
);

PyDoc_STRVAR(value_m_doc, "The value of this variable by the MIP solver." );

PyDoc_STRVAR(kind_doc,
"Either the type 'float' if this is a continuous variable, 'int' if this is\n"
"an integer variable, or 'bool' if this is a binary variable."
);

static PyGetSetDef Bar_getset[] = {
  {"name", (getter)Bar_getname, (setter)Bar_setname, name_doc, NULL},
  {"bounds", (getter)Bar_getbounds, (setter)Bar_setbounds, bounds_doc, NULL},
  {"valid", (getter)Bar_getvalid, (setter)NULL, valid_doc, NULL},
  {"matrix", (getter)Bar_getmatrix, (setter)Bar_setmatrix, matrix_doc, NULL},
  {"nnz", (getter)Bar_getnumnonzero, (setter)NULL, nnz_doc, NULL},
  {"isrow", (getter)Bar_getisrow, (setter)NULL, isrow_doc, NULL},
  {"iscol", (getter)Bar_getiscol, (setter)NULL, iscol_doc, NULL},
  {"scale", (getter)Bar_getscale, (setter)Bar_setscale, scale_doc, NULL},
  {"status", (getter)Bar_getstatus, (setter)Bar_setstatus, status_doc, NULL},
  {"value", (getter)Bar_getvarval, (setter)NULL, value_doc, NULL},
  {"primal", (getter)Bar_getvarval, (setter)NULL, primal_doc, NULL},
  {"dual", (getter)Bar_getvarval, (setter)NULL, dual_doc, (void*)0x1},
  {"primal_s", (getter)Bar_getspecvarval, (setter)NULL, primal_s_doc,
  (void*)(rowcol_primdual_funcptrs)},
  {"dual_s", (getter)Bar_getspecvarval, (setter)NULL, dual_s_doc,
   (void*)(rowcol_primdual_funcptrs+2)},
  {"primal_i", (getter)Bar_getspecvarval, (setter)NULL, primal_i_doc,
   (void*)(rowcol_primdual_funcptrs+4)},
  {"dual_i", (getter)Bar_getspecvarval, (setter)NULL, dual_i_doc,
   (void*)(rowcol_primdual_funcptrs+6)},
  {"value_m", (getter)Bar_getspecvarvalm, (setter)NULL, value_m_doc,
   (void*)(rowcol_primdual_funcptrs+8)},
  {"kind", (getter)Bar_getkind, (setter)Bar_setkind, kind_doc, NULL},
  {NULL}
};

static PyMethodDef Bar_methods[] = {
  /*{"add", (PyCFunction)Bar_add, METH_VARARGS, "add(n)\n\n"
   "Add n more rows (constraints) or columns (struct variables).\n"
   "Returns the index of the first added entry."},*/
  {NULL}
};

PyDoc_STRVAR(bar_doc,
"Bar objects are used to refer to a particular row or column of a linear\n"
"program. Rows and columns may be retrieved by indexing into the rows and\n"
"cols sequences of LPX instances."
);

PyTypeObject BarType = {
  PyVarObject_HEAD_INIT(NULL, 0)
  .tp_name           = "glpk.Bar",
  .tp_basicsize      = sizeof(BarObject),
  .tp_dealloc        = (destructor) Bar_dealloc,
  .tp_repr           = (reprfunc) Bar_Str,
  .tp_str            = (reprfunc)Bar_Str,
#ifdef USE_BAR_GC
  .tp_flags          = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
#else
  .tp_flags          = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
#endif
  .tp_doc            = bar_doc,
  .tp_traverse       = (traverseproc) Bar_traverse,
  .tp_clear          = (inquiry) Bar_clear,
  .tp_richcompare    = (richcmpfunc) Bar_richcompare,
  .tp_weaklistoffset = offsetof(BarObject, weakreflist),
  .tp_methods        = Bar_methods,
  .tp_members        = Bar_members,
  .tp_getset         = Bar_getset,
};
