/* Python Interface for MiniZinc constraint modelling * Author: * Tai Tran * Supervisor: * Guido Tack */ #include "Set.h" static void MznSet_dealloc(MznSet* self) { delete self->ranges; Py_TYPE(self)->tp_free(reinterpret_cast(self)); } static PyObject* MznSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { MznSet* self = reinterpret_cast(type->tp_alloc(type, 0)); self->ranges = new list; self->tid = MOC_SET; return reinterpret_cast(self); } static PyObject* MznSet_iter(PyObject* self) { MznSetIter* iter = reinterpret_cast(MznSetIter_new(&MznSetIter_Type, NULL, NULL)); iter->listBegin = reinterpret_cast(self)->ranges->begin(); iter->listEnd = reinterpret_cast(self)->ranges->end(); iter->listIndex = iter->listBegin; iter->currentValue = iter->listBegin->min; return reinterpret_cast(iter); } bool MznSet::contains(long long val) { list::iterator it = ranges->begin(); list::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::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::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::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::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* r = (reinterpret_cast(self))->ranges; if (r->begin() == r->end()) output << "Empty Set"; else for (list::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(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(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; } }