disjoint_interval_tree 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/test/c/test_dit.c CHANGED
@@ -83,6 +83,19 @@ static const char * current_test = NULL;
83
83
  printf("%s\n", (tests_failed == __before) ? "ok" : "FAILED"); \
84
84
  } while (0)
85
85
 
86
+ ///////////////////////
87
+ // OBJECT CONVENTION //
88
+ ///////////////////////
89
+
90
+ /* Every interval of the test suite carries an object derived from its lower
91
+ * bound, so that any node whose object went astray - most likely the deletion
92
+ * of a node with two children, which moves an interval from one node to
93
+ * another - is caught the moment it is reported.
94
+ *
95
+ * `+1` keeps the object of [0..b[ distinguishable from a NULL object */
96
+ #define OBJ(A) ((dit_object_t) (uintptr_t) ((A) + 1))
97
+ #define OBJ_MATCHES(O, A) ((O) == OBJ(A))
98
+
86
99
  ////////////////////////////
87
100
  // REFERENCE (NAIVE) MODEL //
88
101
  ////////////////////////////
@@ -92,6 +105,7 @@ static const char * current_test = NULL;
92
105
  typedef struct
93
106
  {
94
107
  dit_value_t a, b;
108
+ dit_object_t obj;
95
109
  } interval_t;
96
110
 
97
111
  #define MODEL_CAPACITY 4096
@@ -136,8 +150,9 @@ model_insert(model_t * m, dit_value_t a, dit_value_t b)
136
150
  m->intervals[i] = m->intervals[i - 1];
137
151
  --i;
138
152
  }
139
- m->intervals[i].a = a;
140
- m->intervals[i].b = b;
153
+ m->intervals[i].a = a;
154
+ m->intervals[i].b = b;
155
+ m->intervals[i].obj = OBJ(a);
141
156
  ++m->n;
142
157
 
143
158
  return 1;
@@ -175,6 +190,9 @@ typedef struct
175
190
 
176
191
  /* stop the traversal after that many intervals, 0 to never stop */
177
192
  size_t stop_after;
193
+
194
+ /* number of intervals whose object was not the expected one */
195
+ size_t bad_objects;
178
196
  } collect_t;
179
197
 
180
198
  static void
@@ -182,10 +200,11 @@ collect_init(collect_t * c)
182
200
  {
183
201
  c->n = 0;
184
202
  c->stop_after = 0;
203
+ c->bad_objects = 0;
185
204
  }
186
205
 
187
206
  static int
188
- collect_cb(dit_value_t a, dit_value_t b, void * user)
207
+ collect_cb(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
189
208
  {
190
209
  collect_t * c = (collect_t *) user;
191
210
 
@@ -195,16 +214,84 @@ collect_cb(dit_value_t a, dit_value_t b, void * user)
195
214
  abort();
196
215
  }
197
216
 
198
- c->intervals[c->n].a = a;
199
- c->intervals[c->n].b = b;
217
+ c->intervals[c->n].a = a;
218
+ c->intervals[c->n].b = b;
219
+ c->intervals[c->n].obj = obj;
200
220
  ++c->n;
201
221
 
222
+ if (!OBJ_MATCHES(obj, a))
223
+ ++c->bad_objects;
224
+
202
225
  if (c->stop_after && c->n >= c->stop_after)
203
226
  return 42;
204
227
 
205
228
  return 0;
206
229
  }
207
230
 
231
+ /////////////////////////
232
+ // VERIFYING CALLBACK //
233
+ /////////////////////////
234
+
235
+ /* Same checks as the collector, without the storage: for trees holding more
236
+ * than MODEL_CAPACITY intervals */
237
+
238
+ typedef struct
239
+ {
240
+ size_t n;
241
+ size_t bad_objects;
242
+ size_t out_of_order;
243
+ dit_value_t prev_b;
244
+ int has_prev;
245
+ } verify_t;
246
+
247
+ static void
248
+ verify_init(verify_t * v)
249
+ {
250
+ v->n = 0;
251
+ v->bad_objects = 0;
252
+ v->out_of_order = 0;
253
+ v->prev_b = 0;
254
+ v->has_prev = 0;
255
+ }
256
+
257
+ static int
258
+ verify_cb(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
259
+ {
260
+ verify_t * v = (verify_t *) user;
261
+
262
+ if (!OBJ_MATCHES(obj, a))
263
+ ++v->bad_objects;
264
+
265
+ if (v->has_prev && v->prev_b > a)
266
+ ++v->out_of_order;
267
+
268
+ v->prev_b = b;
269
+ v->has_prev = 1;
270
+ ++v->n;
271
+
272
+ return 0;
273
+ }
274
+
275
+ #define ASSERT_VERIFIED(V) \
276
+ do { \
277
+ ++checks_run; \
278
+ if ((V)->bad_objects) \
279
+ FAIL("%zu of the %zu reported intervals carried a wrong object",\
280
+ (V)->bad_objects, (V)->n); \
281
+ if ((V)->out_of_order) \
282
+ FAIL("%zu of the %zu reported intervals were out of order", \
283
+ (V)->out_of_order, (V)->n); \
284
+ } while (0)
285
+
286
+ /* every reported interval carried the object it was inserted with */
287
+ #define ASSERT_OBJECTS_OK(C) \
288
+ do { \
289
+ ++checks_run; \
290
+ if ((C)->bad_objects) \
291
+ FAIL("%zu of the %zu reported intervals carried a wrong object",\
292
+ (C)->bad_objects, (C)->n); \
293
+ } while (0)
294
+
208
295
  ///////////
209
296
  // TESTS //
210
297
  ///////////
@@ -222,7 +309,7 @@ test_empty(void)
222
309
  ASSERT(dit_at(&tree, 0) == NULL);
223
310
  ASSERT(dit_intersecting(&tree, 0, 100) == NULL);
224
311
  ASSERT_EQ(dit_intersect_p(&tree, 0, 100), 0);
225
- ASSERT_EQ(dit_remove(&tree, 0, 100), 0);
312
+ ASSERT_EQ(dit_remove(&tree, 0, 100, NULL, NULL), 0);
226
313
 
227
314
  /* an empty tree has no hull, and the out params are left untouched */
228
315
  dit_value_t ha = 42;
@@ -247,11 +334,12 @@ test_insert_single(void)
247
334
  dit_t tree;
248
335
  dit_init(&tree);
249
336
 
250
- ASSERT_EQ(dit_insert(&tree, 10, 20), DIT_OK);
337
+ ASSERT_EQ(dit_insert(&tree, 10, 20, OBJ(10)), DIT_OK);
251
338
  ASSERT_COHERENT(&tree);
252
339
  ASSERT_EQ(dit_size(&tree), 1);
253
340
  ASSERT_EQ(dit_empty(&tree), 0);
254
341
  ASSERT_EQ(dit_height(&tree), 1);
342
+ ASSERT(tree.root->obj == OBJ(10));
255
343
 
256
344
  /* the hull of a single node is the node interval itself */
257
345
  ASSERT_EQ(tree.root->augment.hull.a, 10);
@@ -274,8 +362,8 @@ test_insert_empty_interval(void)
274
362
  dit_t tree;
275
363
  dit_init(&tree);
276
364
 
277
- ASSERT_EQ(dit_insert(&tree, 10, 10), DIT_EMPTY);
278
- ASSERT_EQ(dit_insert(&tree, 20, 10), DIT_EMPTY);
365
+ ASSERT_EQ(dit_insert(&tree, 10, 10, OBJ(10)), DIT_EMPTY);
366
+ ASSERT_EQ(dit_insert(&tree, 20, 10, OBJ(20)), DIT_EMPTY);
279
367
  ASSERT_EQ(dit_size(&tree), 0);
280
368
  ASSERT_COHERENT(&tree);
281
369
 
@@ -288,25 +376,25 @@ test_insert_overlap_is_rejected(void)
288
376
  dit_t tree;
289
377
  dit_init(&tree);
290
378
 
291
- ASSERT_EQ(dit_insert(&tree, 10, 20), DIT_OK);
292
- ASSERT_EQ(dit_insert(&tree, 30, 40), DIT_OK);
379
+ ASSERT_EQ(dit_insert(&tree, 10, 20, OBJ(10)), DIT_OK);
380
+ ASSERT_EQ(dit_insert(&tree, 30, 40, OBJ(30)), DIT_OK);
293
381
 
294
382
  /* every flavour of overlap */
295
- ASSERT_EQ(dit_insert(&tree, 10, 20), DIT_OVERLAP); /* identical */
296
- ASSERT_EQ(dit_insert(&tree, 12, 18), DIT_OVERLAP); /* strictly included */
297
- ASSERT_EQ(dit_insert(&tree, 5, 25), DIT_OVERLAP); /* strictly includes */
298
- ASSERT_EQ(dit_insert(&tree, 5, 11), DIT_OVERLAP); /* overlaps the left */
299
- ASSERT_EQ(dit_insert(&tree, 19, 25), DIT_OVERLAP); /* overlaps the right*/
300
- ASSERT_EQ(dit_insert(&tree, 0, 35), DIT_OVERLAP); /* spans both */
383
+ ASSERT_EQ(dit_insert(&tree, 10, 20, OBJ(10)), DIT_OVERLAP); /* identical */
384
+ ASSERT_EQ(dit_insert(&tree, 12, 18, OBJ(12)), DIT_OVERLAP); /* strictly included */
385
+ ASSERT_EQ(dit_insert(&tree, 5, 25, OBJ( 5)), DIT_OVERLAP); /* strictly includes */
386
+ ASSERT_EQ(dit_insert(&tree, 5, 11, OBJ( 5)), DIT_OVERLAP); /* overlaps the left */
387
+ ASSERT_EQ(dit_insert(&tree, 19, 25, OBJ(19)), DIT_OVERLAP); /* overlaps the right*/
388
+ ASSERT_EQ(dit_insert(&tree, 0, 35, OBJ( 0)), DIT_OVERLAP); /* spans both */
301
389
 
302
390
  /* and the tree was left untouched */
303
391
  ASSERT_EQ(dit_size(&tree), 2);
304
392
  ASSERT_COHERENT(&tree);
305
393
 
306
394
  /* adjacent intervals do not overlap */
307
- ASSERT_EQ(dit_insert(&tree, 0, 10), DIT_OK);
308
- ASSERT_EQ(dit_insert(&tree, 20, 30), DIT_OK);
309
- ASSERT_EQ(dit_insert(&tree, 40, 50), DIT_OK);
395
+ ASSERT_EQ(dit_insert(&tree, 0, 10, OBJ( 0)), DIT_OK);
396
+ ASSERT_EQ(dit_insert(&tree, 20, 30, OBJ(20)), DIT_OK);
397
+ ASSERT_EQ(dit_insert(&tree, 40, 50, OBJ(40)), DIT_OK);
310
398
  ASSERT_EQ(dit_size(&tree), 5);
311
399
  ASSERT_COHERENT(&tree);
312
400
 
@@ -322,7 +410,7 @@ test_ordering(void)
322
410
  /* inserted out of order */
323
411
  const dit_value_t starts[] = { 50, 10, 90, 30, 70, 0, 20 };
324
412
  for (size_t i = 0 ; i < sizeof(starts) / sizeof(*starts) ; ++i)
325
- ASSERT_EQ(dit_insert(&tree, starts[i], starts[i] + 5), DIT_OK);
413
+ ASSERT_EQ(dit_insert(&tree, starts[i], starts[i] + 5, OBJ(starts[i])), DIT_OK);
326
414
  ASSERT_COHERENT(&tree);
327
415
 
328
416
  collect_t c;
@@ -349,40 +437,40 @@ test_hull_augment(void)
349
437
 
350
438
  dit_value_t a, b;
351
439
 
352
- ASSERT_EQ(dit_insert(&tree, 100, 110), DIT_OK);
440
+ ASSERT_EQ(dit_insert(&tree, 100, 110, OBJ(100)), DIT_OK);
353
441
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
354
442
  ASSERT_EQ(a, 100);
355
443
  ASSERT_EQ(b, 110);
356
444
 
357
445
  /* growing on the left moves the lower bound only */
358
- ASSERT_EQ(dit_insert(&tree, 10, 20), DIT_OK);
446
+ ASSERT_EQ(dit_insert(&tree, 10, 20, OBJ(10)), DIT_OK);
359
447
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
360
448
  ASSERT_EQ(a, 10);
361
449
  ASSERT_EQ(b, 110);
362
450
 
363
451
  /* growing on the right moves the upper bound only */
364
- ASSERT_EQ(dit_insert(&tree, 200, 210), DIT_OK);
452
+ ASSERT_EQ(dit_insert(&tree, 200, 210, OBJ(200)), DIT_OK);
365
453
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
366
454
  ASSERT_EQ(a, 10);
367
455
  ASSERT_EQ(b, 210);
368
456
 
369
457
  /* inserting in between changes nothing */
370
- ASSERT_EQ(dit_insert(&tree, 50, 60), DIT_OK);
458
+ ASSERT_EQ(dit_insert(&tree, 50, 60, OBJ(50)), DIT_OK);
371
459
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
372
460
  ASSERT_EQ(a, 10);
373
461
  ASSERT_EQ(b, 210);
374
462
 
375
463
  /* enough insertions to trigger rotations at the root */
376
464
  for (dit_value_t i = 0 ; i < 64 ; ++i)
377
- ASSERT_EQ(dit_insert(&tree, 1000 + 10 * i, 1000 + 10 * i + 5), DIT_OK);
465
+ ASSERT_EQ(dit_insert(&tree, 1000 + 10 * i, 1000 + 10 * i + 5, OBJ(1000 + 10 * i)), DIT_OK);
378
466
  ASSERT_COHERENT(&tree);
379
467
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
380
468
  ASSERT_EQ(a, 10);
381
469
  ASSERT_EQ(b, 1635);
382
470
 
383
471
  /* the hull shrinks back when the extremities are removed */
384
- ASSERT_EQ(dit_remove(&tree, 10, 20), 1);
385
- ASSERT_EQ(dit_remove(&tree, 1630, 1635), 1);
472
+ ASSERT_EQ(dit_remove(&tree, 10, 20, NULL, NULL), 1);
473
+ ASSERT_EQ(dit_remove(&tree, 1630, 1635, NULL, NULL), 1);
386
474
  ASSERT_COHERENT(&tree);
387
475
  ASSERT_EQ(dit_hull(&tree, &a, &b), 1);
388
476
  ASSERT_EQ(a, 50);
@@ -411,7 +499,7 @@ test_intersect(void)
411
499
 
412
500
  /* [0..10[ [20..30[ [40..50[ [60..70[ */
413
501
  for (dit_value_t i = 0 ; i < 4 ; ++i)
414
- ASSERT_EQ(dit_insert(&tree, 20 * i, 20 * i + 10), DIT_OK);
502
+ ASSERT_EQ(dit_insert(&tree, 20 * i, 20 * i + 10, OBJ(20 * i)), DIT_OK);
415
503
  ASSERT_COHERENT(&tree);
416
504
 
417
505
  collect_t c;
@@ -468,7 +556,7 @@ test_intersect_early_stop(void)
468
556
  dit_init(&tree);
469
557
 
470
558
  for (dit_value_t i = 0 ; i < 100 ; ++i)
471
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
559
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
472
560
 
473
561
  collect_t c;
474
562
  collect_init(&c);
@@ -496,34 +584,34 @@ test_remove(void)
496
584
 
497
585
  /* [0..10[ [20..30[ [40..50[ [60..70[ */
498
586
  for (dit_value_t i = 0 ; i < 4 ; ++i)
499
- ASSERT_EQ(dit_insert(&tree, 20 * i, 20 * i + 10), DIT_OK);
587
+ ASSERT_EQ(dit_insert(&tree, 20 * i, 20 * i + 10, OBJ(20 * i)), DIT_OK);
500
588
 
501
589
  /* removing a hole removes nothing */
502
- ASSERT_EQ(dit_remove(&tree, 10, 20), 0);
590
+ ASSERT_EQ(dit_remove(&tree, 10, 20, NULL, NULL), 0);
503
591
  ASSERT_EQ(dit_size(&tree), 4);
504
592
  ASSERT_COHERENT(&tree);
505
593
 
506
594
  /* intervals are removed as a whole, never split */
507
- ASSERT_EQ(dit_remove(&tree, 25, 26), 1);
595
+ ASSERT_EQ(dit_remove(&tree, 25, 26, NULL, NULL), 1);
508
596
  ASSERT_EQ(dit_size(&tree), 3);
509
597
  ASSERT(dit_at(&tree, 20) == NULL);
510
598
  ASSERT(dit_at(&tree, 29) == NULL);
511
599
  ASSERT_COHERENT(&tree);
512
600
 
513
601
  /* removing a range spanning several intervals */
514
- ASSERT_EQ(dit_remove(&tree, 5, 45), 2);
602
+ ASSERT_EQ(dit_remove(&tree, 5, 45, NULL, NULL), 2);
515
603
  ASSERT_EQ(dit_size(&tree), 1);
516
604
  ASSERT(dit_at(&tree, 65) != NULL);
517
605
  ASSERT_COHERENT(&tree);
518
606
 
519
607
  /* removing everything */
520
- ASSERT_EQ(dit_remove(&tree, 0, 1000), 1);
608
+ ASSERT_EQ(dit_remove(&tree, 0, 1000, NULL, NULL), 1);
521
609
  ASSERT_EQ(dit_size(&tree), 0);
522
610
  ASSERT(dit_empty(&tree));
523
611
  ASSERT_COHERENT(&tree);
524
612
 
525
613
  /* removing from an empty tree */
526
- ASSERT_EQ(dit_remove(&tree, 0, 1000), 0);
614
+ ASSERT_EQ(dit_remove(&tree, 0, 1000, NULL, NULL), 0);
527
615
  ASSERT_COHERENT(&tree);
528
616
 
529
617
  dit_destroy(&tree);
@@ -536,24 +624,24 @@ test_remove_reinsert(void)
536
624
  dit_init(&tree);
537
625
 
538
626
  for (dit_value_t i = 0 ; i < 256 ; ++i)
539
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
627
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
540
628
  ASSERT_COHERENT(&tree);
541
629
 
542
630
  /* remove every other interval */
543
631
  for (dit_value_t i = 0 ; i < 256 ; i += 2)
544
- ASSERT_EQ(dit_remove(&tree, 10 * i, 10 * i + 5), 1);
632
+ ASSERT_EQ(dit_remove(&tree, 10 * i, 10 * i + 5, NULL, NULL), 1);
545
633
  ASSERT_EQ(dit_size(&tree), 128);
546
634
  ASSERT_COHERENT(&tree);
547
635
 
548
636
  /* the removed slots are now free again */
549
637
  for (dit_value_t i = 0 ; i < 256 ; i += 2)
550
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
638
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
551
639
  ASSERT_EQ(dit_size(&tree), 256);
552
640
  ASSERT_COHERENT(&tree);
553
641
 
554
642
  /* the others are not */
555
643
  for (dit_value_t i = 1 ; i < 256 ; i += 2)
556
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OVERLAP);
644
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OVERLAP);
557
645
  ASSERT_COHERENT(&tree);
558
646
 
559
647
  dit_destroy(&tree);
@@ -566,7 +654,7 @@ test_clear(void)
566
654
  dit_init(&tree);
567
655
 
568
656
  for (dit_value_t i = 0 ; i < 100 ; ++i)
569
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
657
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
570
658
 
571
659
  dit_clear(&tree);
572
660
  ASSERT(dit_empty(&tree));
@@ -574,7 +662,7 @@ test_clear(void)
574
662
  ASSERT_COHERENT(&tree);
575
663
 
576
664
  /* the tree is reusable */
577
- ASSERT_EQ(dit_insert(&tree, 0, 1000), DIT_OK);
665
+ ASSERT_EQ(dit_insert(&tree, 0, 1000, OBJ(0)), DIT_OK);
578
666
  ASSERT_EQ(dit_size(&tree), 1);
579
667
  ASSERT_COHERENT(&tree);
580
668
 
@@ -591,7 +679,7 @@ test_balance_sorted_insertions(void)
591
679
  const dit_value_t n = 4095;
592
680
 
593
681
  for (dit_value_t i = 0 ; i < n ; ++i)
594
- ASSERT_EQ(dit_insert(&tree, 2 * i, 2 * i + 1), DIT_OK);
682
+ ASSERT_EQ(dit_insert(&tree, 2 * i, 2 * i + 1, OBJ(2 * i)), DIT_OK);
595
683
 
596
684
  ASSERT_EQ(dit_size(&tree), n);
597
685
  ASSERT_COHERENT(&tree);
@@ -603,7 +691,7 @@ test_balance_sorted_insertions(void)
603
691
  /* and reverse sorted insertions too */
604
692
  dit_clear(&tree);
605
693
  for (dit_value_t i = n ; i > 0 ; --i)
606
- ASSERT_EQ(dit_insert(&tree, 2 * i, 2 * i + 1), DIT_OK);
694
+ ASSERT_EQ(dit_insert(&tree, 2 * i, 2 * i + 1, OBJ(2 * i)), DIT_OK);
607
695
  ASSERT_EQ(dit_size(&tree), n);
608
696
  ASSERT(dit_height(&tree) <= 18);
609
697
  ASSERT_COHERENT(&tree);
@@ -611,7 +699,7 @@ test_balance_sorted_insertions(void)
611
699
  /* sequential deletions from the left */
612
700
  for (dit_value_t i = 1 ; i <= n ; ++i)
613
701
  {
614
- ASSERT_EQ(dit_remove(&tree, 2 * i, 2 * i + 1), 1);
702
+ ASSERT_EQ(dit_remove(&tree, 2 * i, 2 * i + 1, NULL, NULL), 1);
615
703
  ASSERT_EQ(dit_size(&tree), n - i);
616
704
  }
617
705
  ASSERT(dit_empty(&tree));
@@ -627,9 +715,9 @@ test_extreme_values(void)
627
715
  dit_t tree;
628
716
  dit_init(&tree);
629
717
 
630
- ASSERT_EQ(dit_insert(&tree, 0, 1), DIT_OK);
631
- ASSERT_EQ(dit_insert(&tree, DIT_VALUE_MAX - 1, DIT_VALUE_MAX), DIT_OK);
632
- ASSERT_EQ(dit_insert(&tree, 1, DIT_VALUE_MAX - 1), DIT_OK);
718
+ ASSERT_EQ(dit_insert(&tree, 0, 1, OBJ(0)), DIT_OK);
719
+ ASSERT_EQ(dit_insert(&tree, DIT_VALUE_MAX - 1, DIT_VALUE_MAX, OBJ(DIT_VALUE_MAX - 1)), DIT_OK);
720
+ ASSERT_EQ(dit_insert(&tree, 1, DIT_VALUE_MAX - 1, OBJ(1)), DIT_OK);
633
721
  ASSERT_COHERENT(&tree);
634
722
 
635
723
  ASSERT_EQ(tree.root->augment.hull.a, 0);
@@ -645,7 +733,7 @@ test_extreme_values(void)
645
733
  dit_intersect(&tree, 0, DIT_VALUE_MAX, collect_cb, &c);
646
734
  ASSERT_EQ(c.n, 3);
647
735
 
648
- ASSERT_EQ(dit_remove(&tree, 0, DIT_VALUE_MAX), 3);
736
+ ASSERT_EQ(dit_remove(&tree, 0, DIT_VALUE_MAX, NULL, NULL), 3);
649
737
  ASSERT(dit_empty(&tree));
650
738
  ASSERT_COHERENT(&tree);
651
739
 
@@ -662,7 +750,7 @@ test_check_detects_corruption(void)
662
750
  dit_init(&tree);
663
751
 
664
752
  for (dit_value_t i = 0 ; i < 32 ; ++i)
665
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
753
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
666
754
  ASSERT_EQ(dit_check(&tree, err, sizeof(err)), 0);
667
755
 
668
756
  /* corrupt the cardinality */
@@ -721,7 +809,7 @@ test_dump_dot(void)
721
809
  dit_dump_dot(&tree, f);
722
810
 
723
811
  for (dit_value_t i = 0 ; i < 16 ; ++i)
724
- ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5), DIT_OK);
812
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
725
813
 
726
814
  dit_dump_dot(&tree, f);
727
815
  dit_dump_dot(NULL, f);
@@ -732,6 +820,137 @@ test_dump_dot(void)
732
820
  dit_destroy(&tree);
733
821
  }
734
822
 
823
+ /////////////
824
+ // OBJECTS //
825
+ /////////////
826
+
827
+ static void
828
+ test_objects(void)
829
+ {
830
+ dit_t tree;
831
+ dit_init(&tree);
832
+
833
+ int one, two, three;
834
+
835
+ ASSERT_EQ(dit_insert(&tree, 0, 10, &one), DIT_OK);
836
+ ASSERT_EQ(dit_insert(&tree, 20, 30, &two), DIT_OK);
837
+ ASSERT_EQ(dit_insert(&tree, 40, 50, &three), DIT_OK);
838
+ ASSERT_COHERENT(&tree);
839
+
840
+ /* point and range lookups hand the object back */
841
+ ASSERT(dit_at(&tree, 5)->obj == &one);
842
+ ASSERT(dit_at(&tree, 25)->obj == &two);
843
+ ASSERT(dit_at(&tree, 49)->obj == &three);
844
+ ASSERT(dit_intersecting(&tree, 5, 45)->obj != NULL);
845
+
846
+ /* a NULL object is a legitimate object */
847
+ ASSERT_EQ(dit_insert(&tree, 60, 70, NULL), DIT_OK);
848
+ ASSERT(dit_at(&tree, 65) != NULL);
849
+ ASSERT(dit_at(&tree, 65)->obj == NULL);
850
+
851
+ /* two intervals may share the same object */
852
+ ASSERT_EQ(dit_insert(&tree, 80, 90, &one), DIT_OK);
853
+ ASSERT(dit_at(&tree, 85)->obj == &one);
854
+ ASSERT(dit_at(&tree, 5)->obj == &one);
855
+
856
+ /* an object survives the removal of its neighbours */
857
+ ASSERT_EQ(dit_remove(&tree, 20, 30, NULL, NULL), 1);
858
+ ASSERT_EQ(dit_remove(&tree, 40, 50, NULL, NULL), 1);
859
+ ASSERT_COHERENT(&tree);
860
+ ASSERT(dit_at(&tree, 5)->obj == &one);
861
+ ASSERT(dit_at(&tree, 85)->obj == &one);
862
+ ASSERT(dit_at(&tree, 65)->obj == NULL);
863
+
864
+ dit_destroy(&tree);
865
+ }
866
+
867
+ /* The object must travel with its interval when a node with two children is
868
+ * deleted: that deletion moves the in-order successor's interval into the
869
+ * deleted node, and the object has to move with it */
870
+ static void
871
+ test_objects_survive_deletions(void)
872
+ {
873
+ dit_t tree;
874
+ dit_init(&tree);
875
+
876
+ const dit_value_t n = 512;
877
+
878
+ for (dit_value_t i = 0 ; i < n ; ++i)
879
+ ASSERT_EQ(dit_insert(&tree, 10 * i, 10 * i + 5, OBJ(10 * i)), DIT_OK);
880
+
881
+ verify_t v;
882
+ verify_init(&v);
883
+ dit_each(&tree, verify_cb, &v);
884
+ ASSERT_VERIFIED(&v);
885
+ ASSERT_EQ(v.n, n);
886
+
887
+ /* delete in an order that maximizes two-children deletions: always the
888
+ * interval sitting at the root */
889
+ while (!dit_empty(&tree))
890
+ {
891
+ const dit_value_t a = tree.root->a;
892
+
893
+ if (tree.root->obj != OBJ(a))
894
+ FAIL("the root of the tree holds the object of another interval");
895
+
896
+ ASSERT_EQ(dit_remove(&tree, a, a + 1, NULL, NULL), 1);
897
+
898
+ verify_init(&v);
899
+ dit_each(&tree, verify_cb, &v);
900
+ if (v.bad_objects)
901
+ FAIL("%zu intervals carried a wrong object after removing "
902
+ "[%" PRIu64 "..%" PRIu64 "[", v.bad_objects, a, a + 5);
903
+ if (v.n != dit_size(&tree))
904
+ FAIL("the traversal reported %zu of the %zu remaining intervals",
905
+ v.n, dit_size(&tree));
906
+ }
907
+
908
+ ASSERT_COHERENT(&tree);
909
+ dit_destroy(&tree);
910
+ }
911
+
912
+ static void
913
+ test_remove_callback(void)
914
+ {
915
+ dit_t tree;
916
+ dit_init(&tree);
917
+
918
+ /* [0..10[ [20..30[ [40..50[ [60..70[ */
919
+ for (dit_value_t i = 0 ; i < 4 ; ++i)
920
+ ASSERT_EQ(dit_insert(&tree, 20 * i, 20 * i + 10, OBJ(20 * i)), DIT_OK);
921
+
922
+ collect_t c;
923
+
924
+ /* nothing removed, nothing reported */
925
+ collect_init(&c);
926
+ ASSERT_EQ(dit_remove(&tree, 10, 20, collect_cb, &c), 0);
927
+ ASSERT_EQ(c.n, 0);
928
+
929
+ /* every removed interval is reported once, in increasing order, with its
930
+ * object, before the node is freed */
931
+ collect_init(&c);
932
+ ASSERT_EQ(dit_remove(&tree, 5, 45, collect_cb, &c), 3);
933
+ ASSERT_EQ(c.n, 3);
934
+ ASSERT_OBJECTS_OK(&c);
935
+ ASSERT_EQ(c.intervals[0].a, 0);
936
+ ASSERT_EQ(c.intervals[1].a, 20);
937
+ ASSERT_EQ(c.intervals[2].a, 40);
938
+ ASSERT(c.intervals[0].obj == OBJ(0));
939
+ ASSERT(c.intervals[1].obj == OBJ(20));
940
+ ASSERT(c.intervals[2].obj == OBJ(40));
941
+ ASSERT_COHERENT(&tree);
942
+ ASSERT_EQ(dit_size(&tree), 1);
943
+
944
+ /* a removal cannot be interrupted: the callback return value is ignored */
945
+ collect_init(&c);
946
+ c.stop_after = 1;
947
+ ASSERT_EQ(dit_remove(&tree, 0, 1000, collect_cb, &c), 1);
948
+ ASSERT(dit_empty(&tree));
949
+ ASSERT_COHERENT(&tree);
950
+
951
+ dit_destroy(&tree);
952
+ }
953
+
735
954
  ///////////////////////
736
955
  // RANDOMIZED TESTS //
737
956
  ///////////////////////
@@ -783,9 +1002,13 @@ same_as_model(dit_t * tree, const model_t * m, collect_t * c)
783
1002
  if (c->n != m->n)
784
1003
  return 0;
785
1004
 
1005
+ if (c->bad_objects)
1006
+ return 0;
1007
+
786
1008
  for (size_t i = 0 ; i < m->n ; ++i)
787
- if (c->intervals[i].a != m->intervals[i].a ||
788
- c->intervals[i].b != m->intervals[i].b)
1009
+ if (c->intervals[i].a != m->intervals[i].a ||
1010
+ c->intervals[i].b != m->intervals[i].b ||
1011
+ c->intervals[i].obj != m->intervals[i].obj)
789
1012
  return 0;
790
1013
 
791
1014
  return 1;
@@ -817,7 +1040,7 @@ test_random_against_model(void)
817
1040
  case 0:
818
1041
  {
819
1042
  const int insertable = !model_intersect(&model, a, b);
820
- const dit_status_t status = dit_insert(&tree, a, b);
1043
+ const dit_status_t status = dit_insert(&tree, a, b, OBJ(a));
821
1044
 
822
1045
  if (insertable)
823
1046
  {
@@ -835,14 +1058,35 @@ test_random_against_model(void)
835
1058
  break ;
836
1059
  }
837
1060
 
838
- /* remove */
1061
+ /* remove, checking what the removal callback reports */
839
1062
  case 1:
840
1063
  {
841
- const size_t expected = model_remove(&model, a, b);
842
- const size_t got = dit_remove(&tree, a, b);
1064
+ collect_init(&c);
1065
+
1066
+ size_t expected = 0;
1067
+ for (size_t i = 0 ; i < model.n ; ++i)
1068
+ if (a < model.intervals[i].b && model.intervals[i].a < b)
1069
+ ++expected;
1070
+
1071
+ const size_t got = dit_remove(&tree, a, b, collect_cb, &c);
1072
+ model_remove(&model, a, b);
1073
+
843
1074
  if (got != expected)
844
1075
  FAIL("remove([%" PRIu64 "..%" PRIu64 "[) removed %zu "
845
1076
  "intervals, expected %zu", a, b, got, expected);
1077
+
1078
+ /* the callback saw every removed interval, in increasing
1079
+ * order, each with the object it was inserted with */
1080
+ if (c.n != got)
1081
+ FAIL("remove([%" PRIu64 "..%" PRIu64 "[) reported %zu of "
1082
+ "the %zu intervals it removed", a, b, c.n, got);
1083
+ if (c.bad_objects)
1084
+ FAIL("remove([%" PRIu64 "..%" PRIu64 "[) reported %zu "
1085
+ "wrong objects", a, b, c.bad_objects);
1086
+ for (size_t i = 1 ; i < c.n ; ++i)
1087
+ if (c.intervals[i - 1].a >= c.intervals[i].a)
1088
+ FAIL("remove([%" PRIu64 "..%" PRIu64 "[) reported "
1089
+ "intervals out of order at %zu", a, b, i);
846
1090
  break ;
847
1091
  }
848
1092
 
@@ -857,8 +1101,9 @@ test_random_against_model(void)
857
1101
  if (a < model.intervals[i].b && model.intervals[i].a < b)
858
1102
  {
859
1103
  if (expected >= c.n ||
860
- c.intervals[expected].a != model.intervals[i].a ||
861
- c.intervals[expected].b != model.intervals[i].b)
1104
+ c.intervals[expected].a != model.intervals[i].a ||
1105
+ c.intervals[expected].b != model.intervals[i].b ||
1106
+ c.intervals[expected].obj != model.intervals[i].obj)
862
1107
  FAIL("intersect([%" PRIu64 "..%" PRIu64 "[) "
863
1108
  "mismatch at %zu", a, b, expected);
864
1109
  ++expected;
@@ -867,6 +1112,9 @@ test_random_against_model(void)
867
1112
  if (c.n != expected)
868
1113
  FAIL("intersect([%" PRIu64 "..%" PRIu64 "[) reported %zu "
869
1114
  "intervals, expected %zu", a, b, c.n, expected);
1115
+ if (c.bad_objects)
1116
+ FAIL("intersect([%" PRIu64 "..%" PRIu64 "[) reported %zu "
1117
+ "wrong objects", a, b, c.bad_objects);
870
1118
  break ;
871
1119
  }
872
1120
  }
@@ -903,6 +1151,8 @@ test_random_large(void)
903
1151
  dit_t tree;
904
1152
  dit_init(&tree);
905
1153
 
1154
+ verify_t v;
1155
+
906
1156
  size_t inserted = 0;
907
1157
  size_t removed = 0;
908
1158
 
@@ -912,8 +1162,8 @@ test_random_large(void)
912
1162
  const dit_value_t b = a + 1 + rng_below(64);
913
1163
 
914
1164
  if (rng_below(4) == 0)
915
- removed += dit_remove(&tree, a, b);
916
- else if (dit_insert(&tree, a, b) == DIT_OK)
1165
+ removed += dit_remove(&tree, a, b, NULL, NULL);
1166
+ else if (dit_insert(&tree, a, b, OBJ(a)) == DIT_OK)
917
1167
  ++inserted;
918
1168
 
919
1169
  if ((it % 5000) == 0)
@@ -921,6 +1171,14 @@ test_random_large(void)
921
1171
  char err[512];
922
1172
  if (dit_check(&tree, err, sizeof(err)))
923
1173
  FAIL("iteration %d: incoherent tree: %s", it, err);
1174
+
1175
+ /* `dit_check()` cannot know anything about opaque objects, so
1176
+ * walk the tree and check them against the convention */
1177
+ verify_init(&v);
1178
+ dit_each(&tree, verify_cb, &v);
1179
+ if (v.bad_objects)
1180
+ FAIL("iteration %d: %zu of the %zu intervals carried a wrong "
1181
+ "object", it, v.bad_objects, v.n);
924
1182
  }
925
1183
  }
926
1184
 
@@ -929,8 +1187,17 @@ test_random_large(void)
929
1187
  ASSERT(inserted > 1000);
930
1188
  ASSERT(removed > 1000);
931
1189
 
932
- /* removing everything must empty the tree */
933
- dit_remove(&tree, 0, DIT_VALUE_MAX);
1190
+ verify_init(&v);
1191
+ dit_each(&tree, verify_cb, &v);
1192
+ ASSERT_VERIFIED(&v);
1193
+ ASSERT_EQ(v.n, dit_size(&tree));
1194
+
1195
+ /* removing everything must empty the tree, and hand every object back,
1196
+ * in increasing order */
1197
+ verify_init(&v);
1198
+ const size_t all = dit_remove(&tree, 0, DIT_VALUE_MAX, verify_cb, &v);
1199
+ ASSERT_VERIFIED(&v);
1200
+ ASSERT_EQ(v.n, all);
934
1201
  ASSERT(dit_empty(&tree));
935
1202
  ASSERT_COHERENT(&tree);
936
1203
 
@@ -965,7 +1232,7 @@ test_random_insert_then_remove(void)
965
1232
 
966
1233
  for (size_t i = 0 ; i < n ; ++i)
967
1234
  {
968
- if (dit_insert(&tree, 10 * order[i], 10 * order[i] + 7) != DIT_OK)
1235
+ if (dit_insert(&tree, 10 * order[i], 10 * order[i] + 7, OBJ(10 * order[i])) != DIT_OK)
969
1236
  {
970
1237
  free(order);
971
1238
  FAIL("insertion %zu failed", i);
@@ -998,7 +1265,7 @@ test_random_insert_then_remove(void)
998
1265
 
999
1266
  for (size_t i = 0 ; i < n ; ++i)
1000
1267
  {
1001
- if (dit_remove(&tree, 10 * order[i], 10 * order[i] + 7) != 1)
1268
+ if (dit_remove(&tree, 10 * order[i], 10 * order[i] + 7, NULL, NULL) != 1)
1002
1269
  {
1003
1270
  free(order);
1004
1271
  FAIL("removal %zu failed", i);
@@ -1061,6 +1328,9 @@ main(int argc, char ** argv)
1061
1328
  RUN(test_extreme_values);
1062
1329
  RUN(test_check_detects_corruption);
1063
1330
  RUN(test_dump_dot);
1331
+ RUN(test_objects);
1332
+ RUN(test_objects_survive_deletions);
1333
+ RUN(test_remove_callback);
1064
1334
  RUN(test_random_against_model);
1065
1335
  RUN(test_random_large);
1066
1336
  RUN(test_random_insert_then_remove);