327 lines
9.4 KiB
C++
327 lines
9.4 KiB
C++
/* Python Interface for MiniZinc constraint modelling
|
|
* Author:
|
|
* Tai Tran <tai.tran@student.adelaide.edu.au>
|
|
* Supervisor:
|
|
* Guido Tack <guido.tack@monash.edu>
|
|
*/
|
|
|
|
#include "Set.h"
|
|
|
|
static void MznSet_dealloc(MznSet* self) {
|
|
delete self->ranges;
|
|
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
|
|
}
|
|
|
|
static PyObject* MznSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
|
|
MznSet* self = reinterpret_cast<MznSet*>(type->tp_alloc(type, 0));
|
|
self->ranges = new list<MznRange>;
|
|
self->tid = MOC_SET;
|
|
return reinterpret_cast<PyObject*>(self);
|
|
}
|
|
|
|
static PyObject* MznSet_iter(PyObject* self) {
|
|
MznSetIter* iter = reinterpret_cast<MznSetIter*>(MznSetIter_new(&MznSetIter_Type, NULL, NULL));
|
|
iter->listBegin = reinterpret_cast<MznSet*>(self)->ranges->begin();
|
|
iter->listEnd = reinterpret_cast<MznSet*>(self)->ranges->end();
|
|
iter->listIndex = iter->listBegin;
|
|
iter->currentValue = iter->listBegin->min;
|
|
return reinterpret_cast<PyObject*>(iter);
|
|
}
|
|
|
|
bool MznSet::contains(long long val) {
|
|
list<MznRange>::iterator it = ranges->begin();
|
|
list<MznRange>::iterator end = ranges->end();
|
|
assert(it != end);
|
|
for (; it++ != end;)
|
|
if (val < it->min)
|
|
return false;
|
|
else if (val <= it->max)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool MznSet::continuous() {
|
|
if (ranges->empty())
|
|
throw length_error("Ranges cannot be empty");
|
|
else {
|
|
// returning ranges.size() == 1, with O(1) time complexity
|
|
list<MznRange>::iterator it = ranges->begin();
|
|
return ++it == ranges->end();
|
|
}
|
|
}
|
|
|
|
void MznSet::push(long long min, long long max) {
|
|
if (ranges->empty()) {
|
|
MznRange toAdd(min, max);
|
|
ranges->push_front(toAdd);
|
|
return;
|
|
}
|
|
list<MznRange>::iterator it, last;
|
|
for (it = ranges->begin(); it != ranges->end(); ++it) {
|
|
if (min <= it->max + 1) {
|
|
if (it->min < min) min = it->min;
|
|
if (max <= it->max) {
|
|
ranges->insert(it, MznRange(min, max));
|
|
return;
|
|
}
|
|
goto FOUNDMIN;
|
|
}
|
|
continue;
|
|
FOUNDMIN:
|
|
last = it;
|
|
while (++it != ranges->end()) {
|
|
if (max < it->min - 1) {
|
|
break;
|
|
} else {
|
|
ranges->erase(last);
|
|
last = it;
|
|
if (max <= it->max) {
|
|
break;
|
|
} else
|
|
++it;
|
|
}
|
|
}
|
|
last->min = min;
|
|
last->max = max;
|
|
return;
|
|
}
|
|
ranges->push_back(MznRange(min, max));
|
|
}
|
|
|
|
void MznSet::push(long long v) {
|
|
list<MznRange>::iterator it, last;
|
|
last = ranges->begin();
|
|
if (ranges->empty()) {
|
|
MznRange toAdd(v, v);
|
|
ranges->push_front(toAdd);
|
|
return;
|
|
}
|
|
for (it = last; it != ranges->end(); ++it) {
|
|
if (v < it->min) {
|
|
if (v == it->min - 1) {
|
|
it->min = v;
|
|
if (last->max == v) {
|
|
last->max = it->max;
|
|
ranges->erase(it);
|
|
return;
|
|
}
|
|
} else {
|
|
//
|
|
if (last->max < v) {
|
|
MznRange toAdd(v, v);
|
|
ranges->insert(it, toAdd);
|
|
}
|
|
return;
|
|
}
|
|
} else if (v > it->max) {
|
|
if (v == it->max + 1) {
|
|
it->max = v;
|
|
}
|
|
last = it;
|
|
continue;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (last->max < v) {
|
|
MznRange toAdd(v, v);
|
|
ranges->insert(it, toAdd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
long long MznSet::min() {
|
|
assert(ranges->begin() != ranges->end());
|
|
return ranges->front().min;
|
|
}
|
|
|
|
long long MznSet::max() {
|
|
assert(ranges->begin() != ranges->end());
|
|
return ranges->back().max;
|
|
}
|
|
|
|
static PyObject* MznSet_min(MznSet* self) { return c_to_py_number(self->min()); }
|
|
|
|
static PyObject* MznSet_max(MznSet* self) { return c_to_py_number(self->max()); }
|
|
|
|
static PyObject* MznSet_continuous(MznSet* self) { return PyBool_FromLong(self->continuous()); }
|
|
|
|
static PyObject* MznSet_contains(MznSet* self, PyObject* args) {
|
|
PyObject* py_val;
|
|
if (!PyArg_ParseTuple(args, "O", &py_val)) {
|
|
PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.contains: Parsing error");
|
|
return NULL;
|
|
}
|
|
long long c_val = py_to_c_number(py_val);
|
|
if (PyErr_Occurred()) {
|
|
PyObject *ptype, *pmessage, *ptraceback;
|
|
PyErr_Fetch(&ptype, &pmessage, &ptraceback);
|
|
const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
|
|
string error = "MiniZinc: Set.contains: " + string(pStrErrorMessage);
|
|
PyErr_SetString(ptype, error.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
return PyBool_FromLong(self->contains(c_val));
|
|
}
|
|
|
|
static PyObject* MznSet_push(MznSet* self, PyObject* isv) {
|
|
/*PyObject* isv;
|
|
if (!PyArg_ParseTuple(args,"O",&isv)) {
|
|
PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.push: Parsing error");
|
|
return NULL;
|
|
}*/
|
|
// if (isv == NULL)
|
|
// Py_RETURN_NONE;
|
|
|
|
Py_ssize_t size = PyTuple_Size(isv);
|
|
|
|
for (Py_ssize_t i = 0; i != size; ++i) {
|
|
PyObject* elem = PyTuple_GetItem(isv, i);
|
|
|
|
Mzn_PyContainer_Type elem_type = Mzn_PyContainer_Check(elem);
|
|
Py_ssize_t elem_size = Mzn_PyContainer_Size(elem, elem_type);
|
|
|
|
switch (elem_size) {
|
|
// elem_size == -1 means it is neither list nor tuple
|
|
case -1: {
|
|
int overflow;
|
|
long long c_val = py_to_c_number(elem, &overflow);
|
|
if (PyErr_Occurred()) {
|
|
if (overflow) {
|
|
MZN_PYERR_SET_STRING(PyExc_OverflowError,
|
|
"MiniZinc: Set.push: Overflow at tuple element at pos %li", i);
|
|
return NULL;
|
|
} else {
|
|
MZN_PYERR_SET_STRING(PyExc_TypeError,
|
|
"MiniZinc: Set.push: Type mismatched at tuple element pos %li: "
|
|
"expected an integer or list of integers",
|
|
i);
|
|
return NULL;
|
|
}
|
|
}
|
|
self->push(c_val);
|
|
break;
|
|
}
|
|
case 1: {
|
|
PyObject* py_val = Mzn_PyContainer_GetItem(elem, 0, elem_type);
|
|
long long c_val = py_to_c_number(py_val);
|
|
if (PyErr_Occurred()) {
|
|
PyObject *ptype, *pmessage, *ptraceback;
|
|
PyErr_Fetch(&ptype, &pmessage, &ptraceback);
|
|
const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
|
|
string error = "MiniZinc: Set.push: tuple item %li: " + string(pStrErrorMessage);
|
|
MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
|
|
return NULL;
|
|
}
|
|
self->push(c_val);
|
|
break;
|
|
}
|
|
case 2: {
|
|
PyObject* p_min = Mzn_PyContainer_GetItem(elem, 0, elem_type);
|
|
PyObject* p_max = Mzn_PyContainer_GetItem(elem, 1, elem_type);
|
|
long long c_min = py_to_c_number(p_min);
|
|
if (PyErr_Occurred()) {
|
|
PyObject *ptype, *pmessage, *ptraceback;
|
|
PyErr_Fetch(&ptype, &pmessage, &ptraceback);
|
|
const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
|
|
string error =
|
|
"MiniZinc: Set.push: tuple item %li, first argument: " + string(pStrErrorMessage);
|
|
MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
|
|
return NULL;
|
|
}
|
|
|
|
long long c_max = py_to_c_number(p_max);
|
|
if (PyErr_Occurred()) {
|
|
PyObject *ptype, *pmessage, *ptraceback;
|
|
PyErr_Fetch(&ptype, &pmessage, &ptraceback);
|
|
const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
|
|
string error =
|
|
"MiniZinc: Set.push: tuple item %li, second argument: " + string(pStrErrorMessage);
|
|
MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
|
|
return NULL;
|
|
}
|
|
|
|
if (c_min < c_max)
|
|
self->push(c_min, c_max);
|
|
else if (c_min > c_max)
|
|
self->push(c_max, c_min);
|
|
else
|
|
self->push(c_min);
|
|
break;
|
|
}
|
|
default:
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"MiniZinc: Set.push: The sublist size can only be 1 or 2");
|
|
return NULL;
|
|
}
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static int MznSet_init(MznSet* self, PyObject* args) {
|
|
if (MznSet_push(self, args) == NULL) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static PyObject* MznSet_clear(MznSet* self) {
|
|
self->clear();
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject* MznSet_output(MznSet* self) {
|
|
stringstream s;
|
|
for (list<MznRange>::iterator it = self->ranges->begin(); it != self->ranges->end(); ++it) {
|
|
s << it->min << ".." << it->max << " ";
|
|
}
|
|
const std::string& tmp = s.str();
|
|
const char* cstr = tmp.c_str();
|
|
PyObject* result = PyUnicode_FromString(cstr);
|
|
return result;
|
|
}
|
|
|
|
static PyObject* MznSet_repr(PyObject* self) {
|
|
stringstream output;
|
|
list<MznRange>* r = (reinterpret_cast<MznSet*>(self))->ranges;
|
|
if (r->begin() == r->end())
|
|
output << "Empty Set";
|
|
else
|
|
for (list<MznRange>::iterator it = r->begin();;) {
|
|
if (it->min == it->max)
|
|
output << it->min;
|
|
else
|
|
output << it->min << ".." << it->max;
|
|
if (++it == r->end())
|
|
break;
|
|
else
|
|
output << ", ";
|
|
}
|
|
const std::string& tmp = output.str();
|
|
return PyUnicode_FromString(tmp.c_str());
|
|
}
|
|
|
|
static void MznSetIter_dealloc(MznSetIter* self) {
|
|
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
|
|
}
|
|
|
|
static PyObject* MznSetIter_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
|
|
return type->tp_alloc(type, 0);
|
|
}
|
|
|
|
static PyObject* MznSetIter_iternext(PyObject* self) {
|
|
MznSetIter* iter = reinterpret_cast<MznSetIter*>(self);
|
|
if (iter->listIndex == iter->listEnd) {
|
|
iter->listIndex = iter->listBegin;
|
|
iter->currentValue = iter->listBegin->min;
|
|
PyErr_SetNone(PyExc_StopIteration);
|
|
return NULL;
|
|
} else {
|
|
PyObject* result = c_to_py_number(iter->currentValue);
|
|
iter->currentValue++;
|
|
if (iter->currentValue > iter->listIndex->max) {
|
|
iter->listIndex++;
|
|
if (iter->listIndex != iter->listEnd) iter->currentValue = iter->listIndex->min;
|
|
}
|
|
return result;
|
|
}
|
|
} |