this repo has no description
1/* Python Interface for MiniZinc constraint modelling
2 * Author:
3 * Tai Tran <tai.tran@student.adelaide.edu.au>
4 * Supervisor:
5 * Guido Tack <guido.tack@monash.edu>
6 */
7
8#include "Set.h"
9
10static void MznSet_dealloc(MznSet* self) {
11 delete self->ranges;
12 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
13}
14
15static PyObject* MznSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
16 MznSet* self = reinterpret_cast<MznSet*>(type->tp_alloc(type, 0));
17 self->ranges = new list<MznRange>;
18 self->tid = MOC_SET;
19 return reinterpret_cast<PyObject*>(self);
20}
21
22static PyObject* MznSet_iter(PyObject* self) {
23 MznSetIter* iter = reinterpret_cast<MznSetIter*>(MznSetIter_new(&MznSetIter_Type, NULL, NULL));
24 iter->listBegin = reinterpret_cast<MznSet*>(self)->ranges->begin();
25 iter->listEnd = reinterpret_cast<MznSet*>(self)->ranges->end();
26 iter->listIndex = iter->listBegin;
27 iter->currentValue = iter->listBegin->min;
28 return reinterpret_cast<PyObject*>(iter);
29}
30
31bool MznSet::contains(long long val) {
32 list<MznRange>::iterator it = ranges->begin();
33 list<MznRange>::iterator end = ranges->end();
34 assert(it != end);
35 for (; it++ != end;)
36 if (val < it->min)
37 return false;
38 else if (val <= it->max)
39 return true;
40 return false;
41}
42
43bool MznSet::continuous() {
44 if (ranges->empty())
45 throw length_error("Ranges cannot be empty");
46 else {
47 // returning ranges.size() == 1, with O(1) time complexity
48 list<MznRange>::iterator it = ranges->begin();
49 return ++it == ranges->end();
50 }
51}
52
53void MznSet::push(long long min, long long max) {
54 if (ranges->empty()) {
55 MznRange toAdd(min, max);
56 ranges->push_front(toAdd);
57 return;
58 }
59 list<MznRange>::iterator it, last;
60 for (it = ranges->begin(); it != ranges->end(); ++it) {
61 if (min <= it->max + 1) {
62 if (it->min < min) min = it->min;
63 if (max <= it->max) {
64 ranges->insert(it, MznRange(min, max));
65 return;
66 }
67 goto FOUNDMIN;
68 }
69 continue;
70 FOUNDMIN:
71 last = it;
72 while (++it != ranges->end()) {
73 if (max < it->min - 1) {
74 break;
75 } else {
76 ranges->erase(last);
77 last = it;
78 if (max <= it->max) {
79 break;
80 } else
81 ++it;
82 }
83 }
84 last->min = min;
85 last->max = max;
86 return;
87 }
88 ranges->push_back(MznRange(min, max));
89}
90
91void MznSet::push(long long v) {
92 list<MznRange>::iterator it, last;
93 last = ranges->begin();
94 if (ranges->empty()) {
95 MznRange toAdd(v, v);
96 ranges->push_front(toAdd);
97 return;
98 }
99 for (it = last; it != ranges->end(); ++it) {
100 if (v < it->min) {
101 if (v == it->min - 1) {
102 it->min = v;
103 if (last->max == v) {
104 last->max = it->max;
105 ranges->erase(it);
106 return;
107 }
108 } else {
109 //
110 if (last->max < v) {
111 MznRange toAdd(v, v);
112 ranges->insert(it, toAdd);
113 }
114 return;
115 }
116 } else if (v > it->max) {
117 if (v == it->max + 1) {
118 it->max = v;
119 }
120 last = it;
121 continue;
122 } else {
123 return;
124 }
125 }
126 if (last->max < v) {
127 MznRange toAdd(v, v);
128 ranges->insert(it, toAdd);
129 }
130 return;
131}
132
133long long MznSet::min() {
134 assert(ranges->begin() != ranges->end());
135 return ranges->front().min;
136}
137
138long long MznSet::max() {
139 assert(ranges->begin() != ranges->end());
140 return ranges->back().max;
141}
142
143static PyObject* MznSet_min(MznSet* self) { return c_to_py_number(self->min()); }
144
145static PyObject* MznSet_max(MznSet* self) { return c_to_py_number(self->max()); }
146
147static PyObject* MznSet_continuous(MznSet* self) { return PyBool_FromLong(self->continuous()); }
148
149static PyObject* MznSet_contains(MznSet* self, PyObject* args) {
150 PyObject* py_val;
151 if (!PyArg_ParseTuple(args, "O", &py_val)) {
152 PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.contains: Parsing error");
153 return NULL;
154 }
155 long long c_val = py_to_c_number(py_val);
156 if (PyErr_Occurred()) {
157 PyObject *ptype, *pmessage, *ptraceback;
158 PyErr_Fetch(&ptype, &pmessage, &ptraceback);
159 const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
160 string error = "MiniZinc: Set.contains: " + string(pStrErrorMessage);
161 PyErr_SetString(ptype, error.c_str());
162 return NULL;
163 }
164
165 return PyBool_FromLong(self->contains(c_val));
166}
167
168static PyObject* MznSet_push(MznSet* self, PyObject* isv) {
169 /*PyObject* isv;
170 if (!PyArg_ParseTuple(args,"O",&isv)) {
171 PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.push: Parsing error");
172 return NULL;
173 }*/
174 // if (isv == NULL)
175 // Py_RETURN_NONE;
176
177 Py_ssize_t size = PyTuple_Size(isv);
178
179 for (Py_ssize_t i = 0; i != size; ++i) {
180 PyObject* elem = PyTuple_GetItem(isv, i);
181
182 Mzn_PyContainer_Type elem_type = Mzn_PyContainer_Check(elem);
183 Py_ssize_t elem_size = Mzn_PyContainer_Size(elem, elem_type);
184
185 switch (elem_size) {
186 // elem_size == -1 means it is neither list nor tuple
187 case -1: {
188 int overflow;
189 long long c_val = py_to_c_number(elem, &overflow);
190 if (PyErr_Occurred()) {
191 if (overflow) {
192 MZN_PYERR_SET_STRING(PyExc_OverflowError,
193 "MiniZinc: Set.push: Overflow at tuple element at pos %li", i);
194 return NULL;
195 } else {
196 MZN_PYERR_SET_STRING(PyExc_TypeError,
197 "MiniZinc: Set.push: Type mismatched at tuple element pos %li: "
198 "expected an integer or list of integers",
199 i);
200 return NULL;
201 }
202 }
203 self->push(c_val);
204 break;
205 }
206 case 1: {
207 PyObject* py_val = Mzn_PyContainer_GetItem(elem, 0, elem_type);
208 long long c_val = py_to_c_number(py_val);
209 if (PyErr_Occurred()) {
210 PyObject *ptype, *pmessage, *ptraceback;
211 PyErr_Fetch(&ptype, &pmessage, &ptraceback);
212 const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
213 string error = "MiniZinc: Set.push: tuple item %li: " + string(pStrErrorMessage);
214 MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
215 return NULL;
216 }
217 self->push(c_val);
218 break;
219 }
220 case 2: {
221 PyObject* p_min = Mzn_PyContainer_GetItem(elem, 0, elem_type);
222 PyObject* p_max = Mzn_PyContainer_GetItem(elem, 1, elem_type);
223 long long c_min = py_to_c_number(p_min);
224 if (PyErr_Occurred()) {
225 PyObject *ptype, *pmessage, *ptraceback;
226 PyErr_Fetch(&ptype, &pmessage, &ptraceback);
227 const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
228 string error =
229 "MiniZinc: Set.push: tuple item %li, first argument: " + string(pStrErrorMessage);
230 MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
231 return NULL;
232 }
233
234 long long c_max = py_to_c_number(p_max);
235 if (PyErr_Occurred()) {
236 PyObject *ptype, *pmessage, *ptraceback;
237 PyErr_Fetch(&ptype, &pmessage, &ptraceback);
238 const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage);
239 string error =
240 "MiniZinc: Set.push: tuple item %li, second argument: " + string(pStrErrorMessage);
241 MZN_PYERR_SET_STRING(ptype, error.c_str(), i);
242 return NULL;
243 }
244
245 if (c_min < c_max)
246 self->push(c_min, c_max);
247 else if (c_min > c_max)
248 self->push(c_max, c_min);
249 else
250 self->push(c_min);
251 break;
252 }
253 default:
254 PyErr_SetString(PyExc_TypeError,
255 "MiniZinc: Set.push: The sublist size can only be 1 or 2");
256 return NULL;
257 }
258 }
259 Py_RETURN_NONE;
260}
261
262static int MznSet_init(MznSet* self, PyObject* args) {
263 if (MznSet_push(self, args) == NULL) return -1;
264 return 0;
265}
266
267static PyObject* MznSet_clear(MznSet* self) {
268 self->clear();
269 Py_RETURN_NONE;
270}
271
272static PyObject* MznSet_output(MznSet* self) {
273 stringstream s;
274 for (list<MznRange>::iterator it = self->ranges->begin(); it != self->ranges->end(); ++it) {
275 s << it->min << ".." << it->max << " ";
276 }
277 const std::string& tmp = s.str();
278 const char* cstr = tmp.c_str();
279 PyObject* result = PyUnicode_FromString(cstr);
280 return result;
281}
282
283static PyObject* MznSet_repr(PyObject* self) {
284 stringstream output;
285 list<MznRange>* r = (reinterpret_cast<MznSet*>(self))->ranges;
286 if (r->begin() == r->end())
287 output << "Empty Set";
288 else
289 for (list<MznRange>::iterator it = r->begin();;) {
290 if (it->min == it->max)
291 output << it->min;
292 else
293 output << it->min << ".." << it->max;
294 if (++it == r->end())
295 break;
296 else
297 output << ", ";
298 }
299 const std::string& tmp = output.str();
300 return PyUnicode_FromString(tmp.c_str());
301}
302
303static void MznSetIter_dealloc(MznSetIter* self) {
304 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
305}
306
307static PyObject* MznSetIter_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
308 return type->tp_alloc(type, 0);
309}
310
311static PyObject* MznSetIter_iternext(PyObject* self) {
312 MznSetIter* iter = reinterpret_cast<MznSetIter*>(self);
313 if (iter->listIndex == iter->listEnd) {
314 iter->listIndex = iter->listBegin;
315 iter->currentValue = iter->listBegin->min;
316 PyErr_SetNone(PyExc_StopIteration);
317 return NULL;
318 } else {
319 PyObject* result = c_to_py_number(iter->currentValue);
320 iter->currentValue++;
321 if (iter->currentValue > iter->listIndex->max) {
322 iter->listIndex++;
323 if (iter->listIndex != iter->listEnd) iter->currentValue = iter->listIndex->min;
324 }
325 return result;
326 }
327}