summaryrefslogtreecommitdiff
path: root/lib/test.cc
blob: 5cdbf24ad67e55c2c33ce6937a828c3ce6a784e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <string>
#include <vector>

#include "lib/regex.h"
#include "lib/test_helper.h"
#include "src/util/check.h"

static int test(int flags,
                const char* pattern,
                const char* string,
                const char* expected = nullptr) {
    std::vector<std::vector<regoff_t> > submatch;
    if (!parse_submatch(expected, submatch)) {
        fprintf(stderr, "failed to parse submatch string: %s\n", expected);
        exit(1);
    }
    const size_t nmatch = submatch.size();

    static uint32_t total = 0;
    static uint32_t failed = 0;

    const char* prefix = flags & REG_NFA ? "NFA" : "DFA";
    bool with_hist = flags & REG_SUBHIST;
    regex_t re;
    regmatch_t* pmatch = with_hist ? nullptr : new regmatch_t[nmatch];
    subhistory_t* psubhist = nullptr;
    int result;

    result = regcomp(&re, pattern, flags);
    if (result != 0) {
        fprintf(stderr, "%s: regcomp() failed for regexp %s\n", prefix, pattern);
        goto end;
    }

    // run multiple times to ensure everything gets cleaned up properly
    static constexpr uint32_t NRUNS = 2;
    for (uint32_t i = 0; i < NRUNS; ++i) {
        if (with_hist) {
            // With subhistories every regexec() call may allocate offsets. Clean them up at the
            // beginning of the loop, so that the offsets remain available for inspection after the
            // last iteration.
            regfreesub(psubhist);
            psubhist = regparse(&re, string, nmatch);
            result = psubhist ? 0 : 1;
        } else {
            result = regexec(&re, string, nmatch, pmatch, flags);
        }
        if (result != 0) {
            if (nmatch == 0) {
                // failure was expected => it's a success
                result = 0;
            } else if (nmatch > 0) {
                fprintf(stderr,
                        "%s: regexec() failed for regexp %s and string %s\n",
                        prefix, pattern, string);
                goto end;
            }
        } else if (nmatch == 0) {
            // regexed must have failed, something is wrong
            result = REG_NOMATCH;
            fprintf(stderr,
                    "%s: regexec() didn't fail while it should for regexp %s and string %s\n",
                    prefix, pattern, string);
            goto end;
        }
    }

    CHECK(nmatch == 0 || nmatch == re.re_nsub);

    for (uint32_t i = 0; i < nmatch; ++i) {
        const std::vector<regoff_t>& expect_history = submatch[i];
        const size_t expect_size = expect_history.size() / 2;

        if (!with_hist) {
            // Check only the last pair of offsets for each capturing group.
            const regoff_t expect_so = expect_history[2 * expect_size - 2];
            const regoff_t expect_eo = expect_history[2 * expect_size - 1];
            const regmatch_t& actual = pmatch[i];
            if (expect_so != actual.rm_so || expect_eo != actual.rm_eo) {
                result = 1;
                fprintf(stderr,
                        "%s: incorrect submatch for regexp %s and string %s:\n"
                        "\tpmatch[%u].rm_so = %td (expected %td)\n"
                        "\tpmatch[%u].rm_eo = %td (expected %td)\n",
                        prefix, pattern, string,
                        i, actual.rm_so, expect_so,
                        i, actual.rm_eo, expect_eo);
                goto end;
            }
        } else {
            // Check the full history of each capturing group.
            const subhistory_t& actual_history = psubhist[i];
            if (actual_history.size != expect_size) {
                result = 1;
                fprintf(stderr,
                        "%s: wrong subhistory-%u length for regexp %s and string %s: "
                        "expected %zd, have %zd\n",
                        prefix, i, pattern, string, expect_size, actual_history.size);
                goto end;
            }
            for (uint32_t j = 0; j < actual_history.size; ++j) {
                const regoff_t expect_so = expect_history[2 * j];
                const regoff_t expect_eo = expect_history[2 * j + 1];
                const regmatch_t& actual = actual_history.offs[j];
                if (expect_so != actual.rm_so || expect_eo != actual.rm_eo) {
                    result = 1;
                    fprintf(stderr,
                            "%s: incorrect submatch for regexp %s and string %s:\n"
                            "\tpmatch[%u][%u].rm_so = %td (expected %td)\n"
                            "\tpmatch[%u][%u].rm_eo = %td (expected %td)\n",
                            prefix, pattern, string,
                            i, j, actual.rm_so, expect_so,
                            i, j, actual.rm_eo, expect_eo);
                    goto end;
                }
            }
        }
    }

end:
    regfree(&re);
    if (with_hist) {
        regfreesub(psubhist);
    } else {
        delete[] pmatch;
    }

    ++total;
    if (result != 0) {
        ++failed;
        fprintf(stderr, "failed %u of %u\n", failed, total);
    }

    return result;
}

static int test_tstring(const char* pattern, const char* string, const char* expected) {
    regex_t re;
    const tstring_t* tstr = nullptr;
    std::ostringstream s;
    int result;

    result = regcomp(&re, pattern, REG_MULTIPASS | REG_TSTRING);
    if (result != 0) {
        fprintf(stderr, "[t-string] regcomp() failed for regexp %s\n", pattern);
        goto end;
    }

    // run multiple times to ensure everything gets cleaned up properly
    static constexpr uint32_t NRUNS = 2;
    for (uint32_t i = 0; i < NRUNS; ++i) {
        tstr = regtstring(&re, string);
        if (tstr == nullptr) {
            result = 1;
            fprintf(stderr, "regtstring() failed for regexp %s and string %s\n", pattern, string);
            goto end;
        }
    }

    // Convert T-string to a C++ string.
    for (size_t i = 0; i < tstr->length; ++i) {
        const tchar_t c = tstr->string[i];
        if (c < TAG_BASE) {
            CHECK(c < 0xff); // expect 1-byte characters
            s << static_cast<char>(c);
        } else {
            s << c - TAG_BASE;
        }
        s << ' ';
    }

    if (strcmp(expected, s.str().c_str()) != 0) {
        result = 1;
        fprintf(stderr,
                "incorrect t-string for regexp %s and string %s:\n\texpect: %s\n\tactual: %s\n",
                pattern, string, expected, s.str().c_str());
    }

end:
    regfree(&re);
    return result;
}

static int test_all_posix(int f) {
    int e = 0;

    e |= test(f, "(a+(c+))|(b+(d+))", "ac", "(0,2),(0,2),(1,2),(?,?),(?,?)");

    e |= test(f, "(aaaa|aaa|a)+",    "aaaaaaaaaa", "(0,10),(0,4)(4,8)(8,9)(9,10)");
    e |= test(f, "(aaaa|aaa|a){3,}", "aaaaaaaaaa", "(0,10),(0,4)(4,8)(8,9)(9,10)");
    e |= test(f, "(aaaa|aaa|a){3,4}", "aaaaaaaaaa", "(0,10),(0,4)(4,8)(8,9)(9,10)");

    e |= test(f, "(aaaaaa|aaaaa|aaaa|aa|a){3,4}", "aaaaaaaaaaaaaaa",
              "(0,15),(0,6)(6,12)(12,14)(14,15)");

    e |= test(f, "(aaaaa?a?|aa?){1,4}", "aaaaaaaaaaaaaaa", "(0,15),(0,6)(6,12)(12,14)(14,15)");

    e |= test(f, "(((a){3,4}a?)()a|aa?){1,4}", "aaaaaaaaaaaaaaa",
              "(0,15),(0,6)(6,12)(12,14)(14,15),(0,5)(6,11)(?,?)(?,?),"
              "(0,1)(1,2)(2,3)(3,4)(6,7)(7,8)(8,9)(9,10)(?,?)(?,?),(5,5)(11,11)(?,?)(?,?)");

    e |= test(f, "(((aaaa?|a?)){1,4})+", "aaaaaaaaaa",
              "(0,10),(0,10),(0,4)(4,8)(8,9)(9,10),(0,4)(4,8)(8,9)(9,10)");

    e |= test(f, "((((a){2,3}(()|a)(()|a?)a|a?)){2,5})*", "aaaaaaaaaaaaaa",
              "(0,14),(0,14),(0,6)(6,12)(12,13)(13,14),(0,6)(6,12)(12,13)(13,14),"
              "(0,1)(1,2)(2,3)(6,7)(7,8)(8,9)(?,?)(?,?),(3,4)(9,10)(?,?)(?,?),"
              "(?,?)(?,?)(?,?)(?,?),(4,5)(10,11)(?,?)(?,?),(?,?)(?,?)(?,?)(?,?)");

    e |= test(f, "(((((a){3,3}a?|a?)){0,4})?)*", "aaaaaaaaaa",
              "(0,10),(0,10),(0,10),(0,4)(4,8)(8,9)(9,10),"
              "(0,4)(4,8)(8,9)(9,10),(0,1)(1,2)(2,3)(4,5)(5,6)(6,7)(?,?)(?,?)");

    e |= test(f, "(((((a){3,4}|a?)){1,4}|((a)+(a|())){1,2}))*", "aaaaaaaaaa",
              "(0,10),(0,10),(0,10),(0,4)(4,8)(8,9)(9,10),(0,4)(4,8)(8,9)(9,10),"
              "(0,1)(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(?,?)(?,?),(?,?),(?,?),(?,?),(?,?)");

    e |= test(f, "((a?|a?(a?a?)((())+)+))*", "aaaaaa",
              "(0,6),(0,3)(3,6),(0,3)(3,6),(1,3)(4,6),(3,3)(6,6),(3,3)(6,6),(3,3)(6,6)");

    e |= test(f, "(((a?a)){2,3})*", "aaaa", "(0,4),(0,4),(0,2)(2,4),(0,2)(2,4)");

    e |= test(f, "(((aa?a)((aaa)+))+)+", "aaaaaaaaaa",
              "(0,10),(0,10),(0,5)(5,10),(0,2)(5,7),(2,5)(7,10),(2,5)(7,10)");

    e |= test(f, "(((aaa?)((aaa)+))+)+", "aaaaaaaaaa",
              "(0,10),(0,10),(0,5)(5,10),(0,2)(5,7),(2,5)(7,10),(2,5)(7,10)");

    e |= test(f, "(((aaa?)((aaa){1,3})){1,2})*", "aaaaaaaaaa",
              "(0,10),(0,10),(0,5)(5,10),(0,2)(5,7),(2,5)(7,10),(2,5)(7,10)");

    e |= test(f, "((((aaa?)(aaa){1,3})){1,2})*", "aaaaaaaaaa",
              "(0,10),(0,10),(0,5)(5,10),(0,5)(5,10),(0,2)(5,7),(2,5)(7,10)");

    e |= test(f, "((((aaa?)((a){3,3}){1,3})){1,2})*", "aaaaaaaaaa",
              "(0,10),(0,10),(0,5)(5,10),(0,5)(5,10),(0,2)(5,7),(2,5)(7,10),"
              "(2,3)(3,4)(4,5)(7,8)(8,9)(9,10)");

    e |= test(f, "(a|aa)*", "",           "(0,0),(?,?)");
    e |= test(f, "(a|aa)*", "a",          "(0,1),(0,1)");
    e |= test(f, "(a|aa)*", "aa",         "(0,2),(0,2)");
    e |= test(f, "(a|aa)*", "aaa",        "(0,3),(0,2)(2,3)");
    e |= test(f, "(a|aa)*", "aaaa",       "(0,4),(0,2)(2,4)");
    e |= test(f, "(a|aa)*", "aaaaa",      "(0,5),(0,2)(2,4)(4,5)");
    e |= test(f, "(a|aa)*", "aaaaaa",     "(0,6),(0,2)(2,4)(4,6)");
    e |= test(f, "(a|aa)*", "aaaaaaa",    "(0,7),(0,2)(2,4)(4,6)(6,7)");
    e |= test(f, "(a|aa)*", "aaaaaaaa",   "(0,8),(0,2)(2,4)(4,6)(6,8)");
    e |= test(f, "(a|aa)*", "aaaaaaaaa",  "(0,9),(0,2)(2,4)(4,6)(6,8)(8,9)");
    e |= test(f, "(a|aa)*", "aaaaaaaaaa", "(0,10),(0,2)(2,4)(4,6)(6,8)(8,10)");
    e |= test(f, "(aa|a)*", "",           "(0,0),(?,?)");
    e |= test(f, "(aa|a)*", "a",          "(0,1),(0,1)");
    e |= test(f, "(aa|a)*", "aa",         "(0,2),(0,2)");
    e |= test(f, "(aa|a)*", "aaa",        "(0,3),(0,2)(2,3)");
    e |= test(f, "(aa|a)*", "aaaa",       "(0,4),(0,2)(2,4)");
    e |= test(f, "(aa|a)*", "aaaaa",      "(0,5),(0,2)(2,4)(4,5)");
    e |= test(f, "(aa|a)*", "aaaaaa",     "(0,6),(0,2)(2,4)(4,6)");
    e |= test(f, "(aa|a)*", "aaaaaaa",    "(0,7),(0,2)(2,4)(4,6)(6,7)");
    e |= test(f, "(aa|a)*", "aaaaaaaa",   "(0,8),(0,2)(2,4)(4,6)(6,8)");
    e |= test(f, "(aa|a)*", "aaaaaaaaa",  "(0,9),(0,2)(2,4)(4,6)(6,8)(8,9)");
    e |= test(f, "(aa|a)*", "aaaaaaaaaa", "(0,10),(0,2)(2,4)(4,6)(6,8)(8,10)");

    e |= test(f, "(aaa|aa)*", "aaaaaaaaaa", "(0,10),(0,3)(3,6)(6,8)(8,10)");
    e |= test(f, "(aa|aaa)*", "aaaaaaaaaa", "(0,10),(0,3)(3,6)(6,8)(8,10)");

    e |= test(f, "((a*)*)*", "", "(0,0),(0,0),(0,0)");

    e |= test(f, "(y?){0,2}", "", "(0,0),(0,0)");

    e |= test(f, "((y?){2,3}){2}", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)");

    e |= test(f, "((y?){2,3}){2,3}", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)");

    e |= test(f, "((y?){2,3})*", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)");

    e |= test(f, "((y?){3}){2}", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)(5,5)");

    e |= test(f, "((y?){3}){2,3}", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)(5,5)");

    e |= test(f, "((y?){3})*", "yyyyy", "(0,5),(0,3)(3,5),(0,1)(1,2)(2,3)(3,4)(4,5)(5,5)");

    e |= test(f, "((y?)*){2}", "yyyyy", "(0,5),(0,5)(5,5),(0,1)(1,2)(2,3)(3,4)(4,5)(5,5)");

    e |= test(f, "((y?)*){2,3}", "yyyyy", "(0,5),(0,5)(5,5),(0,1)(1,2)(2,3)(3,4)(4,5)(5,5)");

    e |= test(f, "((y?)*)*", "yyyyy", "(0,5),(0,5),(0,1)(1,2)(2,3)(3,4)(4,5)");

    e |= test(f, "((a)(b)?)*",   "aba", "(0,3),(0,2)(2,3),(0,1)(2,3),(1,2)(?,?)");
    e |= test(f, "((a)|(b))*",   "ba",  "(0,2),(0,1)(1,2),(?,?)(1,2),(0,1)(?,?)");
    e |= test(f, "((a)|(b))*",   "ab",  "(0,2),(0,1)(1,2),(0,1)(?,?),(?,?)(1,2)");
    e |= test(f, "((a?)|(b?))*", "ab",  "(0,2),(0,1)(1,2),(0,1)(?,?),(?,?)(1,2)");
    e |= test(f, "((a?)|(b?))*", "ba",  "(0,2),(0,1)(1,2),(?,?)(1,2),(0,1)(?,?)");
    e |= test(f, "((a?)|(b?)){2,3}", "ab", "(0,2),(0,1)(1,2),(0,1)(?,?),(?,?)(1,2)");
    e |= test(f, "((a?)|(b?)){3}",   "ab", "(0,2),(0,1)(1,2)(2,2),(0,1)(?,?)(2,2),(?,?)(1,2)(?,?)");

    e |= test(f, "y{3}",                     "yyy", "(0,3)");
    e |= test(f, "y{0,2}",                   "",    "(0,0)");
    e |= test(f, "y{0,2}",                   "y",   "(0,1)");
    e |= test(f, "y{0,2}",                   "yy",  "(0,2)");
    e |= test(f, "(y){3}",                   "yyy", "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(y){0,2}",                 "",    "(0,0),(?,?)");
    e |= test(f, "(y){0,2}",                 "y",   "(0,1),(0,1)");
    e |= test(f, "(y){0,2}",                 "yy",  "(0,2),(0,1)(1,2)");
    e |= test(f, "()",                       "",    "(0,0),(0,0)");
    e |= test(f, "(){2}",                    "",    "(0,0),(0,0)(0,0)");
    e |= test(f, "(){0,2}",                  "",    "(0,0),(0,0)");
    e |= test(f, "(((){0,30}){0,30}){0,30}", "",    "(0,0),(0,0),(0,0),(0,0)");
    e |= test(f, "(y?){2}",                  "y",   "(0,1),(0,1)(1,1)");

    e |= test(f, "a",        "a",    "(0,1)");
    e |= test(f, "(a)",      "a",    "(0,1),(0,1)");
    e |= test(f, "(a*)",     "aaa",  "(0,3),(0,3)");
    e |= test(f, "(a*)(b*)", "aabb", "(0,4),(0,2),(2,4)");
    e |= test(f, "(a*)(a*)", "aa",   "(0,2),(0,2),(2,2)");
    e |= test(f, "(a|aa)*",  "aa",   "(0,2),(0,2)");
    e |= test(f, "(a)|(a)",  "a",    "(0,1),(0,1),(?,?)");
    e |= test(f, "(a)*(a)*", "a",    "(0,1),(0,1),(?,?)");

    e |= test(f, "[a]",      "a",    "(0,1)");
    e |= test(f, "[a]",      "b");
    e |= test(f, "[^a]",     "a");
    e |= test(f, "[^a]",     "b",    "(0,1)");
    e |= test(f, "[ac]*",    "ac",   "(0,2)");
    e |= test(f, "[a-c]*",   "abc",  "(0,3)");
    e |= test(f, "[]]",      "]",    "(0,1)");
    e |= test(f, "[^]]",     "]");
    e |= test(f, "[^]]",     "a",    "(0,1)");
    e |= test(f, "[-]",      "-",    "(0,1)");
    e |= test(f, "[]-]*",    "]-",   "(0,2)");
    e |= test(f, "[-a]*",    "-a",   "(0,2)");
    e |= test(f, "[a-]*",    "-a",   "(0,2)");
    e |= test(f, "[-a-]*",   "-a",   "(0,2)");

    // basic
    e |= test(f, "(..)*(...)*",             "a",          "(0,0),(?,?),(?,?)");
    e |= test(f, "(..)*(...)*",             "abcd",       "(0,4),(0,2)(2,4),(?,?)");
    e |= test(f, "(ab|a)(bc|c)",            "abc",        "(0,3),(0,2),(2,3)");
    e |= test(f, "(ab)c|abc",               "abc",        "(0,3),(0,2)");
    e |= test(f, "(a*)(b?)(b+)b{3}",        "aaabbbbbbb", "(0,10),(0,3),(3,4),(4,7)");
    e |= test(f, "(a*)(b{0,1})(b{1,})b{3}", "aaabbbbbbb", "(0,10),(0,3),(3,4),(4,7)");
    e |= test(f, "((a|a)|a)",               "a",          "(0,1),(0,1),(0,1)");
    e |= test(f, "(a*)(a|aa)",              "aaaa",       "(0,4),(0,3),(3,4)");
    e |= test(f, "a*(a.|aa)",               "aaaa",       "(0,4),(2,4)");
    e |= test(f, "a(b)|c(d)|a(e)f",         "aef",        "(0,3),(?,?),(?,?),(1,2)");
    e |= test(f, "(a|b)?.*",                "b",          "(0,1),(0,1)");
    e |= test(f, "(a|b)c|a(b|c)",           "ac",         "(0,2),(0,1),(?,?)");
    e |= test(f, "(a|b)c|a(b|c)",           "ab",         "(0,2),(?,?),(1,2)");
    e |= test(f, "(a|b)*c|(a|ab)*c",        "abc",        "(0,3),(0,1)(1,2),(?,?)");
    e |= test(f, "(.a|.b).*|.*(.a|.b)",     "xa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "a?(ab|ba)ab",             "abab",       "(0,4),(0,2)");
    e |= test(f, "a?(a[c]{0}b|ba)ab",       "abab",       "(0,4),(0,2)");
    e |= test(f, "ab|abab",                 "abbabab",    "(0,2)");
    e |= test(f, "(aa|aaa)*|(a|aaaaa)",     "aa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "(a.|.a.)*|(a|.a...)",     "aa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "(a)(b)(c)",               "abc",        "(0,3),(0,1),(1,2),(2,3)");

    e |= test(f, "((((((x))))))", "x", "(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1)");

    e |= test(f, "((((((x))))))*", "xx",
              "(0,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2)");

    e |= test(f, "a?(ab|ba)*",
              "abababababababababababababababababababab"
              "ababababababababababababababababababababa",
              "(0,81),"
              "(1,3)(3,5)(5,7)(7,9)(9,11)(11,13)(13,15)(15,17)(17,19)(19,21)(21,23)"
              "(23,25)(25,27)(27,29)(29,31)(31,33)(33,35)(35,37)(37,39)(39,41)(41,43)"
              "(43,45)(45,47)(47,49)(49,51)(51,53)(53,55)(55,57)(57,59)(59,61)(61,63)"
              "(63,65)(65,67)(67,69)(69,71)(71,73)(73,75)(75,77)(77,79)(79,81)");

    e |= test(f, "a*a*a*a*a*b",   "aaaaaaaaab", "(0,10)");
    e |= test(f, "a[b]+bc",       "abbc",       "(0,4)");
    e |= test(f, "a[b]+bc",       "abbbbc",     "(0,6)");
    e |= test(f, "a[b]?bc",       "abbc",       "(0,4)");
    e |= test(f, "a[b]?bc",       "abc",        "(0,3)");
    e |= test(f, "a[b]?c",        "abc",        "(0,3)");
    e |= test(f, "ab|cd",         "abc",        "(0,2)");
    e |= test(f, "ab|cd",         "abcd",       "(0,2)");
    e |= test(f, "((a))",         "abc",        "(0,1),(0,1),(0,1)");
    e |= test(f, "(a)b(c)",       "abc",        "(0,3),(0,1),(2,3)");
    e |= test(f, "a*",            "aaa",        "(0,3)");
    e |= test(f, "(a*)*",         "-",          "(0,0),(0,0)");
    e |= test(f, "(a*)+",         "-",          "(0,0),(0,0)");
    e |= test(f, "(a*|b)*",       "-",          "(0,0),(0,0)");
    e |= test(f, "(a+|b)*",       "ab",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(a+|b)+",       "ab",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(a+|b)?",       "ab",         "(0,1),(0,1)");
    e |= test(f, "([abc])*d",     "abbbcd",     "(0,6),(0,1)(1,2)(2,3)(3,4)(4,5)");
    e |= test(f, "([abc])*bcd",   "abcd",       "(0,4),(0,1)");
    e |= test(f, "a|b|c|d|e",     "e",          "(0,1)");
    e |= test(f, "(a|b|c|d|e)f",  "ef",         "(0,2),(0,1)");
    e |= test(f, "((a*|b))*",     "-",          "(0,0),(0,0),(0,0)");
    e |= test(f, "(ab|a[b]*)bc",  "abc",        "(0,3),(0,1)");
    e |= test(f, "a([bc]*)c*",    "abc",        "(0,3),(1,3)");
    e |= test(f, "a([bc]*)(c*d)", "abcd",       "(0,4),(1,3),(3,4)");
    e |= test(f, "a([bc]+)(c*d)", "abcd",       "(0,4),(1,3),(3,4)");
    e |= test(f, "a([bc]*)(c+d)", "abcd",       "(0,4),(1,2),(2,4)");
    e |= test(f, "a[bcd]*dcdcde", "adcdcde",    "(0,7)");
    e |= test(f, "(ab|a)b*c",     "abc",        "(0,3),(0,2)");
    e |= test(f, "((a)(b)c)(d)",  "abcd",       "(0,4),(0,3),(0,1),(1,2),(3,4)");
    e |= test(f, "(.*)c(.*)",     "abcde",      "(0,5),(0,2),(3,5)");
    e |= test(f, "a(bc)d",        "abcd",       "(0,4),(1,3)");
    e |= test(f, "a+(b|c)*d+",    "aabcdd",     "(0,6),(2,3)(3,4)");

    // categorize
    e |= test(f, "(a*)(ab)*(b*)", "abc", "(0,2),(0,1),(?,?),(1,2)");

    e |= test(f, "((a*)(ab)*)((b*)(a*))", "aba", "(0,3),(0,2),(0,0),(0,2),(2,3),(2,2),(2,3)");

    e |= test(f, "(...?.?)*",          "xxxxxx", "(0,6),(0,4)(4,6)");
    e |= test(f, "(a|ab)(bc|c)",       "abcabc", "(0,3),(0,2),(2,3)");
    e |= test(f, "(aba|a*b)(aba|a*b)", "ababa",  "(0,5),(0,2),(2,5)");
    e |= test(f, "(a*){2}",            "xxxxx",  "(0,0),(0,0)(0,0)");
    e |= test(f, "(a*)*",              "a",      "(0,1),(0,1)");
    e |= test(f, "(aba|a*b)*",         "ababa",  "(0,5),(0,2)(2,5)");
    e |= test(f, "(a(b)?)+",           "aba",    "(0,3),(0,2)(2,3),(1,2)(?,?)");
    e |= test(f, ".*(.*)",             "ab",     "(0,2),(2,2)");

    e |= test(f, "(a?)((ab)?)(b?)a?(ab)?b?", "abab", "(0,4),(0,1),(1,1),(?,?),(1,2),(?,?)");

    // forcedassoc
    e |= test(f, "(a|ab)(c|bcd)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(a|ab)(bcd|c)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(ab|a)(c|bcd)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(ab|a)(bcd|c)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "((a|ab)(c|bcd))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "((a|ab)(bcd|c))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "((ab|a)(c|bcd))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "((ab|a)(bcd|c))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "(a|ab)((c|bcd)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(a|ab)((bcd|c)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(ab|a)((c|bcd)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(ab|a)((bcd|c)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(a*)(b|abc)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "(a*)(abc|b)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "((a*)(b|abc))(c*)",   "abc",  "(0,3),(0,3),(0,0),(0,3),(3,3)");
    e |= test(f, "((a*)(abc|b))(c*)",   "abc",  "(0,3),(0,3),(0,0),(0,3),(3,3)");
    e |= test(f, "(a*)((b|abc)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)((abc|b)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)(b|abc)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "(a*)(abc|b)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "((a*)(b|abc))(c*)",   "abc",  "(0,3),(0,3),(0,0),(0,3),(3,3)");
    e |= test(f, "((a*)(abc|b))(c*)",   "abc",  "(0,3),(0,3),(0,0),(0,3),(3,3)");
    e |= test(f, "(a*)((b|abc)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)((abc|b)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a|ab)",              "ab",   "(0,2),(0,2)");
    e |= test(f, "(ab|a)",              "ab",   "(0,2),(0,2)");
    e |= test(f, "(a|ab)(b*)",          "ab",   "(0,2),(0,2),(2,2)");
    e |= test(f, "(ab|a)(b*)",          "ab",   "(0,2),(0,2),(2,2)");

    // glennfowler
    e |= test(f, "(a?)((ab)?)",        "ab",   "(0,2),(0,0),(0,2),(0,2)");
    e |= test(f, "(a?)((ab)?)(b?)",    "ab",   "(0,2),(0,1),(1,1),(?,?),(1,2)");
    e |= test(f, "((a?)((ab)?))(b?)",  "ab",   "(0,2),(0,2),(0,0),(0,2),(0,2),(2,2)");
    e |= test(f, "(a?)(((ab)?)(b?))",  "ab",   "(0,2),(0,1),(1,2),(1,1),(?,?),(1,2)");
    e |= test(f, "(.?)",               "x",    "(0,1),(0,1)");
    e |= test(f, "(.?){1}",            "x",    "(0,1),(0,1)");
    e |= test(f, "(.?)(.?)",           "x",    "(0,1),(0,1),(1,1)");
    e |= test(f, "(.?){2}",            "x",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(.?)*",              "x",    "(0,1),(0,1)");
    e |= test(f, "(.?.?)",             "xxx",  "(0,2),(0,2)");
    e |= test(f, "(.?.?){1}",          "xxx",  "(0,2),(0,2)");
    e |= test(f, "(.?.?)(.?.?)",       "xxx",  "(0,3),(0,2),(2,3)");
    e |= test(f, "(.?.?){2}",          "xxx",  "(0,3),(0,2)(2,3)");
    e |= test(f, "(.?.?)(.?.?)(.?.?)", "xxx",  "(0,3),(0,2),(2,3),(3,3)");
    e |= test(f, "(.?.?){3}",          "xxx",  "(0,3),(0,2)(2,3)(3,3)");
    e |= test(f, "(.?.?)*",            "xx",   "(0,2),(0,2)");
    e |= test(f, "(.?.?)*",            "xxx",  "(0,3),(0,2)(2,3)");
    e |= test(f, "(.?.?)*",            "xxxx", "(0,4),(0,2)(2,4)");

    e |= test(f, "a?((ab)?)(b?)",                  "ab",    "(0,2),(1,1),(?,?),(1,2)");
    e |= test(f, "(a?)((ab)?)b?",                  "ab",    "(0,2),(0,1),(1,1),(?,?)");
    e |= test(f, "a?((ab)?)b?",                    "ab",    "(0,2),(1,1),(?,?)");
    e |= test(f, "(ab?)(b?a)",                     "aba",   "(0,3),(0,2),(2,3)");
    e |= test(f, "(a|ab)(ba|a)",                   "aba",   "(0,3),(0,2),(2,3)");
    e |= test(f, "(a|ab|ba)",                      "aba",   "(0,2),(0,2)");
    e |= test(f, "(a|ab|ba)(a|ab|ba)",             "aba",   "(0,3),(0,2),(2,3)");
    e |= test(f, "(a|ab|ba)*",                     "aba",   "(0,3),(0,2)(2,3)");
    e |= test(f, "(aba|a*b)",                      "ababa", "(0,3),(0,3)");
    e |= test(f, "(aba|a*b)(aba|a*b)",             "ababa", "(0,5),(0,2),(2,5)");
    e |= test(f, "(aba|a*b)(aba|a*b)(aba|a*b)",    "ababa");
    e |= test(f, "(aba|ab|a)",                     "ababa", "(0,3),(0,3)");
    e |= test(f, "(aba|ab|a)(aba|ab|a)",           "ababa", "(0,5),(0,2),(2,5)");
    e |= test(f, "(aba|ab|a)(aba|ab|a)(aba|ab|a)", "ababa", "(0,5),(0,2),(2,4),(4,5)");
    e |= test(f, "(aba|ab|a)*",                    "ababa", "(0,5),(0,2)(2,5)");

    e |= test(f, "(a(b)?)",        "aba", "(0,2),(0,2),(1,2)");
    e |= test(f, "(a(b)?)(a(b)?)", "aba", "(0,3),(0,2),(1,2),(2,3),(?,?)");

    e |= test(f, "(.*)(.*)", "xx", "(0,2),(0,2),(2,2)");
    e |= test(f, ".*(.*)",   "xx", "(0,2),(2,2)");

    e |= test(f, "(a.*z|b.*y)",            "azbazby", "(0,5),(0,5)");
    e |= test(f, "(a.*z|b.*y)(a.*z|b.*y)", "azbazby", "(0,7),(0,5),(5,7)");
    e |= test(f, "(a.*z|b.*y)*",           "azbazby", "(0,7),(0,5)(5,7)");

    e |= test(f, "(.|..)(.*)", "ab","(0,2),(0,2),(2,2)");

    e |= test(f, "((..)*(...)*)",              "xxx", "(0,3),(0,3),(?,?),(0,3)");
    e |= test(f, "((..)*(...)*)*",             "xxx", "(0,3),(0,3),(?,?),(0,3)");
    e |= test(f, "((..)*(...)*)((..)*(...)*)", "xxx", "(0,3),(0,3),(?,?),(0,3),(3,3),(?,?),(?,?)");

    // nullsubexpr
    e |= test(f, "(a*)*",      "a",        "(0,1),(0,1)");
    e |= test(f, "(a*)*",      "x",        "(0,0),(0,0)");
    e |= test(f, "(a*)*",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a*)*",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a*)+",      "a",        "(0,1),(0,1)");
    e |= test(f, "(a*)+",      "x",        "(0,0),(0,0)");
    e |= test(f, "(a*)+",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a*)+",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a+)*",      "a",        "(0,1),(0,1)");
    e |= test(f, "(a+)*",      "x",        "(0,0),(?,?)");
    e |= test(f, "(a+)*",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a+)*",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a+)+",      "a",        "(0,1),(0,1)");
    e |= test(f, "(a+)+",      "x");
    e |= test(f, "(a+)+",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a+)+",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([a]*)*",    "a",        "(0,1),(0,1)");
    e |= test(f, "([a]*)*",    "x",        "(0,0),(0,0)");
    e |= test(f, "([a]*)*",    "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([a]*)*",    "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([a]*)+",    "a",        "(0,1),(0,1)");
    e |= test(f, "([a]*)+",    "x",        "(0,0),(0,0)");
    e |= test(f, "([a]*)+",    "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([a]*)+",    "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([^b]*)*",   "a",        "(0,1),(0,1)");
    e |= test(f, "([^b]*)*",   "b",        "(0,0),(0,0)");
    e |= test(f, "([^b]*)*",   "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([^b]*)*",   "aaaaaab",  "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",   "a",        "(0,1),(0,1)");
    e |= test(f, "([ab]*)*",   "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",   "ababab",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",   "bababa",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",   "b",        "(0,1),(0,1)");
    e |= test(f, "([ab]*)*",   "bbbbbb",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",   "aaaabcde", "(0,5),(0,5)");
    e |= test(f, "([^a]*)*",   "b",        "(0,1),(0,1)");
    e |= test(f, "([^a]*)*",   "bbbbbb",   "(0,6),(0,6)");
    e |= test(f, "([^a]*)*",   "aaaaaa",   "(0,0),(0,0)");
    e |= test(f, "([^ab]*)*",  "ccccxx",   "(0,6),(0,6)");
    e |= test(f, "([^ab]*)*",  "ababab",   "(0,0),(0,0)");
    e |= test(f, "((z)+|a)*",  "zabcde",   "(0,2),(0,1)(1,2),(0,1)(?,?)");
    e |= test(f, "(a)",        "aaa",      "(0,1),(0,1)");
    e |= test(f, "(a*)*(x)",   "x",        "(0,1),(0,0),(0,1)");
    e |= test(f, "(a*)*(x)",   "ax",       "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)*(x)",   "axa",      "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)+(x)",   "x",        "(0,1),(0,0),(0,1)");
    e |= test(f, "(a*)+(x)",   "ax",       "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)+(x)",   "axa",      "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*){2}(x)", "x",        "(0,1),(0,0)(0,0),(0,1)");
    e |= test(f, "(a*){2}(x)", "ax",       "(0,2),(0,1)(1,1),(1,2)");
    e |= test(f, "(a*){2}(x)", "axa",      "(0,2),(0,1)(1,1),(1,2)");

    e |= test(f, "(()|.)(b)",     "ab",    "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "(()|[ab])(b)",  "ab",    "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "(()|[ab])+b",   "aaab",  "(0,4),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?)");
    e |= test(f, "(.|())(b)",     "ab",    "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "([ab]|())(b)",  "ab",    "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "([ab]|())+b",   "aaab",  "(0,4),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?)");
    e |= test(f, "(.?)(b)",       "ab",    "(0,2),(0,1),(1,2)");

    // other
    e |= test(f, "a|(a)",      "a",  "(0,1),(?,?)");
    e |= test(f, "(a)|a",      "a",  "(0,1),(0,1)");
    e |= test(f, "(b)a|b(a)",  "ba", "(0,2),(0,1),(?,?)");
    e |= test(f, "b(a)|(b)a",  "ba", "(0,2),(1,2),(?,?)");
    e |= test(f, "a*|(a|aa)*", "aa", "(0,2),(?,?)");
    e |= test(f, "(a|aa)*|a*", "aa", "(0,2),(0,2)");

    e |= test(f, "(aa*|aaa*)*",                  "aaaaaa", "(0,6),(0,6)");
    e |= test(f, "(aa*|aaa*)(aa*|aaa*)",         "aaaaaa", "(0,6),(0,5),(5,6)");
    e |= test(f, "(aa*|aaa*){2}",                "aaaaaa", "(0,6),(0,5)(5,6)");
    e |= test(f, "((aa)*|(aaa)*)((aa)*|(aaa)*)", "aaaaaa",
              "(0,6),(0,6),(0,2)(2,4)(4,6),(?,?),(6,6),(?,?),(?,?)");
    e |= test(f, "((aa)*|(aaa)*){2}",            "aaaaaa",
              "(0,6),(0,6)(6,6),(0,2)(2,4)(4,6)(?,?),(?,?)(?,?)");
    e |= test(f, "(aa)*|(aaa)*",                 "aaaaaa", "(0,6),(0,2)(2,4)(4,6),(?,?)");

    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XY",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XabY",       "(0,4),(0,3)(3,4)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XababY",     "(0,6),(0,4)(4,6)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XabababY",   "(0,8),(0,3)(3,7)(7,8)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XababababY", "(0,10),(0,4)(4,8)(8,10)");

    e |= test(f, "(y){2}",         "");
    e |= test(f, "(y){2}",         "y");
    e |= test(f, "(y){2}",         "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){0,2}",       "",     "(0,0),(?,?)");
    e |= test(f, "(y){0,2}",       "y",    "(0,1),(0,1)");
    e |= test(f, "(y){0,2}",       "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}",       "");
    e |= test(f, "(y){1,2}",       "y",    "(0,1),(0,1)");
    e |= test(f, "(y){1,2}",       "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y",      "");
    e |= test(f, "(y){1,2}y",      "y");
    e |= test(f, "(y){1,2}y",      "yy",   "(0,2),(0,1)");
    e |= test(f, "(y){1,2}y",      "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y?",     "");
    e |= test(f, "(y){1,2}y?",     "y",    "(0,1),(0,1)");
    e |= test(f, "(y){1,2}y?",     "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y?",     "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y){2,}",        "");
    e |= test(f, "(y){2,}",        "y");
    e |= test(f, "(y){2,}",        "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){2,}",        "yyy",  "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(y?){2}",        "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){2}",        "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){2}",        "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){0,2}",      "",     "(0,0),(0,0)");
    e |= test(f, "(y?){0,2}|(y?)", "y",    "(0,1),(0,1),(?,?)");
    e |= test(f, "(y?){0,2}",      "y",    "(0,1),(0,1)");
    e |= test(f, "(y?){0,2}",      "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}",      "",     "(0,0),(0,0)");
    e |= test(f, "(y?){1,2}",      "y",    "(0,1),(0,1)");
    e |= test(f, "(y?){1,2}",      "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y",     "");
    e |= test(f, "(y?){1,2}y",     "y",    "(0,1),(0,0)");
    e |= test(f, "(y?){1,2}y",     "yy",   "(0,2),(0,1)");
    e |= test(f, "(y?){1,2}y",     "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y?",    "",     "(0,0),(0,0)");
    e |= test(f, "(y?){1,2}y?",    "y",    "(0,1),(0,1)");
    e |= test(f, "(y?){1,2}y?",    "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y?",    "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y?){2,}",       "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){2,}",       "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){2,}",       "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){2,}",       "yyy",  "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(y|(x?)){1,3}",  "y",    "(0,1),(0,1),(?,?)");
    e |= test(f, "(y[y]?){3}",     "yyyy", "(0,4),(0,2)(2,3)(3,4)");

    // repetition
    e |= test(f, "((..)|(.))",                     "");
    e |= test(f, "((..)|(.))((..)|(.))",           "");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "");
    e |= test(f, "((..)|(.)){1}",                  "");
    e |= test(f, "((..)|(.)){2}",                  "");
    e |= test(f, "((..)|(.)){3}",                  "");
    e |= test(f, "((..)|(.))*",                    "", "(0,0),(?,?),(?,?),(?,?)");

    e |= test(f, "((..)|(.))",                     "a", "(0,1),(0,1),(?,?),(0,1)");
    e |= test(f, "((..)|(.))((..)|(.))",           "a");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "a");
    e |= test(f, "((..)|(.)){1}",                  "a", "(0,1),(0,1),(?,?),(0,1)");
    e |= test(f, "((..)|(.)){2}",                  "a");
    e |= test(f, "((..)|(.)){3}",                  "a");
    e |= test(f, "((..)|(.))*",                    "a", "(0,1),(0,1),(?,?),(0,1)");

    e |= test(f, "((..)|(.))",                     "aa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aa",
              "(0,2),(0,1),(?,?),(0,1),(1,2),(?,?),(1,2)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aa");
    e |= test(f, "((..)|(.)){1}",                  "aa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aa", "(0,2),(0,1)(1,2),(?,?)(?,?),(0,1)(1,2)");
    e |= test(f, "((..)|(.)){3}",                  "aa");
    e |= test(f, "((..)|(.))*",                    "aa", "(0,2),(0,2),(0,2),(?,?)");

    e |= test(f, "((..)|(.))",                     "aaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaa",
              "(0,3),(0,2),(0,2),(?,?),(2,3),(?,?),(2,3)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaa",
              "(0,3),(0,1),(?,?),(0,1),(1,2),(?,?),(1,2),(2,3),(?,?),(2,3)");
    e |= test(f, "((..)|(.)){1}",                  "aaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaa", "(0,3),(0,2)(2,3),(0,2)(?,?),(?,?)(2,3)");
    e |= test(f, "((..)|(.)){3}",                  "aaa",
              "(0,3),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?),(0,1)(1,2)(2,3)");
    e |= test(f, "((..)|(.))*",                    "aaa", "(0,3),(0,2)(2,3),(0,2)(?,?),(?,?)(2,3)");

    e |= test(f, "((..)|(.))",                     "aaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,3),(?,?),(2,3),(3,4),(?,?),(3,4)");
    e |= test(f, "((..)|(.)){1}",                  "aaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaa",
              "(0,4),(0,2)(2,3)(3,4),(0,2)(?,?)(?,?),(?,?)(2,3)(3,4)");
    e |= test(f, "((..)|(.))*",                    "aaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");

    e |= test(f, "((..)|(.))",                     "aaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaaa",
              "(0,5),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?),(4,5),(?,?),(4,5)");
    e |= test(f, "((..)|(.)){1}",                  "aaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaaa",
              "(0,5),(0,2)(2,4)(4,5),(0,2)(2,4)(?,?),(?,?)(?,?)(4,5)");
    e |= test(f, "((..)|(.))*",                    "aaaaa",
              "(0,5),(0,2)(2,4)(4,5),(0,2)(2,4)(?,?),(?,?)(?,?)(4,5)");

    e |= test(f, "((..)|(.))",                     "aaaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaaaa",
              "(0,6),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?),(4,6),(4,6),(?,?)");
    e |= test(f, "((..)|(.)){1}",                  "aaaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaaaa",
              "(0,6),(0,2)(2,4)(4,6),(0,2)(2,4)(4,6),(?,?)(?,?)(?,?)");
    e |= test(f, "((..)|(.))*",                    "aaaaaa",
              "(0,6),(0,2)(2,4)(4,6),(0,2)(2,4)(4,6),(?,?)(?,?)(?,?)");

    e |= test(f, "X(.?){0,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){1,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){2,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){3,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){4,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){5,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){6,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){7,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){8,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){0,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){1,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){2,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){3,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){4,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){5,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){6,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){7,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){8,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");

    e |= test(f, "(a|ab|c|bcd){0,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){1,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){2,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){3,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){4,}(d*)",   "ababcd");
    e |= test(f, "(a|ab|c|bcd){0,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){1,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){2,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){3,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){4,10}(d*)", "ababcd");
    e |= test(f, "(a|ab|c|bcd)*(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd)+(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){0,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){1,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){2,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){3,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){4,}(d*)",   "ababcd");
    e |= test(f, "(ab|a|c|bcd){0,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){1,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){2,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){3,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){4,10}(d*)", "ababcd");
    e |= test(f, "(ab|a|c|bcd)*(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd)+(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");

    // rightassoc
    e |= test(f, "(a|ab)(c|bcd)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(a|ab)(bcd|c)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(c|bcd)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(bcd|c)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(a*)(b|abc)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(abc|b)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(b|abc)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(abc|b)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a|ab)(c|bcd)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(a|ab)(bcd|c)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(c|bcd)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(bcd|c)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");

    if (!(f & REG_NFA)) {
        e |= test(f, "((a?){1,300})*",
                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                  "(0,50),(0,50),"
                  "(0,1)(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,9)(9,10)"
                  "(10,11)(11,12)(12,13)(13,14)(14,15)(15,16)(16,17)(17,18)(18,19)(19,20)"
                  "(20,21)(21,22)(22,23)(23,24)(24,25)(25,26)(26,27)(27,28)(28,29)(29,30)"
                  "(30,31)(31,32)(32,33)(33,34)(34,35)(35,36)(36,37)(37,38)(38,39)(39,40)"
                  "(40,41)(41,42)(42,43)(43,44)(44,45)(45,46)(46,47)(47,48)(48,49)(49,50)");
    } else if (!(f & REG_SLOWPREC)) {
        e |= test(f, "((a?){1,1000})*", "aaaa", "(0,4),(0,4),(3,4)");

        e |= test(f, "(((((aa)|((a?)*))*){0,10}){0,10}){0,10}", "",
                  "(0,0),(0,0),(0,0),(0,0),(0,0),(?,?),(0,0),(0,0)");

        e |= test(f, "(((((aa)|((a?)*))*){0,10}){0,10}){0,10}", "aaa",
                  "(0,3),(0,3),(0,3),(0,3),(0,3),(?,?),(0,3),(2,3)");

        e |= test(f, "(((((aa)|((a?)*))*){0,10}){0,10}){0,10}", "aaaaa",
                  "(0,5),(0,5),(0,5),(0,5),(0,5),(?,?),(0,5),(4,5)");
    }

    e |= test(f, "((((a?)+)|(aa))+)", "aaa",
              "(0,3),(0,3),(0,3),(0,3),(0,1)(1,2)(2,3),(?,?)");

    e |= test(f, "(((aa)|((a?)+))+)", "aaa",
              "(0,3),(0,3),(0,3),(?,?),(0,3),(0,1)(1,2)(2,3)");

    e |= test(f, "((a?){1,2}|(a)*)*", "aaaa", "(0,4),(0,4),(?,?),(0,1)(1,2)(2,3)(3,4)");

    e |= test(f, "(((a?){2,3}|(a)*))*", "aaaaa",
              "(0,5),(0,5),(0,5),(?,?),(0,1)(1,2)(2,3)(3,4)(4,5)");

    e |= test(f, "(((a?)|(a?a?))+)", "aa", "(0,2),(0,2),(0,2),(?,?),(0,2)");

    e |= test(f, "((((a)*))*|((((a))*))+)*", "aa",
              "(0,2),(0,2),(0,2),(0,2),(0,1)(1,2),(?,?),(?,?),(?,?),(?,?)");

    e |= test(f, "(((a)*)*|((a)*)+)*", "aa", "(0,2),(0,2),(0,2),(0,1)(1,2),(?,?),(?,?)");

    e |= test(f, "(((a)+)|(((a)+)?))+", "aa",
              "(0,2),(0,2),(0,2),(0,1)(1,2),(?,?),(?,?),(?,?)");

    e |= test(f, "(a*|(a)*)*", "aa", "(0,2),(0,2),(?,?)");

    return e;
}

static int test_all_leftmost(int f) {
    int e = 0;

    e |= test(f, "a",        "a",    "(0,1)");
    e |= test(f, "(a)",      "a",    "(0,1),(0,1)");
    e |= test(f, "(a*)",     "aaa",  "(0,3),(0,3)");
    e |= test(f, "(a*)(b*)", "aabb", "(0,4),(0,2),(2,4)");
    e |= test(f, "(a*)(a*)", "aa",   "(0,2),(0,2),(2,2)");
    e |= test(f, "(a|aa)*",  "aa",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(a)|(a)",  "a",    "(0,1),(0,1),(?,?)");
    e |= test(f, "(a)*(a)*", "a",    "(0,1),(0,1),(?,?)");

    e |= test(f, "[a]",      "a",    "(0,1)");
    e |= test(f, "[a]",      "b");
    e |= test(f, "[^a]",     "a");
    e |= test(f, "[^a]",     "b",    "(0,1)");
    e |= test(f, "[ac]*",    "ac",   "(0,2)");
    e |= test(f, "[a-c]*",   "abc",  "(0,3)");
    e |= test(f, "[]]",      "]",    "(0,1)");
    e |= test(f, "[^]]",     "]");
    e |= test(f, "[^]]",     "a",    "(0,1)");
    e |= test(f, "[-]",      "-",    "(0,1)");
    e |= test(f, "[]-]*",    "]-",   "(0,2)");
    e |= test(f, "[-a]*",    "-a",   "(0,2)");
    e |= test(f, "[a-]*",    "-a",   "(0,2)");
    e |= test(f, "[-a-]*",   "-a",   "(0,2)");

    // basic
    e |= test(f, "(..)*(...)*",             "a",          "(0,0),(?,?),(?,?)");
    e |= test(f, "(..)*(...)*",             "abcd",       "(0,4),(0,2)(2,4),(?,?)");
    e |= test(f, "(ab|a)(bc|c)",            "abc",        "(0,3),(0,2),(2,3)");
    e |= test(f, "(ab)c|abc",               "abc",        "(0,3),(0,2)");
    e |= test(f, "(a*)(b?)(b+)b{3}",        "aaabbbbbbb", "(0,10),(0,3),(3,4),(4,7)");
    e |= test(f, "(a*)(b{0,1})(b{1,})b{3}", "aaabbbbbbb", "(0,10),(0,3),(3,4),(4,7)");
    e |= test(f, "((a|a)|a)",               "a",          "(0,1),(0,1),(0,1)");
    e |= test(f, "(a*)(a|aa)",              "aaaa",       "(0,4),(0,3),(3,4)");
    e |= test(f, "a*(a.|aa)",               "aaaa",       "(0,4),(2,4)");
    e |= test(f, "a(b)|c(d)|a(e)f",         "aef",        "(0,3),(?,?),(?,?),(1,2)");
    e |= test(f, "(a|b)?.*",                "b",          "(0,1),(0,1)");
    e |= test(f, "(a|b)c|a(b|c)",           "ac",         "(0,2),(0,1),(?,?)");
    e |= test(f, "(a|b)c|a(b|c)",           "ab",         "(0,2),(?,?),(1,2)");
    e |= test(f, "(a|b)*c|(a|ab)*c",        "abc",        "(0,3),(0,1)(1,2),(?,?)");
    e |= test(f, "(.a|.b).*|.*(.a|.b)",     "xa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "a?(ab|ba)ab",             "abab",       "(0,4),(0,2)");
    e |= test(f, "a?(a[c]{0}b|ba)ab",       "abab",       "(0,4),(0,2)");
    e |= test(f, "ab|abab",                 "abbabab",    "(0,2)");
    e |= test(f, "(aa|aaa)*|(a|aaaaa)",     "aa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "(a.|.a.)*|(a|.a...)",     "aa",         "(0,2),(0,2),(?,?)");
    e |= test(f, "(a)(b)(c)",               "abc",        "(0,3),(0,1),(1,2),(2,3)");

    e |= test(f, "((((((x))))))", "x", "(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1)");
    e |= test(f, "((((((x))))))*", "xx",
              "(0,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2),(0,1)(1,2)");

    e |= test(f, "a?(ab|ba)*",
              "abababababababababababababababababababab"
              "ababababababababababababababababababababa",
              "(0,81),"
              "(1,3)(3,5)(5,7)(7,9)(9,11)(11,13)(13,15)(15,17)(17,19)(19,21)(21,23)"
              "(23,25)(25,27)(27,29)(29,31)(31,33)(33,35)(35,37)(37,39)(39,41)(41,43)"
              "(43,45)(45,47)(47,49)(49,51)(51,53)(53,55)(55,57)(57,59)(59,61)(61,63)"
              "(63,65)(65,67)(67,69)(69,71)(71,73)(73,75)(75,77)(77,79)(79,81)");

    e |= test(f, "a*a*a*a*a*b",   "aaaaaaaaab", "(0,10)");
    e |= test(f, "a[b]+bc",       "abbc",       "(0,4)");
    e |= test(f, "a[b]+bc",       "abbbbc",     "(0,6)");
    e |= test(f, "a[b]?bc",       "abbc",       "(0,4)");
    e |= test(f, "a[b]?bc",       "abc",        "(0,3)");
    e |= test(f, "a[b]?c",        "abc",        "(0,3)");
    e |= test(f, "ab|cd",         "abc",        "(0,2)");
    e |= test(f, "ab|cd",         "abcd",       "(0,2)");
    e |= test(f, "((a))",         "abc",        "(0,1),(0,1),(0,1)");
    e |= test(f, "(a)b(c)",       "abc",        "(0,3),(0,1),(2,3)");
    e |= test(f, "a*",            "aaa",        "(0,3)");
    e |= test(f, "(a*)*",         "-",          "(0,0),(0,0)");
    e |= test(f, "(a*)+",         "-",          "(0,0),(0,0)");
    e |= test(f, "(a*|b)*",       "-",          "(0,0),(0,0)");
    e |= test(f, "(a+|b)*",       "ab",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(a+|b)+",       "ab",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(a+|b)?",       "ab",         "(0,1),(0,1)");
    e |= test(f, "([abc])*d",     "abbbcd",     "(0,6),(0,1)(1,2)(2,3)(3,4)(4,5)");
    e |= test(f, "([abc])*bcd",   "abcd",       "(0,4),(0,1)");
    e |= test(f, "a|b|c|d|e",     "e",          "(0,1)");
    e |= test(f, "(a|b|c|d|e)f",  "ef",         "(0,2),(0,1)");
    e |= test(f, "((a*|b))*",     "-",          "(0,0),(0,0),(0,0)");
    e |= test(f, "(ab|a[b]*)bc",  "abc",        "(0,3),(0,1)");
    e |= test(f, "a([bc]*)c*",    "abc",        "(0,3),(1,3)");
    e |= test(f, "a([bc]*)(c*d)", "abcd",       "(0,4),(1,3),(3,4)");
    e |= test(f, "a([bc]+)(c*d)", "abcd",       "(0,4),(1,3),(3,4)");
    e |= test(f, "a([bc]*)(c+d)", "abcd",       "(0,4),(1,2),(2,4)");
    e |= test(f, "a[bcd]*dcdcde", "adcdcde",    "(0,7)");
    e |= test(f, "(ab|a)b*c",     "abc",        "(0,3),(0,2)");
    e |= test(f, "((a)(b)c)(d)",  "abcd",       "(0,4),(0,3),(0,1),(1,2),(3,4)");
    e |= test(f, "(.*)c(.*)",     "abcde",      "(0,5),(0,2),(3,5)");
    e |= test(f, "a(bc)d",        "abcd",       "(0,4),(1,3)");
    e |= test(f, "a+(b|c)*d+",    "aabcdd",     "(0,6),(2,3)(3,4)");

    // categorize
    e |= test(f, "(a*)(ab)*(b*)", "abc", "(0,2),(0,1),(?,?),(1,2)");

    e |= test(f, "((a*)(ab)*)((b*)(a*))", "aba", "(0,3),(0,1),(0,1),(?,?),(1,3),(1,2),(2,3)");

    e |= test(f, "(...?.?)*",          "xxxxxx", "(0,6),(0,4)(4,6)");
    e |= test(f, "(a|ab)(bc|c)",       "abcabc", "(0,3),(0,1),(1,3)");
    e |= test(f, "(aba|a*b)(aba|a*b)", "ababa",  "(0,5),(0,2),(2,5)");
    e |= test(f, "(a*){2}",            "xxxxx",  "(0,0),(0,0)(0,0)");
    e |= test(f, "(a*)*",              "a",      "(0,1),(0,1)");
    e |= test(f, "(aba|a*b)*",         "ababa",  "(0,5),(0,2)(2,5)");
    e |= test(f, "(a(b)?)+",           "aba",    "(0,3),(0,2)(2,3),(1,2)(?,?)");
    e |= test(f, ".*(.*)",             "ab",     "(0,2),(2,2)");

    e |= test(f, "(a?)((ab)?)(b?)a?(ab)?b?", "abab", "(0,4),(0,1),(1,1),(?,?),(1,2),(?,?)");

    // forcedassoc
    e |= test(f, "(a|ab)(c|bcd)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(a|ab)(bcd|c)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(ab|a)(c|bcd)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "(ab|a)(bcd|c)",       "abcd", "(0,4),(0,1),(1,4)");
    e |= test(f, "((a|ab)(c|bcd))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "((a|ab)(bcd|c))(d*)", "abcd", "(0,4),(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "((ab|a)(c|bcd))(d*)", "abcd", "(0,4),(0,3),(0,2),(2,3),(3,4)");
    e |= test(f, "((ab|a)(bcd|c))(d*)", "abcd", "(0,4),(0,3),(0,2),(2,3),(3,4)");
    e |= test(f, "(a|ab)((c|bcd)(d*))", "abcd", "(0,4),(0,1),(1,4),(1,4),(4,4)");
    e |= test(f, "(a|ab)((bcd|c)(d*))", "abcd", "(0,4),(0,1),(1,4),(1,4),(4,4)");
    e |= test(f, "(ab|a)((c|bcd)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(ab|a)((bcd|c)(d*))", "abcd", "(0,4),(0,2),(2,4),(2,3),(3,4)");
    e |= test(f, "(a*)(b|abc)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "(a*)(abc|b)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "((a*)(b|abc))(c*)",   "abc",  "(0,3),(0,2),(0,1),(1,2),(2,3)");
    e |= test(f, "((a*)(abc|b))(c*)",   "abc",  "(0,3),(0,2),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)((b|abc)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)((abc|b)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)(b|abc)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "(a*)(abc|b)",         "abc",  "(0,3),(0,0),(0,3)");
    e |= test(f, "((a*)(b|abc))(c*)",   "abc",  "(0,3),(0,2),(0,1),(1,2),(2,3)");
    e |= test(f, "((a*)(abc|b))(c*)",   "abc",  "(0,3),(0,2),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)((b|abc)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a*)((abc|b)(c*))",   "abc",  "(0,3),(0,1),(1,3),(1,2),(2,3)");
    e |= test(f, "(a|ab)",              "ab",   "(0,2),(0,2)");
    e |= test(f, "(ab|a)",              "ab",   "(0,2),(0,2)");
    e |= test(f, "(a|ab)(b*)",          "ab",   "(0,2),(0,1),(1,2)");
    e |= test(f, "(ab|a)(b*)",          "ab",   "(0,2),(0,2),(2,2)");

    // glennfowler
    e |= test(f, "(a?)((ab)?)",       "ab", "(0,2),(0,0),(0,2),(0,2)");
    e |= test(f, "(a?)((ab)?)(b?)",   "ab", "(0,2),(0,1),(1,1),(?,?),(1,2)");
    e |= test(f, "((a?)((ab)?))(b?)", "ab", "(0,2),(0,1),(0,1),(1,1),(?,?),(1,2)");
    e |= test(f, "(a?)(((ab)?)(b?))", "ab", "(0,2),(0,1),(1,2),(1,1),(?,?),(1,2)");

    e |= test(f, "(.?)",               "x",   "(0,1),(0,1)");
    e |= test(f, "(.?){1}",            "x",   "(0,1),(0,1)");
    e |= test(f, "(.?)(.?)",           "x",   "(0,1),(0,1),(1,1)");
    e |= test(f, "(.?){2}",            "x",   "(0,1),(0,1)(1,1)");
    e |= test(f, "(.?)*",              "x",   "(0,1),(0,1)");
    e |= test(f, "(.?.?)",             "xxx", "(0,2),(0,2)");
    e |= test(f, "(.?.?){1}",          "xxx", "(0,2),(0,2)");
    e |= test(f, "(.?.?)(.?.?)",       "xxx", "(0,3),(0,2),(2,3)");
    e |= test(f, "(.?.?){2}",          "xxx", "(0,3),(0,2)(2,3)");
    e |= test(f, "(.?.?)(.?.?)(.?.?)", "xxx", "(0,3),(0,2),(2,3),(3,3)");
    e |= test(f, "(.?.?){3}",          "xxx", "(0,3),(0,2)(2,3)(3,3)");
    e |= test(f, "(.?.?)*",            "xxx", "(0,3),(0,2)(2,3)");

    e |= test(f, "a?((ab)?)(b?)",                  "ab",    "(0,2),(1,1),(?,?),(1,2)");
    e |= test(f, "(a?)((ab)?)b?",                  "ab",    "(0,2),(0,1),(1,1),(?,?)");
    e |= test(f, "a?((ab)?)b?",                    "ab",    "(0,2),(1,1),(?,?)");
    e |= test(f, "(ab?)(b?a)",                     "aba",   "(0,3),(0,2),(2,3)");
    e |= test(f, "(a|ab)(ba|a)",                   "aba",   "(0,3),(0,1),(1,3)");
    e |= test(f, "(a|ab|ba)",                      "aba",   "(0,2),(0,2)");
    e |= test(f, "(a|ab|ba)(a|ab|ba)",             "aba",   "(0,3),(0,1),(1,3)");
    e |= test(f, "(a|ab|ba)*",                     "aba",   "(0,3),(0,1)(1,3)");
    e |= test(f, "(aba|a*b)",                      "ababa", "(0,3),(0,3)");
    e |= test(f, "(aba|a*b)(aba|a*b)",             "ababa", "(0,5),(0,2),(2,5)");
    e |= test(f, "(aba|a*b)(aba|a*b)(aba|a*b)",    "ababa");
    e |= test(f, "(aba|ab|a)",                     "ababa", "(0,3),(0,3)");
    e |= test(f, "(aba|ab|a)(aba|ab|a)",           "ababa", "(0,5),(0,2),(2,5)");
    e |= test(f, "(aba|ab|a)(aba|ab|a)(aba|ab|a)", "ababa", "(0,5),(0,2),(2,4),(4,5)");
    e |= test(f, "(aba|ab|a)*",                    "ababa", "(0,5),(0,2)(2,5)");

    e |= test(f, "(a(b)?)",        "aba", "(0,2),(0,2),(1,2)");
    e |= test(f, "(a(b)?)(a(b)?)", "aba", "(0,3),(0,2),(1,2),(2,3),(?,?)");

    e |= test(f, "(.*)(.*)",               "xx",      "(0,2),(0,2),(2,2)");
    e |= test(f, ".*(.*)",                 "xx",      "(0,2),(2,2)");
    e |= test(f, "(a.*z|b.*y)",            "azbazby", "(0,5),(0,5)");
    e |= test(f, "(a.*z|b.*y)(a.*z|b.*y)", "azbazby", "(0,7),(0,5),(5,7)");
    e |= test(f, "(a.*z|b.*y)*",           "azbazby", "(0,7),(0,5)(5,7)");
    e |= test(f, "(.|..)(.*)",             "ab",      "(0,2),(0,1),(1,2)");

    e |= test(f, "((..)*(...)*)",              "xxx", "(0,3),(0,3),(?,?),(0,3)");
    e |= test(f, "((..)*(...)*)*",             "xxx", "(0,3),(0,3),(?,?),(0,3)");
    e |= test(f, "((..)*(...)*)((..)*(...)*)", "xxx", "(0,3),(0,3),(?,?),(0,3),(3,3),(?,?),(?,?)");

    // nullsubexpr
    e |= test(f, "(a*)*",        "a",        "(0,1),(0,1)");
    e |= test(f, "(a*)*",        "x",        "(0,0),(0,0)");
    e |= test(f, "(a*)*",        "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a*)*",        "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a*)+",        "a",        "(0,1),(0,1)");
    e |= test(f, "(a*)+",        "x",        "(0,0),(0,0)");
    e |= test(f, "(a*)+",        "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a*)+",        "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a+)*",        "a",        "(0,1),(0,1)");
    e |= test(f, "(a+)*",        "x",        "(0,0),(?,?)");
    e |= test(f, "(a+)*",        "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a+)*",        "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "(a+)+",        "a",        "(0,1),(0,1)");
    e |= test(f, "(a+)+",        "x");
    e |= test(f, "(a+)+",        "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "(a+)+",        "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([a]*)*",      "a",        "(0,1),(0,1)");
    e |= test(f, "([a]*)*",      "x",        "(0,0),(0,0)");
    e |= test(f, "([a]*)*",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([a]*)*",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([a]*)+",      "a",        "(0,1),(0,1)");
    e |= test(f, "([a]*)+",      "x",        "(0,0),(0,0)");
    e |= test(f, "([a]*)+",      "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([a]*)+",      "aaaaaax",  "(0,6),(0,6)");
    e |= test(f, "([^b]*)*",     "a",        "(0,1),(0,1)");
    e |= test(f, "([^b]*)*",     "b",        "(0,0),(0,0)");
    e |= test(f, "([^b]*)*",     "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([^b]*)*",     "aaaaaab",  "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",     "a",        "(0,1),(0,1)");
    e |= test(f, "([ab]*)*",     "aaaaaa",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",     "ababab",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",     "bababa",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",     "b",        "(0,1),(0,1)");
    e |= test(f, "([ab]*)*",     "bbbbbb",   "(0,6),(0,6)");
    e |= test(f, "([ab]*)*",     "aaaabcde", "(0,5),(0,5)");
    e |= test(f, "([^a]*)*",     "b",        "(0,1),(0,1)");
    e |= test(f, "([^a]*)*",     "bbbbbb",   "(0,6),(0,6)");
    e |= test(f, "([^a]*)*",     "aaaaaa",   "(0,0),(0,0)");
    e |= test(f, "([^ab]*)*",    "ccccxx",   "(0,6),(0,6)");
    e |= test(f, "([^ab]*)*",    "ababab",   "(0,0),(0,0)");
    e |= test(f, "((z)+|a)*",    "zabcde",   "(0,2),(0,1)(1,2),(0,1)(?,?)");
    e |= test(f, "(a)",          "aaa",      "(0,1),(0,1)");
    e |= test(f, "(a*)*(x)",     "x",        "(0,1),(0,0),(0,1)");
    e |= test(f, "(a*)*(x)",     "ax",       "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)*(x)",     "axa",      "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)+(x)",     "x",        "(0,1),(0,0),(0,1)");
    e |= test(f, "(a*)+(x)",     "ax",       "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*)+(x)",     "axa",      "(0,2),(0,1),(1,2)");
    e |= test(f, "(a*){2}(x)",   "x",        "(0,1),(0,0)(0,0),(0,1)");
    e |= test(f, "(a*){2}(x)",   "ax",       "(0,2),(0,1)(1,1),(1,2)");
    e |= test(f, "(a*){2}(x)",   "axa",      "(0,2),(0,1)(1,1),(1,2)");
    e |= test(f, "(()|.)(b)",    "ab",       "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "(()|[ab])(b)", "ab",       "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "(()|[ab])+b",  "aaab",     "(0,4),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?)");
    e |= test(f, "(.|())(b)",    "ab",       "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "([ab]|())(b)", "ab",       "(0,2),(0,1),(?,?),(1,2)");
    e |= test(f, "([ab]|())+b",  "aaab",     "(0,4),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?)");
    e |= test(f, "(.?)(b)",      "ab",       "(0,2),(0,1),(1,2)");

    // other
    e |= test(f, "(a|aa)*",    "aaa",  "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(a|aa)*",    "aaaa", "(0,4),(0,1)(1,2)(2,3)(3,4)");
    e |= test(f, "(aa|a)*",    "aaa",  "(0,3),(0,2)(2,3)");
    e |= test(f, "(aa|a)*",    "aaaa", "(0,4),(0,2)(2,4)");
    e |= test(f, "a|(a)",      "a",    "(0,1),(?,?)");
    e |= test(f, "(a)|a",      "a",    "(0,1),(0,1)");
    e |= test(f, "(b)a|b(a)",  "ba",   "(0,2),(0,1),(?,?)");
    e |= test(f, "b(a)|(b)a",  "ba",   "(0,2),(1,2),(?,?)");
    e |= test(f, "a*|(a|aa)*", "aa",   "(0,2),(?,?)");
    e |= test(f, "(a|aa)*|a*", "aa",   "(0,2),(0,1)(1,2)");

    e |= test(f, "(aa*|aaa*)*",                  "aaaaaa", "(0,6),(0,6)");
    e |= test(f, "(aa*|aaa*)(aa*|aaa*)",         "aaaaaa", "(0,6),(0,5),(5,6)");
    e |= test(f, "(aa*|aaa*){2}",                "aaaaaa", "(0,6),(0,5)(5,6)");
    e |= test(f, "((aa)*|(aaa)*)((aa)*|(aaa)*)", "aaaaaa",
              "(0,6),(0,6),(0,2)(2,4)(4,6),(?,?),(6,6),(?,?),(?,?)");
    e |= test(f, "((aa)*|(aaa)*){2}",            "aaaaaa",
              "(0,6),(0,6)(6,6),(0,2)(2,4)(4,6)(?,?),(?,?)(?,?)");
    e |= test(f, "(aa)*|(aaa)*",                 "aaaaaa", "(0,6),(0,2)(2,4)(4,6),(?,?)");

    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XY",         "(0,2),(0,1)(1,2)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XabY",       "(0,4),(0,2)(2,4)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XababY",     "(0,6),(0,1)(1,5)(5,6)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XabababY",   "(0,8),(0,2)(2,6)(6,8)");
    e |= test(f, "(X|Xa|Xab|Xaba|abab|baba|bY|Y)*", "XababababY", "(0,10),(0,1)(1,5)(5,9)(9,10)");

    e |= test(f, "(y){2}",        "");
    e |= test(f, "(y){2}",        "y");
    e |= test(f, "(y){2}",        "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){0,2}",      "",     "(0,0),(?,?)");
    e |= test(f, "(y){0,2}",      "y",    "(0,1),(0,1)");
    e |= test(f, "(y){0,2}",      "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}",      "");
    e |= test(f, "(y){1,2}",      "y",    "(0,1),(0,1)");
    e |= test(f, "(y){1,2}",      "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y",     "");
    e |= test(f, "(y){1,2}y",     "y");
    e |= test(f, "(y){1,2}y",     "yy",   "(0,2),(0,1)");
    e |= test(f, "(y){1,2}y",     "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y?",    "");
    e |= test(f, "(y){1,2}y?",    "y",    "(0,1),(0,1)");
    e |= test(f, "(y){1,2}y?",    "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){1,2}y?",    "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y){2,}",       "");
    e |= test(f, "(y){2,}",       "y");
    e |= test(f, "(y){2,}",       "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y){2,}",       "yyy",  "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(y?){2}",       "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){2}",       "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){2}",       "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){0,2}",     "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){0,2}",     "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){0,2}",     "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}",     "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){1,2}",     "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){1,2}",     "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y",    "");
    e |= test(f, "(y?){1,2}y",    "y",    "(0,1),(0,0)(0,0)");
    e |= test(f, "(y?){1,2}y",    "yy",   "(0,2),(0,1)(1,1)");
    e |= test(f, "(y?){1,2}y",    "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y?",   "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){1,2}y?",   "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){1,2}y?",   "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){1,2}y?",   "yyy",  "(0,3),(0,1)(1,2)");
    e |= test(f, "(y?){2,}",      "",     "(0,0),(0,0)(0,0)");
    e |= test(f, "(y?){2,}",      "y",    "(0,1),(0,1)(1,1)");
    e |= test(f, "(y?){2,}",      "yy",   "(0,2),(0,1)(1,2)");
    e |= test(f, "(y?){2,}",      "yyy",  "(0,3),(0,1)(1,2)(2,3)");
    e |= test(f, "(y|(x?)){1,3}", "y",    "(0,1),(0,1)(1,1)(1,1),(?,?)(1,1)(1,1)");
    e |= test(f, "(y[y]?){3}",    "yyyy", "(0,4),(0,2)(2,3)(3,4)");

    // repetition
    e |= test(f, "((..)|(.))",                     "");
    e |= test(f, "((..)|(.))((..)|(.))",           "");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "");
    e |= test(f, "((..)|(.)){1}",                  "");
    e |= test(f, "((..)|(.)){2}",                  "");
    e |= test(f, "((..)|(.)){3}",                  "");
    e |= test(f, "((..)|(.))*",                    "", "(0,0),(?,?),(?,?),(?,?)");

    e |= test(f, "((..)|(.))",                     "a", "(0,1),(0,1),(?,?),(0,1)");
    e |= test(f, "((..)|(.))((..)|(.))",           "a");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "a");
    e |= test(f, "((..)|(.)){1}",                  "a", "(0,1),(0,1),(?,?),(0,1)");
    e |= test(f, "((..)|(.)){2}",                  "a");
    e |= test(f, "((..)|(.)){3}",                  "a");
    e |= test(f, "((..)|(.))*",                    "a", "(0,1),(0,1),(?,?),(0,1)");

    e |= test(f, "((..)|(.))",                     "aa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aa",
              "(0,2),(0,1),(?,?),(0,1),(1,2),(?,?),(1,2)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aa");
    e |= test(f, "((..)|(.)){1}",                  "aa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aa", "(0,2),(0,1)(1,2),(?,?)(?,?),(0,1)(1,2)");
    e |= test(f, "((..)|(.)){3}",                  "aa");
    e |= test(f, "((..)|(.))*",                    "aa", "(0,2),(0,2),(0,2),(?,?)");

    e |= test(f, "((..)|(.))",                     "aaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaa",
              "(0,3),(0,2),(0,2),(?,?),(2,3),(?,?),(2,3)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaa",
              "(0,3),(0,1),(?,?),(0,1),(1,2),(?,?),(1,2),(2,3),(?,?),(2,3)");
    e |= test(f, "((..)|(.)){1}",                  "aaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaa", "(0,3),(0,2)(2,3),(0,2)(?,?),(?,?)(2,3)");
    e |= test(f, "((..)|(.)){3}",                  "aaa",
              "(0,3),(0,1)(1,2)(2,3),(?,?)(?,?)(?,?),(0,1)(1,2)(2,3)");
    e |= test(f, "((..)|(.))*",                    "aaa", "(0,3),(0,2)(2,3),(0,2)(?,?),(?,?)(2,3)");

    e |= test(f, "((..)|(.))",                     "aaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,3),(?,?),(2,3),(3,4),(?,?),(3,4)");
    e |= test(f, "((..)|(.)){1}",                  "aaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaa",
              "(0,4),(0,2)(2,3)(3,4),(0,2)(?,?)(?,?),(?,?)(2,3)(3,4)");
    e |= test(f, "((..)|(.))*",                    "aaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");

    e |= test(f, "((..)|(.))",                     "aaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaaa",
              "(0,5),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?),(4,5),(?,?),(4,5)");
    e |= test(f, "((..)|(.)){1}",                  "aaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaaa",
              "(0,5),(0,2)(2,4)(4,5),(0,2)(2,4)(?,?),(?,?)(?,?)(4,5)");
    e |= test(f, "((..)|(.))*",                    "aaaaa",
              "(0,5),(0,2)(2,4)(4,5),(0,2)(2,4)(?,?),(?,?)(?,?)(4,5)");

    e |= test(f, "((..)|(.))",                     "aaaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))",           "aaaaaa",
              "(0,4),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?)");
    e |= test(f, "((..)|(.))((..)|(.))((..)|(.))", "aaaaaa",
              "(0,6),(0,2),(0,2),(?,?),(2,4),(2,4),(?,?),(4,6),(4,6),(?,?)");
    e |= test(f, "((..)|(.)){1}",                  "aaaaaa", "(0,2),(0,2),(0,2),(?,?)");
    e |= test(f, "((..)|(.)){2}",                  "aaaaaa",
              "(0,4),(0,2)(2,4),(0,2)(2,4),(?,?)(?,?)");
    e |= test(f, "((..)|(.)){3}",                  "aaaaaa",
              "(0,6),(0,2)(2,4)(4,6),(0,2)(2,4)(4,6),(?,?)(?,?)(?,?)");
    e |= test(f, "((..)|(.))*",                    "aaaaaa",
              "(0,6),(0,2)(2,4)(4,6),(0,2)(2,4)(4,6),(?,?)(?,?)(?,?)");

    e |= test(f, "X(.?){0,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){1,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){2,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){3,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){4,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){5,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){6,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");
    e |= test(f, "X(.?){7,}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)");

    e |= test(f, "X(.?){8,}Y",  "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){0,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){1,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){2,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){3,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){4,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){5,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){6,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){7,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");
    e |= test(f, "X(.?){8,8}Y", "X1234567Y", "(0,9),(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)(8,8)");

    e |= test(f, "(a|ab|c|bcd){0,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){1,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){2,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){3,}(d*)",   "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){4,}(d*)",   "ababcd");
    e |= test(f, "(a|ab|c|bcd){0,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){1,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){2,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){3,10}(d*)", "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd){4,10}(d*)", "ababcd");
    e |= test(f, "(a|ab|c|bcd)*(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(a|ab|c|bcd)+(d*)",      "ababcd", "(0,6),(0,2)(2,3)(3,6),(6,6)");
    e |= test(f, "(ab|a|c|bcd){0,}(d*)",   "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){1,}(d*)",   "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){2,}(d*)",   "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){3,}(d*)",   "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){4,}(d*)",   "ababcd");
    e |= test(f, "(ab|a|c|bcd){0,10}(d*)", "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){1,10}(d*)", "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){2,10}(d*)", "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){3,10}(d*)", "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd){4,10}(d*)", "ababcd");
    e |= test(f, "(ab|a|c|bcd)*(d*)",      "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");
    e |= test(f, "(ab|a|c|bcd)+(d*)",      "ababcd", "(0,6),(0,2)(2,4)(4,5),(5,6)");

    // rightassoc
    e |= test(f, "(a|ab)(c|bcd)(d*)",   "abcd", "(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "(a|ab)(bcd|c)(d*)",   "abcd", "(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "(ab|a)(c|bcd)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(bcd|c)(d*)",   "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(a*)(b|abc)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(abc|b)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(b|abc)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a*)(abc|b)(c*)",     "abc",  "(0,3),(0,1),(1,2),(2,3)");
    e |= test(f, "(a|ab)(c|bcd)(d|.*)", "abcd", "(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "(a|ab)(bcd|c)(d|.*)", "abcd", "(0,4),(0,1),(1,4),(4,4)");
    e |= test(f, "(ab|a)(c|bcd)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");
    e |= test(f, "(ab|a)(bcd|c)(d|.*)", "abcd", "(0,4),(0,2),(2,3),(3,4)");

    // other
    std::string s = "(0,4),(0,4),(0,1)(1,2)(2,3)(3,4)";
    for (size_t i = 0; i < 1000 - 4; ++i) s += "(4,4)";

    e |= test(f, "((a?){1,1000})*", "aaaa", s.c_str());

    return e;
}

static int test_all_tstring() {
    int e = 0;

    e |= test_tstring("a",        "a",   "1 a 2 ");
    e |= test_tstring("(a)",      "a",   "1 3 a 4 2 ");
    e |= test_tstring("(((a)))",  "a",   "1 3 a 4 2 ");
    e |= test_tstring("(a)*",     "aaa", "1 3 a 4 3 a 4 3 a 4 2 ");
    e |= test_tstring("(a(b?))*", "aba", "1 3 5 a 6 7 b 8 4 3 5 a 6 7 8 4 2 ");
    e |= test_tstring("(a(b)?)*", "aba", "1 3 5 a 6 7 9 b 10 8 4 3 5 a 6 7 8 4 2 ");
    e |= test_tstring("(a(b*))*", "abb", "1 3 5 a 6 7 b b 8 4 2 ");
    e |= test_tstring("(a(b)*)*", "abb", "1 3 5 a 6 7 9 b 10 9 b 10 8 4 2 ");
    e |= test_tstring("(a){2}",   "aa",  "1 3 a 4 3 a 4 2 ");
    e |= test_tstring("ab",       "ab",  "1 a b 2 ");
    e |= test_tstring("a(b)",     "ab",  "1 3 a 4 5 b 6 2 ");
    e |= test_tstring("(a)b",     "ab",  "1 3 a 4 5 b 6 2 ");
    e |= test_tstring("(a)(b)",   "ab",  "1 3 a 4 5 b 6 2 ");

    return e;
}

int main() {
    int e = 0;

    e |= test_all_posix(0);
    e |= test_all_posix(REG_MULTIPASS);
    e |= test_all_posix(REG_SUBHIST);
    e |= test_all_posix(REG_SUBHIST | REG_MULTIPASS);

    e |= test_all_leftmost(REG_LEFTMOST);
    e |= test_all_leftmost(REG_LEFTMOST | REG_MULTIPASS);
    e |= test_all_leftmost(REG_LEFTMOST | REG_SUBHIST);
    e |= test_all_leftmost(REG_LEFTMOST | REG_SUBHIST | REG_MULTIPASS);

    e |= test_all_posix(REG_NFA);
    e |= test_all_posix(REG_NFA | REG_TRIE);
    e |= test_all_posix(REG_NFA | REG_SLOWPREC);

    e |= test_all_leftmost(REG_NFA | REG_LEFTMOST);
    e |= test_all_leftmost(REG_NFA | REG_LEFTMOST | REG_TRIE);

    e |= test_all_tstring();

    return e;
}