1 /*
2 
3     MPDM - Minimum Profit Data Manager
4     mpdm_x.c - Extended functions
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 #include "config.h"
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <locale.h>
19 #include <wchar.h>
20 
21 #include "mpdm.h"
22 
23 /** code **/
24 
mpdm_new_x(mpdm_type_t type,const void * f,mpdm_t a)25 mpdm_t mpdm_new_x(mpdm_type_t type, const void *f, mpdm_t a)
26 {
27     mpdm_t r = NULL;
28 
29     switch (type) {
30     case MPDM_TYPE_FUNCTION:
31         r = mpdm_new(type, f, 0);
32         break;
33 
34     case MPDM_TYPE_PROGRAM:
35         r = mpdm_new(type, NULL, 0);
36 
37         mpdm_push(r, MPDM_X(f));
38         mpdm_push(r, a);
39 
40         break;
41 
42     default:
43         r = NULL;
44     }
45 
46     return r;
47 }
48 
49 
50 /**
51  * mpdm_is_true - Returns 1 if a value is true.
52  * @v: the value
53  *
54  * Returns 1 if @v is true. False values are: NULL, integers
55  * with value 0, reals with value 0.0, empty strings and the
56  * special string "0".
57  * The reference count is touched.
58  */
mpdm_is_true(mpdm_t v)59 int mpdm_is_true(mpdm_t v)
60 {
61     int r;
62 
63     mpdm_ref(v);
64     r = mpdm_type_vc(v)->is_true(v);
65     mpdm_unref(v);
66 
67     return r;
68 }
69 
70 
71 /**
72  * mpdm_bool - Returns a boolean value.
73  * @b: true or false
74  *
75  * Returns the stored values for TRUE or FALSE.
76  */
mpdm_bool(int b)77 mpdm_t mpdm_bool(int b)
78 {
79     return mpdm_get_wcs(mpdm_root(), b ? L"TRUE" : L"FALSE");
80 }
81 
82 
mpdm_count(mpdm_t v)83 int mpdm_count(mpdm_t v)
84 {
85     return mpdm_type_vc(v)->count(v);
86 }
87 
88 
89 /**
90  * mpdm_get_i - Gets an element by integer subscript.
91  * @s: the set
92  * @index: the subscript of the element
93  *
94  * Returns the element at @index of the set @s.
95  */
mpdm_get_i(const mpdm_t s,int index)96 mpdm_t mpdm_get_i(const mpdm_t s, int index)
97 {
98     return mpdm_type_vc(s)->get_i(s, index);
99 }
100 
101 
102 /**
103  * mpdm_get - Gets an element by index.
104  * @s: the set
105  * @index: the index
106  *
107  * Returns the element at @index of the set @s.
108  */
mpdm_get(const mpdm_t s,mpdm_t index)109 mpdm_t mpdm_get(const mpdm_t s, mpdm_t index)
110 {
111     return mpdm_type_vc(s)->get(s, index);
112 }
113 
114 
115 /**
116  * mpdm_del_i - Deletes an element by integer subscript.
117  * @s: the set
118  * @index: subscript of the element to be deleted
119  *
120  * Deletes the element at @index of the @s set.
121  */
mpdm_del_i(const mpdm_t s,int index)122 mpdm_t mpdm_del_i(const mpdm_t s, int index)
123 {
124     return mpdm_type_vc(s)->del_i(s, index);
125 }
126 
127 
mpdm_del(const mpdm_t s,mpdm_t index)128 mpdm_t mpdm_del(const mpdm_t s, mpdm_t index)
129 {
130     return mpdm_type_vc(s)->del(s, index);
131 }
132 
133 
mpdm_set_i(mpdm_t s,mpdm_t e,int index)134 mpdm_t mpdm_set_i(mpdm_t s, mpdm_t e, int index)
135 {
136     return mpdm_type_vc(s)->set_i(s, e, index);
137 }
138 
139 
mpdm_set(mpdm_t s,mpdm_t e,mpdm_t index)140 mpdm_t mpdm_set(mpdm_t s, mpdm_t e, mpdm_t index)
141 {
142     return mpdm_type_vc(s)->set(s, e, index);
143 }
144 
145 
146 /**
147  * mpdm_exec - Executes an executable value.
148  * @c: the code value
149  * @args: the arguments
150  * @ctxt: the context
151  *
152  * Executes an executable value. If @c is a scalar value, its data
153  * should be a pointer to a directly executable C function with a
154  * prototype of mpdm_t func(mpdm_t args, mpdm_t ctxt); if it's a multiple
155  * one, the first value's data should be a pointer to a directly executable
156  * C function with a prototype of
157  * mpdm_t func(mpdm_t b, mpdm_t args, mpdm_t ctxt) and
158  * the second value will be passed as the @b argument. This value is used
159  * to store bytecode or so when implementing virtual machines or compilers.
160  * The @ctxt is meant to be used as a special context to implement local
161  * symbol tables and such. Its meaning is free and can be NULL.
162  * If @c is a file descriptor, a line is read from it if the call has no
163  * arguments or otherwise all of them are written into it.
164  *
165  * Returns the return value of the code. If @c is NULL or not executable,
166  * returns NULL.
167  * [Value Management]
168  */
mpdm_exec(mpdm_t c,mpdm_t args,mpdm_t ctxt)169 mpdm_t mpdm_exec(mpdm_t c, mpdm_t args, mpdm_t ctxt)
170 {
171     mpdm_t r = NULL;
172 
173     mpdm_ref(c);
174     mpdm_ref(args);
175     mpdm_ref(ctxt);
176 
177     r = mpdm_type_vc(c)->exec(c, args, ctxt);
178 
179     mpdm_ref(r);
180 
181     mpdm_unref(ctxt);
182     mpdm_unref(args);
183     mpdm_unref(c);
184 
185     return mpdm_unrefnd(r);
186 }
187 
188 
mpdm_exec_1(mpdm_t c,mpdm_t a1,mpdm_t ctxt)189 mpdm_t mpdm_exec_1(mpdm_t c, mpdm_t a1, mpdm_t ctxt)
190 {
191     mpdm_t a = MPDM_A(1);
192 
193     mpdm_set_i(a, a1, 0);
194 
195     return mpdm_exec(c, a, ctxt);
196 }
197 
198 
mpdm_exec_2(mpdm_t c,mpdm_t a1,mpdm_t a2,mpdm_t ctxt)199 mpdm_t mpdm_exec_2(mpdm_t c, mpdm_t a1, mpdm_t a2, mpdm_t ctxt)
200 {
201     mpdm_t a = MPDM_A(2);
202 
203     mpdm_set_i(a, a1, 0);
204     mpdm_set_i(a, a2, 1);
205 
206     return mpdm_exec(c, a, ctxt);
207 }
208 
209 
mpdm_exec_3(mpdm_t c,mpdm_t a1,mpdm_t a2,mpdm_t a3,mpdm_t ctxt)210 mpdm_t mpdm_exec_3(mpdm_t c, mpdm_t a1, mpdm_t a2, mpdm_t a3, mpdm_t ctxt)
211 {
212     mpdm_t a = MPDM_A(3);
213 
214     mpdm_set_i(a, a1, 0);
215     mpdm_set_i(a, a2, 1);
216     mpdm_set_i(a, a3, 2);
217 
218     return mpdm_exec(c, a, ctxt);
219 }
220 
221 
222 /**
223  * mpdm_iterator - Iterates through the content of a set.
224  * @set: the set (hash, array, file or scalar)
225  * @context: A pointer to an opaque context
226  * @v: a pointer to a value to store the key
227  * @i: a pointer to a value to store the index
228  *
229  * Iterates through the @set. If it's a hash, every value/index pair
230  * is returned on each call. If it's an array, @v contains the
231  * element and @i the index number on each call. If it's a file,
232  * @v contains the line read and @i the index number. Otherwise, it's
233  * assumed to be a string containing a numeral and @v and @i are filled
234  * with values from 0 to @set - 1 on each call.
235  *
236  * Any of @v and @i pointers can be NULL if the value is not of interest.
237  *
238  * The @context pointer to integer is opaque and should be
239  * initialized to zero on the first call.
240  *
241  * Returns 0 if no more data is left in @set.
242  * [Hashes]
243  * [Arrays]
244  */
mpdm_iterator(mpdm_t set,int * context,mpdm_t * v,mpdm_t * i)245 int mpdm_iterator(mpdm_t set, int *context, mpdm_t *v, mpdm_t *i)
246 {
247     int ret = 0;
248 
249     mpdm_ref(set);
250     ret = mpdm_type_vc(set)->iterator(set, context, v, i);
251     mpdm_unrefnd(set);
252 
253     return ret;
254 }
255 
256 
vc_default_map(mpdm_t set,mpdm_t filter,mpdm_t ctxt)257 mpdm_t vc_default_map(mpdm_t set, mpdm_t filter, mpdm_t ctxt)
258 {
259     mpdm_t r = MPDM_A(0);
260     int n = 0;
261     mpdm_t v, i;
262 
263     while (mpdm_iterator(set, &n, &v, &i)) {
264         mpdm_t w = NULL;
265         mpdm_ref(v);
266         mpdm_ref(i);
267 
268         switch (mpdm_type(filter)) {
269         case MPDM_TYPE_FUNCTION:
270         case MPDM_TYPE_PROGRAM:
271             w = mpdm_exec_2(filter, v, i, ctxt);
272             break;
273 
274         case MPDM_TYPE_ARRAY:
275         case MPDM_TYPE_OBJECT:
276             w = mpdm_get(filter, v);
277             break;
278 
279         case MPDM_TYPE_REGEX:
280             w = mpdm_regex(v, filter, 0);
281             break;
282 
283         case MPDM_TYPE_STRING:
284             w = mpdm_fmt(filter, v);
285             break;
286 
287         default:
288             w = v;
289             break;
290         }
291 
292         mpdm_push(r, w);
293 
294         mpdm_unref(i);
295         mpdm_unref(v);
296     }
297 
298     return r;
299 }
300 
301 
mpdm_map(mpdm_t set,mpdm_t filter,mpdm_t ctxt)302 mpdm_t mpdm_map(mpdm_t set, mpdm_t filter, mpdm_t ctxt)
303 {
304     mpdm_t r;
305 
306     mpdm_ref(set);
307     mpdm_ref(filter);
308     mpdm_ref(ctxt);
309 
310     r = mpdm_type_vc(set)->map(set, filter, ctxt);
311 
312     mpdm_unref(ctxt);
313     mpdm_unref(filter);
314     mpdm_unref(set);
315 
316     return r;
317 }
318 
319 
mpdm_omap(mpdm_t set,mpdm_t filter,mpdm_t ctxt)320 mpdm_t mpdm_omap(mpdm_t set, mpdm_t filter, mpdm_t ctxt)
321 {
322     mpdm_t v, i;
323     mpdm_t out = MPDM_O();
324     int n = 0;
325 
326     mpdm_ref(set);
327     mpdm_ref(filter);
328     mpdm_ref(ctxt);
329 
330     out = MPDM_O();
331 
332     while (mpdm_iterator(set, &n, &v, &i)) {
333         mpdm_t w = NULL;
334         mpdm_ref(i);
335         mpdm_ref(v);
336 
337         switch (mpdm_type(filter)) {
338         case MPDM_TYPE_NULL:
339             /* invert hash */
340             w = MPDM_A(2);
341             mpdm_set_i(w, v, 0);
342             mpdm_set_i(w, i, 1);
343 
344             break;
345 
346         case MPDM_TYPE_FUNCTION:
347         case MPDM_TYPE_PROGRAM:
348             w = mpdm_exec_2(filter, v, i, ctxt);
349             break;
350 
351         case MPDM_TYPE_ARRAY:
352             /* the set provides the values,
353                the filter array provides the indexes */
354             w = MPDM_A(2);
355             mpdm_set_i(w, v, 0);
356             mpdm_set_i(w, mpdm_get(filter, i), 1);
357 
358             break;
359 
360         case MPDM_TYPE_OBJECT:
361             w = mpdm_get(filter, v);
362             break;
363 
364         case MPDM_TYPE_REGEX:
365             w = mpdm_regex(v, filter, 0);
366             break;
367 
368         case MPDM_TYPE_STRING:
369             w = mpdm_fmt(filter, v);
370             break;
371 
372         default:
373             break;
374         }
375 
376         mpdm_ref(w);
377 
378         /* if the filtered value is an array, it's a value/index pair */
379         if (mpdm_type(w) == MPDM_TYPE_ARRAY)
380             mpdm_set(out, mpdm_get_i(w, 0), mpdm_get_i(w, 1));
381         else
382             mpdm_set(out, w, i);
383 
384         mpdm_unref(w);
385         mpdm_unref(v);
386         mpdm_unref(i);
387     }
388 
389     mpdm_unref(ctxt);
390     mpdm_unref(filter);
391     mpdm_unref(set);
392 
393     return out;
394 }
395 
396 
mpdm_grep(mpdm_t set,mpdm_t filter,mpdm_t ctxt)397 mpdm_t mpdm_grep(mpdm_t set, mpdm_t filter, mpdm_t ctxt)
398 {
399     mpdm_t out = NULL;
400 
401     mpdm_ref(set);
402     mpdm_ref(filter);
403     mpdm_ref(ctxt);
404 
405     if (set != NULL) {
406         mpdm_t v, i;
407         int n = 0;
408 
409         out = mpdm_type(set) == MPDM_TYPE_OBJECT ? MPDM_O() : MPDM_A(0);
410 
411         while (mpdm_iterator(set, &n, &v, &i)) {
412             mpdm_t w = NULL;
413             mpdm_ref(i);
414             mpdm_ref(v);
415 
416             switch (mpdm_type(filter)) {
417             case MPDM_TYPE_FUNCTION:
418             case MPDM_TYPE_PROGRAM:
419                 w = mpdm_exec_2(filter, v, i, ctxt);
420                 break;
421 
422             case MPDM_TYPE_REGEX:
423             case MPDM_TYPE_STRING:
424                 w = mpdm_regex(v, filter, 0);
425                 break;
426 
427             case MPDM_TYPE_OBJECT:
428                 w = mpdm_bool(mpdm_exists(filter, v));
429                 break;
430 
431             default:
432                 break;
433             }
434 
435             if (mpdm_is_true(w)) {
436                 if (mpdm_type(out) == MPDM_TYPE_OBJECT)
437                     mpdm_set(out, v, i);
438                 else
439                     mpdm_push(out, v);
440             }
441 
442             mpdm_unref(v);
443             mpdm_unref(i);
444         }
445     }
446 
447     mpdm_unref(ctxt);
448     mpdm_unref(filter);
449     mpdm_unref(set);
450 
451     return out;
452 }
453 
454 
455 /**
456  * mpdm_join - Joins two values.
457  * @a: first value
458  * @b: second value
459  *
460  * Joins two values. If both are hashes, a new hash containing the
461  * pairs in @a overwritten with the keys in @b is returned; if both
462  * are arrays, a new array is returned with all elements in @a followed
463  * by all elements in b; if @a is an array and @b is a string,
464  * a new string is returned with all elements in @a joined using @b
465  * as a separator; and if @a is a hash and @b is a string, a new array
466  * is returned containing all pairs in @a joined using @b as a separator.
467  * [Arrays]
468  * [Hashes]
469  * [Strings]
470  */
mpdm_join(const mpdm_t a,const mpdm_t b)471 mpdm_t mpdm_join(const mpdm_t a, const mpdm_t b)
472 {
473     int n, c = 0;
474     mpdm_t r, v, i;
475 
476     mpdm_ref(a);
477     mpdm_ref(b);
478 
479     switch (mpdm_type(a)) {
480     case MPDM_TYPE_OBJECT:
481 
482         switch (mpdm_type(b)) {
483         case MPDM_TYPE_OBJECT:
484             /* hash~hash -> hash */
485             r = MPDM_O();
486 
487             n = 0;
488             while (mpdm_iterator(a, &n, &v, &i))
489                 mpdm_set(r, v, i);
490             n = 0;
491             while (mpdm_iterator(b, &n, &v, &i))
492                 mpdm_set(r, v, i);
493 
494             break;
495 
496         case MPDM_TYPE_ARRAY:
497             /* hash~array -> hash */
498             r = MPDM_O();
499 
500             /* the array is a list of pairs */
501             for (n = 0; n < mpdm_size(b); n += 2)
502                 mpdm_set(r, mpdm_get_i(b, n + 1), mpdm_get_i(b, n));
503 
504             break;
505 
506         case MPDM_TYPE_STRING:
507             /* hash~string -> array */
508             r = MPDM_A(mpdm_count(a));
509 
510             n = 0;
511             while (mpdm_iterator(a, &n, &v, &i))
512                 mpdm_set_i(r, mpdm_strcat(i, mpdm_strcat(b, v)), c++);
513 
514             break;
515 
516         default:
517             r = NULL;
518             break;
519         }
520 
521         break;
522 
523     case MPDM_TYPE_ARRAY:
524     case MPDM_TYPE_FILE:
525         switch (mpdm_type(b)) {
526         case MPDM_TYPE_ARRAY:
527         case MPDM_TYPE_FILE:
528             /* array~array -> array */
529             r = MPDM_A(0);
530 
531             n = 0;
532             while (mpdm_iterator(a, &n, &v, NULL))
533                 mpdm_push(r, v);
534             n = 0;
535             while (mpdm_iterator(b, &n, &v, NULL))
536                 mpdm_push(r, v);
537 
538             break;
539 
540         case MPDM_TYPE_STRING:
541         case MPDM_TYPE_NULL:
542             /* array~string -> string */
543             r = mpdm_join_wcs(a, b ? mpdm_string(b) : NULL);
544 
545             break;
546 
547         default:
548             r = NULL;
549             break;
550         }
551 
552         break;
553 
554     case MPDM_TYPE_STRING:
555         /* string~string -> string */
556         r = mpdm_strcat(a, b);
557         break;
558 
559     case MPDM_TYPE_INTEGER:
560     case MPDM_TYPE_REAL:
561         /* real~real -> sum! */
562         r = MPDM_R(mpdm_rval(a) + mpdm_rval(b));
563         break;
564 
565     default:
566         r = NULL;
567         break;
568     }
569 
570     mpdm_unref(b);
571     mpdm_unref(a);
572 
573     return r;
574 }
575 
576 
577 mpdm_t mpdm_splice_a(const mpdm_t v, const mpdm_t i,
578                      int offset, int del, mpdm_t *n, mpdm_t *d);
579 mpdm_t mpdm_splice_s(const mpdm_t v, const mpdm_t i,
580                      int offset, int del, mpdm_t *n, mpdm_t *d);
581 
mpdm_splice(const mpdm_t v,const mpdm_t i,int offset,int del,mpdm_t * n,mpdm_t * d)582 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i,
583                    int offset, int del, mpdm_t *n, mpdm_t *d)
584 {
585     mpdm_t r;
586 
587     switch (mpdm_type(v)) {
588     case MPDM_TYPE_NULL:
589     case MPDM_TYPE_STRING:
590         r = mpdm_splice_s(v, i, offset, del, n, d);
591         break;
592 
593     case MPDM_TYPE_ARRAY:
594         r = mpdm_splice_a(v, i, offset, del, n, d);
595         break;
596 
597     default:
598         r = NULL;
599         break;
600     }
601 
602     return r;
603 }
604 
605 
606 /**
607  * mpdm_cmp - Compares two values.
608  * @v1: the first value
609  * @v2: the second value
610  *
611  * Compares two values. If both has the MPDM_STRING flag set,
612  * a comparison using wcscoll() is returned; if both are arrays,
613  * the size is compared first and, if they have the same number
614  * elements, each one is compared; otherwise, a simple visual
615  * representation comparison is done.
616  * [Strings]
617  */
mpdm_cmp(const mpdm_t v1,const mpdm_t v2)618 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
619 {
620     int r;
621 
622     mpdm_ref(v1);
623     mpdm_ref(v2);
624 
625     /* same values? */
626     if (v1 == v2)
627         r = 0;
628     else
629     if (v1 == NULL)
630         r = -1;
631     else
632     if (v2 == NULL)
633         r = 1;
634     else {
635         switch (mpdm_type(v1)) {
636         case MPDM_TYPE_NULL:
637             r = -1;
638             break;
639 
640         case MPDM_TYPE_INTEGER:
641             r = mpdm_ival(v1) - mpdm_ival(v2);
642             break;
643 
644         case MPDM_TYPE_REAL:
645             {
646                 double d = mpdm_rval(v1) - mpdm_rval(v2);
647                 r = d < 0.0 ? -1 : d > 0.0 ? 1 : 0;
648             }
649             break;
650 
651         case MPDM_TYPE_ARRAY:
652         case MPDM_TYPE_OBJECT:
653         case MPDM_TYPE_PROGRAM:
654 
655             if (mpdm_type(v2) == mpdm_type(v1)) {
656                 /* if they are the same size, compare elements one by one */
657                 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
658                     int n = 0;
659                     mpdm_t v, i;
660 
661                     while (mpdm_iterator(v1, &n, &v, &i)) {
662                         if ((r = mpdm_cmp(v, mpdm_get(v2, i))) != 0)
663                             break;
664                     }
665                 }
666 
667                 break;
668             }
669 
670             /* fallthrough */
671 
672         default:
673             r = mpdm_cmp_wcs(v1, v2 ? mpdm_string(v2) : NULL);
674             break;
675         }
676     }
677 
678     mpdm_unref(v2);
679     mpdm_unref(v1);
680 
681     return r;
682 }
683 
684 
mpdm_multiply(mpdm_t v,mpdm_t i)685 mpdm_t mpdm_multiply(mpdm_t v, mpdm_t i)
686 {
687     mpdm_t r = NULL;
688 
689     switch (mpdm_type(v)) {
690     case MPDM_TYPE_INTEGER:
691     case MPDM_TYPE_REAL:
692         r = MPDM_R(mpdm_rval(v) * mpdm_rval(i));
693         break;
694 
695     case MPDM_TYPE_STRING:
696         /* replicate string */
697         {
698             int n = mpdm_ival(i);
699             wchar_t *ptr = NULL;
700             int z = 0;
701 
702             while (n) {
703                 ptr = mpdm_pokev(ptr, &z, v);
704                 n--;
705             }
706 
707             r = MPDM_NS(ptr, z);
708         }
709 
710         break;
711 
712     case MPDM_TYPE_ARRAY:
713         /* replicate an array */
714         {
715             int m, n, c;
716 
717             c = mpdm_ival(i);
718             r = MPDM_A(c * mpdm_size(v));
719 
720             for (n = 0; n < mpdm_size(v); n++) {
721                 mpdm_t w = mpdm_get_i(v, n);
722 
723                 for (m = 0; m < c; m++)
724                     mpdm_set_i(r, w, m * mpdm_size(v) + n);
725             }
726         }
727 
728         break;
729 
730     default:
731         break;
732     }
733 
734     return r;
735 }
736 
737 
mpdm_substract(mpdm_t m,mpdm_t s)738 mpdm_t mpdm_substract(mpdm_t m, mpdm_t s)
739 {
740     int n;
741     mpdm_t v, i;
742     mpdm_t r = NULL;
743 
744     switch (mpdm_type(m)) {
745     case MPDM_TYPE_INTEGER:
746     case MPDM_TYPE_REAL:
747     case MPDM_TYPE_STRING:
748         r = MPDM_R(mpdm_rval(m) - mpdm_rval(s));
749         break;
750 
751     case MPDM_TYPE_ARRAY:
752         switch (mpdm_type(s)) {
753         case MPDM_TYPE_ARRAY:
754             r = MPDM_A(0);
755 
756             for (n = 0; n < mpdm_size(m); n++) {
757                 mpdm_t w = mpdm_get_i(m, n);
758 
759                 if (mpdm_seek(s, w, 1) == -1)
760                     mpdm_push(r, w);
761             }
762 
763             break;
764 
765         case MPDM_TYPE_OBJECT:
766             r = MPDM_A(0);
767 
768             for (n = 0; n < mpdm_size(m); n++) {
769                 mpdm_t w = mpdm_get_i(m, n);
770 
771                 if (!mpdm_exists(s, w))
772                     mpdm_push(r, w);
773             }
774 
775             break;
776 
777         default:
778             break;
779         }
780 
781         break;
782 
783     case MPDM_TYPE_OBJECT:
784         switch (mpdm_type(s)) {
785         case MPDM_TYPE_ARRAY:
786             r = MPDM_O();
787 
788             n = 0;
789             while (mpdm_iterator(m, &n, &v, &i)) {
790                 if (mpdm_seek(s, i, 1) == -1)
791                     mpdm_set(r, v, i);
792             }
793 
794             break;
795 
796         case MPDM_TYPE_OBJECT:
797             r = MPDM_O();
798 
799             n = 0;
800             while (mpdm_iterator(m, &n, &v, &i)) {
801                 if (!mpdm_exists(s, i))
802                     mpdm_set(r, v, i);
803             }
804 
805             break;
806 
807         default:
808             break;
809         }
810 
811         break;
812 
813     default:
814         break;
815     }
816 
817     return r;
818 }
819