blob: ba083ada8aa107e39d8fba4614282f014db84bfe [file] [log] [blame]
Damiend99b0522013-12-21 18:17:45 +00001#include <stdlib.h>
2#include <stdint.h>
John R. Lenton3b0bd872014-01-12 15:56:25 +00003#include <string.h>
Damiend99b0522013-12-21 18:17:45 +00004#include <assert.h>
5
6#include "nlr.h"
7#include "misc.h"
8#include "mpconfig.h"
Damien George71c51812014-01-04 20:21:15 +00009#include "mpqstr.h"
Damiend99b0522013-12-21 18:17:45 +000010#include "obj.h"
Damien George71c51812014-01-04 20:21:15 +000011#include "runtime.h"
John R. Lentonc1bef212014-01-11 12:39:33 +000012#include "runtime0.h"
Damiend99b0522013-12-21 18:17:45 +000013#include "map.h"
14
15typedef struct _mp_obj_set_t {
16 mp_obj_base_t base;
17 mp_set_t set;
18} mp_obj_set_t;
19
John R. Lenton0ce03b42014-01-12 15:17:42 +000020typedef struct _mp_obj_set_it_t {
21 mp_obj_base_t base;
22 mp_obj_set_t *set;
23 machine_uint_t cur;
24} mp_obj_set_it_t;
25
26static mp_obj_t set_it_iternext(mp_obj_t self_in);
27
Damiend99b0522013-12-21 18:17:45 +000028void set_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
29 mp_obj_set_t *self = self_in;
John R. Lenton7244a142014-01-12 23:37:45 +000030 if (self->set.used == 0) {
31 print(env, "set()");
32 return;
33 }
Damiend99b0522013-12-21 18:17:45 +000034 bool first = true;
35 print(env, "{");
36 for (int i = 0; i < self->set.alloc; i++) {
37 if (self->set.table[i] != MP_OBJ_NULL) {
38 if (!first) {
39 print(env, ", ");
40 }
41 first = false;
42 mp_obj_print_helper(print, env, self->set.table[i]);
43 }
44 }
45 print(env, "}");
46}
47
John R. Lentonc1bef212014-01-11 12:39:33 +000048
Damien George71c51812014-01-04 20:21:15 +000049static mp_obj_t set_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) {
50 switch (n_args) {
51 case 0:
52 // return a new, empty set
53 return mp_obj_new_set(0, NULL);
54
55 case 1:
56 {
57 // 1 argument, an iterable from which we make a new set
58 mp_obj_t set = mp_obj_new_set(0, NULL);
59 mp_obj_t iterable = rt_getiter(args[0]);
60 mp_obj_t item;
61 while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
62 mp_obj_set_store(set, item);
63 }
64 return set;
65 }
66
67 default:
68 nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "set takes at most 1 argument, %d given", (void*)(machine_int_t)n_args));
69 }
70}
71
John R. Lenton0ce03b42014-01-12 15:17:42 +000072const mp_obj_type_t set_it_type = {
73 { &mp_const_type },
74 "set_iterator",
75 .iternext = set_it_iternext,
76};
77
78static mp_obj_t set_it_iternext(mp_obj_t self_in) {
79 assert(MP_OBJ_IS_TYPE(self_in, &set_it_type));
80 mp_obj_set_it_t *self = self_in;
81 machine_uint_t max = self->set->set.alloc;
82 mp_obj_t *table = self->set->set.table;
83
84 for (machine_uint_t i = self->cur; i < max; i++) {
85 if (table[i] != NULL) {
86 self->cur = i + 1;
87 return table[i];
88 }
89 }
90
91 return mp_const_stop_iteration;
92}
93
94static mp_obj_t set_getiter(mp_obj_t set_in) {
95 mp_obj_set_it_t *o = m_new_obj(mp_obj_set_it_t);
96 o->base.type = &set_it_type;
97 o->set = (mp_obj_set_t *)set_in;
98 o->cur = 0;
99 return o;
100}
101
John R. Lenton19b14d32014-01-12 15:29:11 +0000102
103/******************************************************************************/
104/* set methods */
105
106static mp_obj_t set_add(mp_obj_t self_in, mp_obj_t item) {
107 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
108 mp_obj_set_t *self = self_in;
John R. Lenton2a241722014-01-12 16:39:39 +0000109 mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lenton19b14d32014-01-12 15:29:11 +0000110 return mp_const_none;
111}
112static MP_DEFINE_CONST_FUN_OBJ_2(set_add_obj, set_add);
113
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000114static mp_obj_t set_clear(mp_obj_t self_in) {
115 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
116 mp_obj_set_t *self = self_in;
117
118 mp_set_clear(&self->set);
119
120 return mp_const_none;
121}
122static MP_DEFINE_CONST_FUN_OBJ_1(set_clear_obj, set_clear);
123
John R. Lenton3b0bd872014-01-12 15:56:25 +0000124static mp_obj_t set_copy(mp_obj_t self_in) {
125 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
126 mp_obj_set_t *self = self_in;
127
128 mp_obj_set_t *other = m_new_obj(mp_obj_set_t);
129 other->base.type = &set_type;
John R. Lenton7244a142014-01-12 23:37:45 +0000130 mp_set_init(&other->set, self->set.alloc - 1);
John R. Lenton3b0bd872014-01-12 15:56:25 +0000131 other->set.used = self->set.used;
132 memcpy(other->set.table, self->set.table, self->set.alloc * sizeof(mp_obj_t));
133
134 return other;
135}
136static MP_DEFINE_CONST_FUN_OBJ_1(set_copy_obj, set_copy);
137
John R. Lenton2a241722014-01-12 16:39:39 +0000138static mp_obj_t set_discard(mp_obj_t self_in, mp_obj_t item) {
139 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
140 mp_obj_set_t *self = self_in;
141 mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_REMOVE_IF_FOUND);
142 return mp_const_none;
143}
144static MP_DEFINE_CONST_FUN_OBJ_2(set_discard_obj, set_discard);
John R. Lenton19b14d32014-01-12 15:29:11 +0000145
John R. Lenton032129f2014-01-12 17:07:17 +0000146static mp_obj_t set_diff_int(int n_args, const mp_obj_t *args, bool update) {
147 assert(n_args > 0);
148 assert(MP_OBJ_IS_TYPE(args[0], &set_type));
149 mp_obj_set_t *self;
150 if (update) {
151 self = args[0];
152 } else {
153 self = set_copy(args[0]);
154 }
155
156
157 for (int i = 1; i < n_args; i++) {
158 mp_obj_t other = args[i];
159 if (self == other) {
160 set_clear(self);
161 } else {
162 mp_obj_t iter = rt_getiter(other);
163 mp_obj_t next;
164 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
165 set_discard(self, next);
166 }
167 }
168 }
169
170 return self;
171}
172
173static mp_obj_t set_diff(int n_args, const mp_obj_t *args) {
174 return set_diff_int(n_args, args, false);
175}
176static MP_DEFINE_CONST_FUN_OBJ_VAR(set_diff_obj, 1, set_diff);
177
178static mp_obj_t set_diff_update(int n_args, const mp_obj_t *args) {
179 set_diff_int(n_args, args, true);
180 return mp_const_none;
181}
182static MP_DEFINE_CONST_FUN_OBJ_VAR(set_diff_update_obj, 1, set_diff_update);
183
John R. Lentonf1ae6b42014-01-12 17:54:03 +0000184static mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update) {
185 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
186 if (self_in == other) {
187 return update ? mp_const_none : set_copy(self_in);
188 }
189
190 mp_obj_set_t *self = self_in;
191 mp_obj_set_t *out = mp_obj_new_set(0, NULL);
192
193 mp_obj_t iter = rt_getiter(other);
194 mp_obj_t next;
195 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
196 if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
197 set_add(out, next);
198 }
199 }
200
201 if (update) {
202 m_del(mp_obj_t, self->set.table, self->set.alloc);
203 self->set.alloc = out->set.alloc;
204 self->set.used = out->set.used;
205 self->set.table = out->set.table;
206 }
207
208 return update ? mp_const_none : out;
209}
210
211static mp_obj_t set_intersect(mp_obj_t self_in, mp_obj_t other) {
212 return set_intersect_int(self_in, other, false);
213}
214static MP_DEFINE_CONST_FUN_OBJ_2(set_intersect_obj, set_intersect);
215
216static mp_obj_t set_intersect_update(mp_obj_t self_in, mp_obj_t other) {
217 return set_intersect_int(self_in, other, true);
218}
219static MP_DEFINE_CONST_FUN_OBJ_2(set_intersect_update_obj, set_intersect_update);
220
John R. Lenton4a080672014-01-12 18:03:21 +0000221static mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) {
222 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
223 mp_obj_set_t *self = self_in;
224
225 mp_obj_t iter = rt_getiter(other);
226 mp_obj_t next;
227 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
228 if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
229 return mp_const_false;
230 }
231 }
232 return mp_const_true;
233}
234static MP_DEFINE_CONST_FUN_OBJ_2(set_isdisjoint_obj, set_isdisjoint);
235
John R. Lentonbe790f92014-01-12 23:09:10 +0000236static mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool proper) {
John R. Lentonae00d332014-01-12 18:23:36 +0000237 mp_obj_set_t *self;
238 bool cleanup_self = false;
239 if (MP_OBJ_IS_TYPE(self_in, &set_type)) {
240 self = self_in;
241 } else {
242 self = set_make_new(NULL, 1, &self_in);
243 cleanup_self = true;
244 }
245
246 mp_obj_set_t *other;
247 bool cleanup_other = false;
248 if (MP_OBJ_IS_TYPE(other_in, &set_type)) {
249 other = other_in;
250 } else {
251 other = set_make_new(NULL, 1, &other_in);
252 cleanup_other = true;
253 }
John R. Lentonbe790f92014-01-12 23:09:10 +0000254 bool out = true;
255 if (proper && self->set.used == other->set.used) {
256 out = false;
257 } else {
258 mp_obj_t iter = set_getiter(self);
259 mp_obj_t next;
260 while ((next = set_it_iternext(iter)) != mp_const_stop_iteration) {
261 if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) {
262 out = false;
263 break;
264 }
John R. Lentonae00d332014-01-12 18:23:36 +0000265 }
266 }
267 if (cleanup_self) {
268 set_clear(self);
269 }
270 if (cleanup_other) {
271 set_clear(other);
272 }
John R. Lentonbe790f92014-01-12 23:09:10 +0000273 return MP_BOOL(out);
274}
275static mp_obj_t set_issubset(mp_obj_t self_in, mp_obj_t other_in) {
276 return set_issubset_internal(self_in, other_in, false);
John R. Lentonae00d332014-01-12 18:23:36 +0000277}
278static MP_DEFINE_CONST_FUN_OBJ_2(set_issubset_obj, set_issubset);
279
John R. Lentonbe790f92014-01-12 23:09:10 +0000280static mp_obj_t set_issubset_proper(mp_obj_t self_in, mp_obj_t other_in) {
281 return set_issubset_internal(self_in, other_in, true);
282}
283
John R. Lentonae00d332014-01-12 18:23:36 +0000284static mp_obj_t set_issuperset(mp_obj_t self_in, mp_obj_t other_in) {
John R. Lentonbe790f92014-01-12 23:09:10 +0000285 return set_issubset_internal(other_in, self_in, false);
John R. Lentonae00d332014-01-12 18:23:36 +0000286}
287static MP_DEFINE_CONST_FUN_OBJ_2(set_issuperset_obj, set_issuperset);
288
John R. Lentonbe790f92014-01-12 23:09:10 +0000289static mp_obj_t set_issuperset_proper(mp_obj_t self_in, mp_obj_t other_in) {
290 return set_issubset_internal(other_in, self_in, true);
291}
292
293static mp_obj_t set_equal(mp_obj_t self_in, mp_obj_t other_in) {
294 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
295 mp_obj_set_t *self = self_in;
296 if (!MP_OBJ_IS_TYPE(other_in, &set_type)) {
297 return mp_const_false;
298 }
299 mp_obj_set_t *other = other_in;
300 if (self->set.used != other->set.used) {
301 return mp_const_false;
302 }
303 return set_issubset(self_in, other_in);
304}
305
John R. Lentonae00d332014-01-12 18:23:36 +0000306static mp_obj_t set_pop(mp_obj_t self_in) {
307 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
308 mp_obj_set_t *self = self_in;
309
310 if (self->set.used == 0) {
311 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "pop from an empty set"));
312 }
313 mp_obj_t obj = mp_set_lookup(&self->set, NULL,
314 MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_FIRST);
315 return obj;
316}
317static MP_DEFINE_CONST_FUN_OBJ_1(set_pop_obj, set_pop);
318
319static mp_obj_t set_remove(mp_obj_t self_in, mp_obj_t item) {
320 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
321 mp_obj_set_t *self = self_in;
322 if (mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_REMOVE_IF_FOUND) == MP_OBJ_NULL) {
323 nlr_jump(mp_obj_new_exception(MP_QSTR_KeyError));
324 }
325 return mp_const_none;
326}
327static MP_DEFINE_CONST_FUN_OBJ_2(set_remove_obj, set_remove);
John R. Lenton032129f2014-01-12 17:07:17 +0000328
John R. Lenton0de386b2014-01-12 19:39:48 +0000329static mp_obj_t set_symmetric_difference_update(mp_obj_t self_in, mp_obj_t other_in) {
330 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
331 mp_obj_set_t *self = self_in;
332 mp_obj_t iter = rt_getiter(other_in);
333 mp_obj_t next;
334 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
335 mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
336 }
337 return mp_const_none;
338}
339static MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_update_obj, set_symmetric_difference_update);
340
341static mp_obj_t set_symmetric_difference(mp_obj_t self_in, mp_obj_t other_in) {
342 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
343 self_in = set_copy(self_in);
344 set_symmetric_difference_update(self_in, other_in);
345 return self_in;
346}
347static MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_obj, set_symmetric_difference);
348
349static void set_update_int(mp_obj_set_t *self, mp_obj_t other_in) {
350 mp_obj_t iter = rt_getiter(other_in);
351 mp_obj_t next;
352 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
353 mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
354 }
355}
356
357static mp_obj_t set_update(int n_args, const mp_obj_t *args) {
358 assert(n_args > 0);
359 assert(MP_OBJ_IS_TYPE(args[0], &set_type));
360
361 for (int i = 1; i < n_args; i++) {
362 set_update_int(args[0], args[i]);
363 }
364
365 return mp_const_none;
366}
367static MP_DEFINE_CONST_FUN_OBJ_VAR(set_update_obj, 1, set_update);
368
369static mp_obj_t set_union(mp_obj_t self_in, mp_obj_t other_in) {
370 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
371 mp_obj_set_t *self = set_copy(self_in);
372 set_update_int(self, other_in);
373 return self;
374}
375static MP_DEFINE_CONST_FUN_OBJ_2(set_union_obj, set_union);
376
377
John R. Lentonbe790f92014-01-12 23:09:10 +0000378static mp_obj_t set_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
379 mp_obj_t args[] = {lhs, rhs};
380 switch (op) {
381 case RT_BINARY_OP_OR:
382 return set_union(lhs, rhs);
383 case RT_BINARY_OP_XOR:
384 return set_symmetric_difference(lhs, rhs);
385 case RT_BINARY_OP_AND:
386 return set_intersect(lhs, rhs);
387 case RT_BINARY_OP_SUBTRACT:
388 return set_diff(2, args);
389 case RT_BINARY_OP_INPLACE_OR:
390 return set_union(lhs, rhs);
391 case RT_BINARY_OP_INPLACE_XOR:
392 return set_symmetric_difference(lhs, rhs);
393 case RT_BINARY_OP_INPLACE_AND:
394 return set_intersect(lhs, rhs);
395 case RT_BINARY_OP_INPLACE_SUBTRACT:
396 return set_diff(2, args);
397 case RT_COMPARE_OP_LESS:
398 return set_issubset_proper(lhs, rhs);
399 case RT_COMPARE_OP_MORE:
400 return set_issuperset_proper(lhs, rhs);
401 case RT_COMPARE_OP_EQUAL:
402 return set_equal(lhs, rhs);
403 case RT_COMPARE_OP_LESS_EQUAL:
404 return set_issubset(lhs, rhs);
405 case RT_COMPARE_OP_MORE_EQUAL:
406 return set_issuperset(lhs, rhs);
407 case RT_COMPARE_OP_NOT_EQUAL:
408 return MP_BOOL(set_equal(lhs, rhs) == mp_const_false);
John R. Lenton189c8e12014-01-13 00:52:06 +0000409 case RT_COMPARE_OP_IN:
410 case RT_COMPARE_OP_NOT_IN:
411 {
412 mp_obj_set_t *o = lhs;
413 mp_obj_t elem = mp_set_lookup(&o->set, rhs, MP_MAP_LOOKUP);
414 return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (elem == NULL));
415 }
John R. Lentonbe790f92014-01-12 23:09:10 +0000416 default:
417 // op not supported
418 return NULL;
419 }
420}
John R. Lenton0de386b2014-01-12 19:39:48 +0000421
John R. Lenton19b14d32014-01-12 15:29:11 +0000422/******************************************************************************/
423/* set constructors & public C API */
424
425
426static const mp_method_t set_type_methods[] = {
427 { "add", &set_add_obj },
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000428 { "clear", &set_clear_obj },
John R. Lenton3b0bd872014-01-12 15:56:25 +0000429 { "copy", &set_copy_obj },
John R. Lenton2a241722014-01-12 16:39:39 +0000430 { "discard", &set_discard_obj },
John R. Lenton032129f2014-01-12 17:07:17 +0000431 { "difference", &set_diff_obj },
432 { "difference_update", &set_diff_update_obj },
John R. Lentonf1ae6b42014-01-12 17:54:03 +0000433 { "intersection", &set_intersect_obj },
434 { "intersection_update", &set_intersect_update_obj },
John R. Lenton4a080672014-01-12 18:03:21 +0000435 { "isdisjoint", &set_isdisjoint_obj },
John R. Lentonae00d332014-01-12 18:23:36 +0000436 { "issubset", &set_issubset_obj },
437 { "issuperset", &set_issuperset_obj },
438 { "pop", &set_pop_obj },
439 { "remove", &set_remove_obj },
John R. Lenton0de386b2014-01-12 19:39:48 +0000440 { "symmetric_difference", &set_symmetric_difference_obj },
441 { "symmetric_difference_update", &set_symmetric_difference_update_obj },
442 { "union", &set_union_obj },
443 { "update", &set_update_obj },
John R. Lenton19b14d32014-01-12 15:29:11 +0000444 { NULL, NULL }, // end-of-list sentinel
445};
446
Damien George71c51812014-01-04 20:21:15 +0000447const mp_obj_type_t set_type = {
Damiend99b0522013-12-21 18:17:45 +0000448 { &mp_const_type },
449 "set",
Damien George97209d32014-01-07 15:58:30 +0000450 .print = set_print,
451 .make_new = set_make_new,
John R. Lentonbe790f92014-01-12 23:09:10 +0000452 .binary_op = set_binary_op,
John R. Lenton0ce03b42014-01-12 15:17:42 +0000453 .getiter = set_getiter,
John R. Lenton19b14d32014-01-12 15:29:11 +0000454 .methods = set_type_methods,
Damiend99b0522013-12-21 18:17:45 +0000455};
456
457mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items) {
458 mp_obj_set_t *o = m_new_obj(mp_obj_set_t);
459 o->base.type = &set_type;
460 mp_set_init(&o->set, n_args);
461 for (int i = 0; i < n_args; i++) {
John R. Lenton2a241722014-01-12 16:39:39 +0000462 mp_set_lookup(&o->set, items[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
Damiend99b0522013-12-21 18:17:45 +0000463 }
464 return o;
465}
466
467void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) {
468 assert(MP_OBJ_IS_TYPE(self_in, &set_type));
469 mp_obj_set_t *self = self_in;
John R. Lenton2a241722014-01-12 16:39:39 +0000470 mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
Damiend99b0522013-12-21 18:17:45 +0000471}