File: vmvec.cpp

package info (click to toggle)
qtads 2.1.6-1
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 16,156 kB
  • ctags: 18,767
  • sloc: cpp: 133,078; ansic: 26,048; xml: 18; makefile: 11
file content (3246 lines) | stat: -rw-r--r-- 97,874 bytes parent folder | download | duplicates (8)
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
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
#ifdef RCSID
static char RCSid[] =
"$Header$";
#endif

/* 
 *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  vmvec.cpp - T3 VM Vector metaclass
Function
  
Notes
  
Modified
  05/14/00 MJRoberts  - Creation
*/

#include <stdlib.h>
#include <stdarg.h>

#include "t3std.h"
#include "vmtype.h"
#include "vmmcreg.h"
#include "vmmeta.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmvec.h"
#include "vmlst.h"
#include "vmerr.h"
#include "vmerrnum.h"
#include "vmfile.h"
#include "vmstack.h"
#include "vmbif.h"
#include "vmundo.h"
#include "vmrun.h"
#include "vmiter.h"
#include "vmsort.h"


/* ------------------------------------------------------------------------ */
/*
 *   The largest number of items we can store is the smaller of the maximum
 *   value for a UINT2 (unsigned 16-bit integer, so 2^16-1) or the maximum
 *   allocation size (OSMALMAX) divided by the element size (DATAHOLDER)
 *   divided minus overhead. 
 */
const int32_t VEC_MAX_BY_ALO =
    (int32_t)(OSMALMAX/(VMB_DATAHOLDER + 1.0/8)) - 4;
const int32_t VEC_MAX_ELEMENTS =
    (VEC_MAX_BY_ALO < 0xFFFF ? VEC_MAX_BY_ALO : 0xFFFF);


/* ------------------------------------------------------------------------ */
/*
 *   statics 
 */

/* metaclass registration object */
static CVmMetaclassVector metaclass_reg_obj;
CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj;

/* function table */
int (CVmObjVector::
     *CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self,
                                   vm_val_t *retval, uint *argc) =
{
    &CVmObjVector::getp_undef,                                         /* 0 */
    &CVmObjVector::getp_to_list,                                       /* 1 */
    &CVmObjVector::getp_get_size,                                      /* 2 */
    &CVmObjVector::getp_copy_from,                                     /* 3 */
    &CVmObjVector::getp_fill_val,                                      /* 4 */
    &CVmObjVector::getp_subset,                                        /* 5 */
    &CVmObjVector::getp_apply_all,                                     /* 6 */
    &CVmObjVector::getp_index_which,                                   /* 7 */
    &CVmObjVector::getp_for_each,                                      /* 8 */
    &CVmObjVector::getp_for_each_assoc,                                /* 9 */
    &CVmObjVector::getp_map_all,                                      /* 10 */
    &CVmObjVector::getp_index_of,                                     /* 11 */
    &CVmObjVector::getp_val_which,                                    /* 12 */
    &CVmObjVector::getp_last_index_of,                                /* 13 */
    &CVmObjVector::getp_last_index_which,                             /* 14 */
    &CVmObjVector::getp_last_val_which,                               /* 15 */
    &CVmObjVector::getp_count_of,                                     /* 16 */
    &CVmObjVector::getp_count_which,                                  /* 17 */
    &CVmObjVector::getp_get_unique,                                   /* 18 */
    &CVmObjVector::getp_append_unique,                                /* 19 */
    &CVmObjVector::getp_sort,                                         /* 20 */
    &CVmObjVector::getp_set_length,                                   /* 21 */
    &CVmObjVector::getp_insert_at,                                    /* 22 */
    &CVmObjVector::getp_remove_element_at,                            /* 23 */
    &CVmObjVector::getp_remove_range,                                 /* 24 */
    &CVmObjVector::getp_append,                                       /* 25 */
    &CVmObjVector::getp_prepend,                                      /* 26 */
    &CVmObjVector::getp_append_all,                                   /* 27 */
    &CVmObjVector::getp_remove_element,                               /* 28 */
    &CVmObjVector::getp_splice,                                       /* 29 */
    &CVmObjVector::getp_join,                                         /* 30 */
    &CVmObjVector::getp_generate,                                     /* 31 */
    &CVmObjVector::getp_indexOfMin,                                   /* 32 */
    &CVmObjVector::getp_minVal,                                       /* 33 */
    &CVmObjVector::getp_indexOfMax,                                   /* 34 */
    &CVmObjVector::getp_maxVal                                        /* 35 */
};

/* static method indices */
static const int PROPIDX_generate = 31;


/* ------------------------------------------------------------------------ */
/* 
 *   create with a given number of elements 
 */
CVmObjVector::CVmObjVector(VMG_ size_t element_count)
{
    /* allocate space */
    alloc_vector(vmg_ element_count);

    /* 
     *   a vector intially has no elements in use; the element_count
     *   merely gives the number of slots to be allocated initially (this
     *   won't affect the allocation count, which is stored separately) 
     */
    set_element_count(0);
}

/* ------------------------------------------------------------------------ */
/*
 *   allocate space 
 */
void CVmObjVector::alloc_vector(VMG_ size_t cnt)
{
    /* allocate space for the given number of elements */
    ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this);

    /* set the element count and allocated count */
    set_allocated_count(cnt);
    set_element_count(cnt);

    /* clear the undo bits */
    clear_undo_bits();
}

/* ------------------------------------------------------------------------ */
/*
 *   notify of deletion 
 */
void CVmObjVector::notify_delete(VMG_ int)
{
    /* free our extension */
    if (ext_ != 0)
        G_mem->get_var_heap()->free_mem(ext_);
}

/* ------------------------------------------------------------------------ */
/*
 *   Check an argument to see if it's a source list or vector 
 */
static int get_src_obj(VMG_ const vm_val_t *arg, size_t *copy_cnt)
{
    /* check for a list */
    int c;
    if (arg->is_listlike(vmg0_) && (c = arg->ll_length(vmg0_)) >= 0)
    {
        /* it's a list-like object */
        *copy_cnt = c;
        return TRUE;
    }

    /* it's not a list */
    return FALSE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   create dynamically using stack arguments 
 */
vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr,
                                            uint argc)
{
    vm_obj_id_t id;
    CVmObjVector *vec;
    size_t cnt = 0;
    size_t copy_cnt = 0;
    vm_val_t *lstval = 0;
    size_t i;
    
    /* check arguments */
    if (argc > 2)
        err_throw(VMERR_WRONG_NUM_OF_ARGS);

    /* if there are no arguments, use a default initial allocation size */
    if (argc == 0)
        cnt = 10;

    /* parse the first argument, if present */
    if (argc >= 1)
    {
        /* get the first argument */
        vm_val_t *arg1 = G_stk->get(0);

        /* check for an initial allocation length or source object */
        if (arg1->typ == VM_INT)
        {
            /* get the number of elements to allocate */
            cnt = (size_t)arg1->val.intval;
        }
        else if (get_src_obj(vmg_ arg1, &copy_cnt))
        {
            /* it's a source object */
            lstval = arg1;
        }
        else
        {
            /* anything else is invalid */
            err_throw(VMERR_BAD_TYPE_BIF);
        }
    }

    /* parse the second argument, if present */
    if (argc >= 2)
    {
        /* get the second argument */
        vm_val_t *arg2 = G_stk->get(1);
        
        /* see what we have */
        if (get_src_obj(vmg_ arg2, &copy_cnt))
        {
            /* it's a source object */
            lstval = arg2;
        }
        else if (arg2->typ == VM_INT)
        {
            /* 
             *   They want an explicit initial size, with the elements filled
             *   in with nil values.  Set the copy size; we'll know to fill
             *   in nil values since there's no source object to copy from.  
             */
            copy_cnt = (size_t)arg2->val.intval;
        }
        else
        {
            /* invalid source type */
            err_throw(VMERR_BAD_TYPE_BIF);
        }
    }

    /* 
     *   make sure we allocate enough initial space to store the source
     *   object we're copying 
     */
    if (copy_cnt > cnt)
        cnt = copy_cnt;

    /* create the vector with the given number of elements */
    id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
    vec = new (vmg_ id) CVmObjVector(vmg_ cnt);

    /* copy or initialize the elements */
    for (i = 0 ; i < copy_cnt ; ++i)
    {
        vm_val_t ele;

        /* 
         *   get my element at this index: if we have a source list or
         *   vector, take the current element from that list or vector;
         *   otherwise, initialize the element to nil 
         */
        if (lstval != 0)
            lstval->ll_index(vmg_ &ele, i + 1);
        else
            ele.set_nil();

        /* 
         *   set my element at this index - this is construction, so there's
         *   no need to save undo information 
         */
        vec->set_element(i, &ele);
    }

    /* set the initial size */
    vec->set_element_count(copy_cnt);

    /* discard arguments */
    G_stk->discard(argc);

    /* return the new object */
    return id;
}

/* ------------------------------------------------------------------------ */
/* 
 *   save to a file 
 */
void CVmObjVector::save_to_file(VMG_ class CVmFile *fp)
{
    size_t ele_cnt;
    size_t alloc_cnt;

    /* get our element count and full allocation count */
    alloc_cnt = get_allocated_count();
    ele_cnt = get_element_count();

    /* write the counts and the elements */
    fp->write_uint2(alloc_cnt);
    fp->write_uint2(ele_cnt);
    fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));
}

/* 
 *   restore from a file 
 */
void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self,
                                     CVmFile *fp, CVmObjFixup *fixups)
{
    size_t ele_cnt;
    size_t alloc_cnt;

    /* read the element count and the full allocation count */
    alloc_cnt = fp->read_uint2();
    ele_cnt = fp->read_uint2();

    /* free any existing extension */
    if (ext_ != 0)
    {
        G_mem->get_var_heap()->free_mem(ext_);
        ext_ = 0;
    }

    /* allocate the space at the allocation count */
    alloc_vector(vmg_ alloc_cnt);

    /* set the actual in-use element count */
    set_element_count(ele_cnt);

    /* read the contents */
    fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));

    /* fix up object references in the values */
    fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt);
}

/* ------------------------------------------------------------------------ */
/* 
 *   create with no initial contents 
 */
vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set)
{
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
    new (vmg_ id) CVmObjVector();
    return id;
}

/* 
 *   create with a given number of elements 
 */
vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count)
{
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
    new (vmg_ id) CVmObjVector(vmg_ element_count);
    return id;
}


/* ------------------------------------------------------------------------ */
/*
 *   create with a given list of values, given as varargs vm_val_t values
 */
vm_obj_id_t CVmObjVector::create_fill(VMG_ int in_root_set,
                                      size_t element_count, ...)
{
    /* create the object */
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
    CVmObjVector *vec = new (vmg_ id) CVmObjVector(vmg_ element_count);

    /* populate the elements */
    va_list args;
    va_start(args, element_count);
    for (size_t i = 0 ; i < element_count ; ++i)
    {
        /* get the next value */
        const vm_val_t *val = va_arg(args, const vm_val_t *);

        /* store it */
        vec->set_element(i, val);
    }
    va_end(args);

    /* set the element count */
    vec->set_element_count(element_count);

    /* return the new object's ID */
    return id;
}


/* ------------------------------------------------------------------------ */
/*
 *   Create a copy of this object 
 */
vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val)
{
    vm_obj_id_t new_id;
    CVmObjVector *new_vec;
    
    /* save the original object on the stack for gc protection */
    G_stk->push(self_val);

    /* create a new vector with the same parameters as this one */
    new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
    new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count());
    new_vec->set_element_count(get_element_count());

    /* copy the elements from the old vector to the new vector */
    memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
           calc_alloc_ele(get_element_count()));

    /* done with the gc protection - discard it */
    G_stk->discard(1);

    /* return the new vector's object ID */
    return new_id;
}

/* ------------------------------------------------------------------------ */
/*
 *   Set the allocation size - this is the number of elements we've allocated
 *   space for, which might exceed the number of elements we're actually
 *   storing currently.  
 */
void CVmObjVector::set_allocated_count(size_t cnt)
{
    /* if the new size exceeds the maximum allowed size, throw an error */
    if (cnt > 65535)
        err_throw(VMERR_OUT_OF_RANGE);

    /* set the new allocated size */
    vmb_put_len(cons_get_vector_ext_ptr(), cnt);
}

/* ------------------------------------------------------------------------ */
/*
 *   Create an iterator 
 */
void CVmObjVector::new_iterator(VMG_ vm_val_t *retval,
                                const vm_val_t *self_val)
{
    vm_val_t copy_val;

    /* 
     *   Create a copy of the vector - since a vector can change, we must
     *   always create an iterator based on a copy, so that the iterator has
     *   a consistent view of the original as of the time the iterator was
     *   created.  
     */
    copy_val.set_obj(create_copy(vmg_ self_val));

    /* push the copy for gc protection */
    G_stk->push(&copy_val);

    /* 
     *   Set up a new indexed iterator object.  The first valid index for a
     *   vector is always 1, and the last valid index is the same as the
     *   number of elements.  
     */
    retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ &copy_val,
        1, get_element_count()));

    /* discard gc protection */
    G_stk->discard();
}

/*
 *   Create a live iterator 
 */
void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval,
                                     const vm_val_t *self_val)
{
    /* 
     *   Set up a new indexed iterator object.  The first valid index for a
     *   vector is always 1, and the last valid index is the same as the
     *   number of elements.  Since we want a "live" iterator, create the
     *   iterator with a direct reference to the vector.  
     */
    retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val,
        1, get_element_count()));
}

/* ------------------------------------------------------------------------ */
/* 
 *   set a property 
 */
void CVmObjVector::set_prop(VMG_ class CVmUndo *,
                            vm_obj_id_t, vm_prop_id_t,
                            const vm_val_t *)
{
    err_throw(VMERR_INVALID_SETPROP);
}

/* ------------------------------------------------------------------------ */
/* 
 *   call a static property 
 */
int CVmObjVector::call_stat_prop(VMG_ vm_val_t *result,
                                 const uchar **pc_ptr, uint *argc,
                                 vm_prop_id_t prop)
{
    /* get the function table index */
    int idx = G_meta_table->prop_to_vector_idx(
        metaclass_reg_->get_reg_idx(), prop);

    /* check for static methods */
    switch (idx)
    {
    case PROPIDX_generate:
        return static_getp_generate(vmg_ result, argc);

    default:
        /* inherit the default handling */
        return CVmObjCollection::call_stat_prop(
            vmg_ result, pc_ptr, argc, prop);
    }
}


/* ------------------------------------------------------------------------ */
/* 
 *   get a property 
 */
int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
                           vm_obj_id_t self, vm_obj_id_t *source_obj,
                           uint *argc)
{
    uint func_idx;

    /* translate the property index to an index into our function table */
    func_idx = G_meta_table
               ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);

    /* call the appropriate function */
    if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
    {
        *source_obj = metaclass_reg_->get_class_obj(vmg0_);
        return TRUE;
    }

    /* inherit our base class handling */
    return CVmObjCollection::
        get_prop(vmg_ prop, retval, self, source_obj, argc);
}

/* ------------------------------------------------------------------------ */
/*
 *   Expand the vector to accommodate the given number of new elements.
 *   We'll increase the element count, and we'll save undo for the change.
 *   If necessary, we'll re-allocate our memory extension block at a
 *   larger size. 
 */
void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements)
{
    /* calculate the element count */
    size_t new_ele_cnt = get_element_count() + added_elements;
    
    /* remember the new size, saving undo */
    set_element_count_undo(vmg_ self, new_ele_cnt);
}


/* ------------------------------------------------------------------------ */
/*
 *   Append an element, with no undo 
 */
void CVmObjVector::append_element(VMG_ vm_obj_id_t self, const vm_val_t *val)
{
    /* expand the vector if necessary */
    size_t cnt = get_element_count();
    if (cnt >= get_allocated_count())
    {
        /* expand by 50% of the current size, or at least 5 new elements */
        int inc = cnt/2;
        if (inc < 5)
            inc = 5;

        /* do the expansion */
        expand_by(vmg_ self, inc);
    }

    /* set the element */
    set_element(cnt, val);

    /* count it in the vector */
    set_element_count(cnt+1);
}

/* ------------------------------------------------------------------------ */
/*
 *   Set the element count, keeping undo for the change 
 */
void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self,
                                          size_t new_ele_cnt)
{
    /* add undo if necessary */
    if (G_undo != 0)
    {
        size_t old_ele_cnt;
        vm_val_t old_val;
        size_t idx;
        vm_val_t nil_val;

        /* get the old size */
        old_ele_cnt = get_element_count();

        /* 
         *   If we're shrinking the vector, save undo for each element we're
         *   dropping off the end; to do this, simply set each element we're
         *   losing to nil, since this will add an undo record with the
         *   original value of the element.  
         *   
         *   Note that we must save undo for dropped elements BEFORE we set
         *   the vector's new sizd.  We must perform the steps in this order
         *   because undo is applied in reverse order: when we read back the
         *   undo, we'll first change the vector size to its old size (since
         *   that's the last saved undo instruction), then we'll set the
         *   elements to their old values.  
         */
        nil_val.set_nil();
        for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx)
            set_element_undo(vmg_ self, idx, &nil_val);

        /* set up an integer with the pre-modification element count */
        old_val.set_int(old_ele_cnt);

        /* 
         *   add the undo record - use the special index value 0xffffffff
         *   as the key; this will never be a valid index for a real
         *   element, because we could never address an element with an
         *   index this high in a 32-bit address space 
         */
        G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val);
    }

    /* 
     *   if the new size is larger than our current allocation size,
     *   increase the allocated size 
     */
    if (new_ele_cnt > get_allocated_count())
    {
        size_t new_alloc_cnt;
        size_t new_mem_size;

        /* bump up the allocated size */
        new_alloc_cnt = get_allocated_count() + get_alloc_count_increment();

        /* if that's still not big enough, go up to the requested size */
        if (new_alloc_cnt < new_ele_cnt)
            new_alloc_cnt = new_ele_cnt;

        /* get the new size we need */
        new_mem_size = calc_alloc(new_alloc_cnt);

        /* reallocate our memory at the new, larger size */
        ext_ = (char *)G_mem->get_var_heap()
               ->realloc_mem(new_mem_size, ext_, this);

        /* set the new allocation size */
        set_allocated_count(new_alloc_cnt);

        /* 
         *   clear the undo bits (it's easier than moving them, and the only
         *   cost is that we might generate some redundant undo records) 
         */
        clear_undo_bits();
    }

    /* 
     *   if we're expanding the in-use size of the vector, set each
     *   newly-added element to nil 
     */
    if (new_ele_cnt > get_element_count())
    {
        size_t i;
        vm_val_t nil_val;

        /* 
         *   set the old value for each new element to nil (we don't have to
         *   save undo for this, since the undo operation for the change in
         *   size will simply discard all of the new elements) 
         */
        nil_val.set_nil();
        for (i = get_element_count() ; i < new_ele_cnt ; ++i)
            set_element(i, &nil_val);
    }

    /* set the new element count */
    set_element_count(new_ele_cnt);
}

/* ------------------------------------------------------------------------ */
/* 
 *   notify of new undo savepoint 
 */
void CVmObjVector::notify_new_savept()
{
    /* 
     *   clear the undo bits - we obviously have no undo for the new
     *   savepoint yet 
     */
    clear_undo_bits();
}

/* ------------------------------------------------------------------------ */
/*
 *   apply undo 
 */
void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec)
{
    /* 
     *   if the record refers to index 0xffffffff, this is a size-change
     *   record; otherwise, it's an ordinary element change record 
     */
    if ((size_t)rec->id.intval == 0xffffffffU)
    {
        /* 
         *   it's a size change - apply the new size, which is stored as
         *   an integer in the old value record 
         */
        set_element_count((size_t)rec->oldval.val.intval);
    }
    else
    {
        /* it's an ordinary index record - set the element from undo data */
        set_element((size_t)rec->id.intval, &rec->oldval);
    }
}

/* ------------------------------------------------------------------------ */
/*
 *   mark references from an undo record 
 */
void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec)
{
    /* if the undo record refers to an object, mark the object */
    if (rec->oldval.typ == VM_OBJ)
        G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE);
}

/* ------------------------------------------------------------------------ */
/* 
 *   mark references 
 */
void CVmObjVector::mark_refs(VMG_ uint state)
{
    /* get my element count */
    size_t cnt = get_element_count();

    /* mark as referenced each object in the vector */
    for (char *p = get_element_ptr(0) ; cnt != 0 ;
         --cnt, inc_element_ptr(&p))
    {
        /* 
         *   if this is an object, mark it as referenced, and mark its
         *   references as referenced 
         */
        if (vmb_get_dh_type(p) == VM_OBJ)
            G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state);
    }
}

/* ------------------------------------------------------------------------ */
/*
 *   load from an image file 
 */
void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self,
                                   const char *ptr, size_t siz)
{
    /* load from the image data */
    load_image_data(vmg_ ptr, siz);

    /* 
     *   save our image data pointer in the object table, so that we can
     *   access it (without storing it ourselves) during a reload
     */
    G_obj_table->save_image_pointer(self, ptr, siz);
}

/*
 *   reload the object from image data 
 */
void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t,
                                     const char *ptr, size_t siz)
{
    /* load the image data */
    load_image_data(vmg_ ptr, siz);
}

/* 
 *   load the data from an image file 
 */
void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz)
{
    size_t alloc_cnt;
    size_t ele_cnt;

    /* if we already have memory allocated, free it */
    if (ext_ != 0)
    {
        G_mem->get_var_heap()->free_mem(ext_);
        ext_ = 0;
    }

    /* get the allocation size and the actual in-use size */
    alloc_cnt = vmb_get_len(ptr);
    ele_cnt = vmb_get_len(ptr + VMB_LEN);

    /* make sure we allocate at least as much as is actually in use */
    if (alloc_cnt < ele_cnt)
        alloc_cnt = ele_cnt;

    /* make sure the size isn't larger than we'd expect */
    if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
        siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt);

    /* 
     *   allocate memory at the new allocation size as indicated in the
     *   image data 
     */
    alloc_vector(vmg_ alloc_cnt);

    /* set the in-use element count */
    set_element_count(ele_cnt);

    /* 
     *   if the size is smaller than we'd expect from the initialized
     *   element count, set extra elements to nil 
     */
    if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
    {
        size_t i;
        vm_val_t nil_val;

        /* set all elements to nil */
        nil_val.set_nil();
        for (i = 0 ; i < ele_cnt ; ++i)
            set_element(i, &nil_val);
    }

    /* copy the initialized elements from the image data */
    memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2);

    /* we're resetting to initial state, so forget all undo */
    clear_undo_bits();
}

/* ------------------------------------------------------------------------ */
/* 
 *   index the vector
 */
int CVmObjVector::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t,
                              const vm_val_t *index_val)
{
    /* get the index value as an integer */
    int32_t idx = index_val->num_to_int(vmg0_);

    /* make sure it's in range - 1 to our element count, inclusive */
    if (idx < 1 || (uint32_t)idx > get_element_count())
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* 
     *   get the indexed element and store it in the result, adjusting the
     *   index to the C-style 0-based range 
     */
    get_element(idx - 1, result);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   set an indexed element of the vector
 */
int CVmObjVector::set_index_val_q(VMG_ vm_val_t *new_container,
                                  vm_obj_id_t self,
                                  const vm_val_t *index_val,
                                  const vm_val_t *new_val)
{
    /* get the index value as an integer */
    int32_t idx = index_val->num_to_int(vmg0_);

    /* make sure it's at least 1 */
    if (idx < 1)
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* 
     *   if it's higher than the current length, extend the vector with nil
     *   entries to the requested size 
     */
    if ((uint32_t)idx > get_element_count())
    {
        size_t i;
        vm_val_t nil_val;
        
        /* note the first new element index */
        i = get_element_count();

        /* extend the vector */
        set_element_count_undo(vmg_ self, idx);

        /* 
         *   Fill in entries between the old length and the new length with
         *   nil.  Note that we don't have to fill in the very last element,
         *   since we'll explicitly set it to the new value momentarily
         *   anyway.  Note also that we don't need to keep undo for the
         *   initializations, since on undo we'll truncate the vector to
         *   remove the newly-added elements and thus won't need to restore
         *   any values for the slots.  
         */
        for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i)
            set_element(i, &nil_val);

        /* 
         *   set the new value - this doesn't require undo since we had to
         *   expand the vector to make room for it 
         */
        set_element(idx - 1, new_val);
    }
    else
    {
        /* set the element and record undo, using a zero-based index */
        set_element_undo(vmg_ self, (size_t)idx - 1, new_val);
    }

    /* the result is the original vector value */
    new_container->set_obj(self);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   Set an element and record undo for the change - takes a C-style 0-based
 *   index.  
 */
void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self,
                                    size_t idx, const vm_val_t *new_val)
{
    /* if we don't have undo for this element already, save undo now */
    if (G_undo != 0 && !get_undo_bit(idx))
    {
        vm_val_t old_val;

        /* get the pre-modification value of this element */
        get_element(idx, &old_val);

        /* add the undo record */
        G_undo->add_new_record_int_key(vmg_ self, idx, &old_val);

        /* 
         *   set the undo bit to indicate that we now have undo for this
         *   element 
         */
        set_undo_bit(idx, TRUE);
    }

    /* get the indexed element and store it in the result */
    set_element(idx, new_val);
}

/* ------------------------------------------------------------------------ */
/* 
 *   Check a value for equality.  We will match any list or vector with the
 *   same number of elements and the same value for each element.  
 */
int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val,
                         int depth) const
{
    int cnt;
    int cnt2;
    int idx;

    /* if the recursion depth is excessive, throw an error */
    if (depth > VM_MAX_TREE_DEPTH_EQ)
        err_throw(VMERR_TREE_TOO_DEEP_EQ);

    /* if the other value is a reference to myself, we certainly match */
    if (val->typ == VM_OBJ && val->val.obj == self)
    {
        /* no need to look at the contents if this is a reference to me */
        return TRUE;
    }

    /* if it's not list-like, it's not a match */
    if (!val->is_listlike(vmg0_) || (cnt2 = val->ll_length(vmg0_)) < 0)
        return FALSE;

    /* if the sizes don't match, the values are not equal */
    cnt = get_element_count();
    if (cnt != cnt2)
        return FALSE;

    /* compare element by element */
    for (idx = 0 ; idx < cnt ; ++idx)
    {
        vm_val_t val1;
        vm_val_t val2;
        
        /* get this element of self */
        get_element(idx, &val1);

        /* get this element of the other value */
        val->ll_index(vmg_ &val2, idx + 1);
        
        /* if these elements aren't equal, our values aren't equal */
        if (!val1.equals(vmg_ &val2, depth + 1))
            return FALSE;
    }

    /* we didn't find any differences, so the values are equal */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   Hash value calculation 
 */
uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const
{
    uint hash;
    uint i;

    /* if the recursion depth is excessive, throw an error */
    if (depth > VM_MAX_TREE_DEPTH_EQ)
        err_throw(VMERR_TREE_TOO_DEEP_EQ);
    
    /* combine the hash values of the members of the vector */
    for (hash = 0, i = 0 ; i < get_element_count() ; ++i)
    {
        vm_val_t ele;
        
        /* get this element */
        get_element(i, &ele);

        /* 
         *   Calculate its hash value and add it into the combined hash.
         *   
         *   Note that we have to increase the recursion depth, because we're
         *   recursively calculating the hash value of our contents, and our
         *   contents can refer (directly or indirectly) back to this object.
         *   In other words, we can have cycles in the reference tree from
         *   this object back to itself, so we need to keep track of the
         *   recursion depth to ensure we don't loop forever.  
         */
        hash += ele.calc_hash(vmg_ depth + 1);
    }

    /* return the combined hash */
    return hash;
}

/* ------------------------------------------------------------------------ */
/* 
 *   add a value to the vector
 */
int CVmObjVector::add_val(VMG_ vm_val_t *result,
                          vm_obj_id_t self, const vm_val_t *val)
{
    int idx;
    int rhs_cnt, alo_cnt;
    int i;
    CVmObjVector *new_vec;

    /* push the value to append, for gc protection */
    G_stk->push(val);

    /* 
     *   remember the index of the first new element - this is simply one
     *   higher than the last valid current index 
     */
    idx = get_element_count();

    /* get the number of elements to add */
    rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1);

    /* if it's not list-like, just add it directly as a single element */
    alo_cnt = (rhs_cnt < 0 ? 1 : rhs_cnt);

    /* 
     *   allocate a copy of the vector for the result - make it big enough
     *   for my elements plus the elements to be appended 
     */
    result->set_obj(create(vmg_ FALSE, idx + alo_cnt));

    /* get the return value as a vector */
    new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);

    /* push the result for gc protection */
    G_stk->push(result);

    /* copy my elements into the result */
    memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
           calc_alloc_ele(idx));

    /* 
     *   set the element count to include the elements copied so far (it's a
     *   new vector, so there's no need to save undo) 
     */
    new_vec->set_element_count(idx);

    /* add the new element or elements */
    if (rhs_cnt < 0)
    {
        /* add a single element */
        new_vec->set_element(idx, val);
        new_vec->set_element_count(idx + 1);
    }
    else
    {
        /* add the contents of the rhs */
        for (i = 1 ; i <= rhs_cnt ; ++i, ++idx)
        {
            /* get this element from the right-hand side */
            vm_val_t ele;
            val->ll_index(vmg_ &ele, i);
            
            /* store the element in the result */
            new_vec->set_element(idx, &ele);
            new_vec->set_element_count(idx + 1);
        }
    }

    /* discard the gc protect */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   subtract a value from the vector
 */
int CVmObjVector::sub_val(VMG_ vm_val_t *result,
                          vm_obj_id_t self, const vm_val_t *val)
{
    int ele_cnt, rhs_cnt;
    int i;
    CVmObjVector *new_vec;
    size_t new_cnt;

    /* push the value to append, for gc protection */
    G_stk->push(val);

    /* note the initial element count */
    ele_cnt = get_element_count();

    /* get the number of elements to remove */
    rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1);

    /* 
     *   allocate a new vector for the result - the new object will be no
     *   larger than the current object, so allocate at the same size 
     */
    result->set_obj(create(vmg_ FALSE, ele_cnt));

    /* push the result for gc protection */
    G_stk->push(result);

    /* get the return value as a vector */
    new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);

    /* 
     *   copy each element from me to the new vector, but don't copy
     *   elements found in the right-hand side 
     */
    for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i)
    {
        vm_val_t ele;
        int found = FALSE;

        /* get this element of self */
        get_element(i, &ele);

        /* check the right-hand side for a match */
        if (rhs_cnt < 0)
        {
            /* single element - just check this element */
            if (val->equals(vmg_ &ele))
                found = TRUE;
        }
        else
        {
            /* scan the right side for the element */
            for (int j = 1 ; j <= rhs_cnt ; ++j)
            {
                /* get this right-hand element */
                vm_val_t rh_ele;
                val->ll_index(vmg_ &rh_ele, j);

                /* 
                 *   if this right-hand element matches this left-hand
                 *   element, note that we have a match 
                 */
                if (rh_ele.equals(vmg_ &ele))
                {
                    /* note that we found it */
                    found = TRUE;
                    
                    /* no need to look any further - one match is enough */
                    break;
                }
            }
        }

        /* 
         *   If we didn't find the value, include it in the result vector.
         *   Note that we don't have to save undo, since we're still
         *   constructing the new vector. 
         */
        if (!found)
        {
            /* store the element in the result vector */
            new_vec->set_element(new_cnt, &ele);

            /* count it */
            ++new_cnt;
        }
    }

    /* 
     *   update the new vector's size - we don't have to save undo for this
     *   change, because we're still constructing the new vector 
     */
    new_vec->set_element_count(new_cnt);

    /* discard the gc protection */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - convert to a list 
 */
int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval,
                               uint *argc)
{
    int src_cnt;
    int dst_cnt;
    int start_idx;
    CVmObjList *lst;
    int idx;
    uint orig_argc = (argc != 0 ? *argc : 0);
    static CVmNativeCodeDesc desc(0, 2);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* note our size */
    src_cnt = get_element_count();

    /* 
     *   if there's a starting index, retrieve it; otherwise, start at our
     *   first element 
     */
    if (orig_argc >= 1)
        start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
    else
        start_idx = 1;

    /* negative index values count from the end of the list */
    if (start_idx < 0)
        start_idx += src_cnt + 1;

    /* if the starting index is below 1, force it to 1 */
    if (start_idx < 1)
        start_idx = 1;

    /* adjust the starting index to a 0-based index */
    --start_idx;

    /* 
     *   if there's a size argument, retrieve it; if it's not specified,
     *   use our actual size for the output size 
     */
    if (orig_argc >= 2)
        dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_);
    else
        dst_cnt = src_cnt;

    /* 
     *   in no case will the result list have more elements than we can
     *   actually supply 
     */
    if (start_idx >= src_cnt)
    {
        /* we're starting past our last element - we can't supply anything */
        dst_cnt = 0;
    }
    else if (src_cnt - start_idx < dst_cnt)
    {
        /* we can't supply as many values as requested - lower the size */
        dst_cnt = src_cnt - start_idx;
    }

    /* push a self-reference for garbage collection protection */
    G_stk->push()->set_obj(self);

    /* create the new list */
    retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt));
    lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);

    /* set the list elements */
    for (idx = 0 ; idx < dst_cnt ; ++idx)
    {
        vm_val_t val;

        /* get my element at this index */
        get_element(idx + start_idx, &val);

        /* store the element in the list */
        lst->cons_set_element(idx, &val);
    }

    /* discard the self-reference */
    G_stk->discard();

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - copy from another vector or list 
 */
int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                 uint *argc)
{
    /* check arguments */
    static CVmNativeCodeDesc desc(4);
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* retrieve the source value */
    vm_val_t src_val;
    G_stk->pop(&src_val);

    /* make sure the source argument is list-like, and get its length */
    int src_cnt;
    if (!src_val.is_listlike(vmg0_)
        || (src_cnt = src_val.ll_length(vmg0_)) < 0)
        err_throw(VMERR_BAD_TYPE_BIF);

    /* get the source starting index, adjusting negatives to count from end */
    int src_start = CVmBif::pop_int_val(vmg0_);
    if (src_start < 0)
        src_start += src_cnt + 1;

    /* note the destination length (i.e., my element count) */
    int dst_cnt = get_element_count();

    /* get the destination starting index, adjusting negative values */
    int dst_start = CVmBif::pop_int_val(vmg0_);
    if (dst_start < 0)
        dst_start += dst_cnt + 1;

    /* get the number of elements to copy */
    int copy_cnt = CVmBif::pop_int_val(vmg0_);

    /* if either starting index is below 1, force it to 1 */
    if (src_start < 1)
        src_start = 1;
    if (dst_start < 1)
        dst_start = 1;

    /* adjust the starting indices to 0-based values */
    --src_start;
    --dst_start;

    /* limit our copying to the remaining elements of the source */
    if (src_start >= src_cnt)
        copy_cnt = 0;
    else if (src_start + copy_cnt >= src_cnt)
        copy_cnt = src_cnt - src_start;

    /* expand the vector if necessary to make room for added elements */
    if (dst_start + copy_cnt > dst_cnt)
        set_element_count_undo(vmg_ self, dst_start + copy_cnt);

    /* set the list elements */
    for (int i = 0 ; i < copy_cnt ; ++i)
    {
        /* get the source element at this index */
        vm_val_t val;
        src_val.ll_index(vmg_ &val, i + src_start + 1);

        /* set my element at this index, recording undo for the change */
        set_element_undo(vmg_ self, i + dst_start, &val);
    }

    /* the return value is 'self' */
    retval->set_obj(self);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - fill with a value 
 */
int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                uint *argc)
{
    /* check arguments */
    uint orig_argc = (argc != 0 ? *argc : 0);
    static CVmNativeCodeDesc desc(1, 2);
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* note our size */
    int cnt = get_element_count();

    /* the return value is 'self' */
    retval->set_obj(self);

    /* get the fill value */
    vm_val_t fill_val;
    G_stk->pop(&fill_val);

    /* get the starting index; the default is 1 if it's not specified */
    int start_idx = (orig_argc >= 2 ? CVmBif::pop_int_val(vmg0_) : 1);

    /* a negative index counts from the end of the vector */
    if (start_idx < 0)
        start_idx += cnt + 1;

    /* if the starting index is below 1, force it to 1 */
    if (start_idx < 1)
        start_idx = 1;

    /* adjust the starting index to a 0-based index */
    --start_idx;

    /* 
     *   if there's a count argument, retrieve it; if it's not specified,
     *   use our actual size for the count 
     */
    int end_idx = (orig_argc >= 3
                   ? start_idx + CVmBif::pop_int_val(vmg0_)
                   : cnt);

    /* push self and the fill value for gc protection */
    G_stk->push(retval);
    G_stk->push(&fill_val);

    /* expand the vector to the requested size */
    if (end_idx > cnt)
        set_element_count_undo(vmg_ self, end_idx);

    /* set the elements to the fill value */
    for (int idx = start_idx ; idx < end_idx ; ++idx)
    {
        /* set the element to the fill value, recording undo for the change */
        set_element_undo(vmg_ self, idx, &fill_val);
    }

    /* discard GC protection */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - get the number of elements 
 */
int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval,
                                uint *argc)
{
    static CVmNativeCodeDesc desc(0);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* return my element count */
    retval->set_int(get_element_count());

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - select a subset; returns a new vector consisting
 *   of the subset of the original vector 
 */
int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval,
                              uint *argc)
{
    const vm_val_t *func_val;
    size_t src;
    size_t dst;
    size_t cnt;
    CVmObjVector *new_vec;
    static CVmNativeCodeDesc desc(1);
    vm_rcdesc rc(vmg_ "Vector.subset", self, 5, G_stk->get(0), argc);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the function pointer argument, but leave it on the stack */
    func_val = G_stk->get(0);

    /* push a self-reference while allocating to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* 
     *   allocate the new vector that we'll return - all we know at this
     *   point is that the new vector won't be larger than the current
     *   vector, so allocate at the current size 
     */
    cnt = get_element_count();
    retval->set_obj(create(vmg_ FALSE, cnt));

    /* get the return value as an vector */
    new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);

    /* 
     *   push a reference to the new list to protect it from the garbage
     *   collector, which could be invoked in the course of executing the
     *   user callback 
     */
    G_stk->push(retval);

    /*
     *   Go through each element of our list, and invoke the callback on
     *   each element.  If the element passes, write it to the current
     *   output location in the list; otherwise, just skip it.
     *   
     *   Note that we're using the same list as source and destination,
     *   which is easy because the list will either shrink or stay the
     *   same - we'll never need to insert new elements.  
     */
    for (src = dst = 0 ; src < cnt ; ++src)
    {
        vm_val_t ele;
        const vm_val_t *val;

        /* get this element from the source vector */
        get_element(src, &ele);

        /* push the element as the callback's argument */
        G_stk->push(&ele);

        /* invoke the callback */
        G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0);

        /* get the result from R0 */
        val = G_interpreter->get_r0();

        /* 
         *   if the callback returned non-nil and non-zero, include this
         *   element in the result 
         */
        if (val->typ == VM_NIL
            || (val->typ == VM_INT && val->val.intval == 0))
        {
            /* it's nil or zero - don't include it in the result */
        }
        else
        {
            /* 
             *   include this element in the result (there's no need to
             *   save undo, since the whole vector is new) 
             */
            new_vec->set_element(dst, &ele);

            /* advance the output index */
            ++dst;
        }
    }

    /* set the actual number of elements in the result */
    new_vec->set_element_count(dst);

    /* discard our gc protection (self, return value) and our arguments */
    G_stk->discard(3);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - apply a callback to each element
 */
int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                 uint *argc)
{
    const vm_val_t *func_val;
    size_t idx;
    static CVmNativeCodeDesc desc(1);
    vm_rcdesc rc(vmg_ "Vector.applyAll", self, 6, G_stk->get(0), argc);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the function pointer argument, but leave it on the stack */
    func_val = G_stk->get(0);

    /* push a self-reference while working to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* 
     *   we're going to return the 'self' object, since we update the vector
     *   in-place 
     */
    retval->set_obj(self);

    /*
     *   Go through each element of our vector, invoking the callback on
     *   each element.  Replace each element with the result of the
     *   callback.  Note that we intentionally re-check the element count on
     *   each iteration, in case the callback changes the number of
     *   elements.  
     */
    for (idx = 0 ; idx < get_element_count() ; ++idx)
    {
        /* push this element as the callback's argument */
        push_element(vmg_ idx);

        /* invoke the callback */
        G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0);

        /* replace this element with the result */
        set_element_undo(vmg_ self, idx, G_interpreter->get_r0());
    }

    /* discard our gc protection (self) and our arguments */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - find the first element matching a condition
 */
int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                   uint *argc)
{
    /* use our general handler, working forwards */
    vm_rcdesc rc(vmg_ "Vector.indexWhich", self, 7, G_stk->get(0), argc);
    return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE, &rc);
}

/*
 *   property evaluator - lastIndexWhich 
 */
int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self,
                                        vm_val_t *retval, uint *argc)
{
    /* use our general handler, working backwards */
    vm_rcdesc rc(vmg_ "Vector.lastIndexWhich", self, 14, G_stk->get(0), argc);
    return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE, &rc);
}

/*
 *   general handler for indexWhich and lastIndexWhich 
 */
int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                  uint *argc, int forward, int count_only,
                                  vm_rcdesc *rc)
{
    const vm_val_t *func_val;
    size_t cnt;
    size_t idx;
    int val_cnt;
    static CVmNativeCodeDesc desc(1);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the function pointer argument, but leave it on the stack */
    func_val = G_stk->get(0);

    /* push a self-reference while working to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* presume we won't find a matching element */
    retval->set_nil();
    val_cnt = 0;

    /* get the number of elements to visit */
    cnt = get_element_count();

    /* start at the first or last element, depending on direction */
    idx = (forward ? 0 : cnt);

    /*
     *   Go through each element of our vector, and invoke the callback on
     *   each element, looking for the first one that returns true.  
     */
    for (;;)
    {
        /* if we've reached the last element, we can stop looking */
        if (forward ? idx >= cnt : idx == 0)
            break;

        /* if we're going backwards, decrement the index */
        if (!forward)
            --idx;

        /* push the element as the callback's argument */
        push_element(vmg_ idx);

        /* invoke the callback */
        G_interpreter->call_func_ptr(vmg_ func_val, 1, rc, 0);

        /* 
         *   if the callback returned true, we've found the element we're
         *   looking for 
         */
        if (G_interpreter->get_r0()->typ == VM_NIL
            || (G_interpreter->get_r0()->typ == VM_INT
                && G_interpreter->get_r0()->val.intval == 0))
        {
            /* nil or zero - this one failed the test, so keep looking */
        }
        else
        {
            /* it passed the test - check what we're doing */
            if (count_only)
            {
                /* we only want the count */
                ++val_cnt;
            }
            else
            {
                /* we want the (1-based) index - return it */
                retval->set_int(idx + 1);
            
                /* found it - no need to keep looking */
                break;
            }
        }

        /* if we're going forwards, increment the index */
        if (forward)
            ++idx;
    }
    
    /* discard our gc protection (self) and our arguments */
    G_stk->discard(2);

    /* return the count if desired */
    if (count_only)
        retval->set_int(val_cnt);
    
    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - lastValWhich 
 */
int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self,
                                      vm_val_t *retval, uint *argc)
{
    /* get the index of the value using lastIndexWhich */
    getp_last_index_which(vmg_ self, retval, argc);

    /* if the return value is a valid index, get the value at the index */
    if (retval->typ == VM_INT)
    {
        int idx;
        
        /* get the element as the return value */
        idx = (int)retval->val.intval;
        get_element(idx - 1, retval);
    }

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - valWhich 
 */
int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                 uint *argc)
{
    /* get the index of the value using indexWhich */
    getp_index_which(vmg_ self, retval, argc);

    /* if the return value is a valid index, get the value at the index */
    if (retval->typ == VM_INT)
    {
        int idx;

        /* get the element as the return value */
        idx = (int)retval->val.intval;
        get_element(idx - 1, retval);
    }

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - call a callback for each element
 */
int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                uint *argc)
{
    /* use the general forEach processor */
    vm_rcdesc rc(vmg_ "Vector.forEach", self, 8, G_stk->get(0), argc);
    return for_each_gen(vmg_ self, retval, argc, FALSE, &rc);
}

/*
 *   property evaluator - call a callback for each element 
 */
int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self,
                                      vm_val_t *retval, uint *argc)
{
    /* use the general forEach processor */
    vm_rcdesc rc(vmg_ "Vector.forEachAssoc", self, 9, G_stk->get(0), argc);
    return for_each_gen(vmg_ self, retval, argc, TRUE, &rc);
}


/*
 *   General forEach/forEachAssoc processor 
 */
int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self,
                               vm_val_t *retval, uint *argc,
                               int pass_key_to_cb, vm_rcdesc *rc)
{
    const vm_val_t *func_val;
    size_t idx;
    static CVmNativeCodeDesc desc(1);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the function pointer argument, but leave it on the stack */
    func_val = G_stk->get(0);

    /* push a self-reference while working to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* no return value */
    retval->set_nil();

    /* 
     *   Invoke the callback on each element.  Note that we intentionally do
     *   not cache the element count, since it is possible that a program
     *   could change the vector size in the course of an iteration; if we
     *   cached the size, and the actual size were reduced during the
     *   iteration, we would visit invalid elements past the new end of the
     *   vector.  To avoid this possibility, we re-check the current element
     *   count on each iteration to make sure we haven't run off the end of
     *   the vector.  
     */
    for (idx = 0 ; idx < get_element_count() ; ++idx)
    {
        /* push the element as the callback's argument */
        push_element(vmg_ idx);

        /* push the index argument if desired */
        if (pass_key_to_cb)
            G_stk->push()->set_int(idx + 1);

        /* invoke the callback */
        G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1,
                                     rc, 0);
    }

    /* discard our gc protection (self) and our arguments */
    G_stk->discard(2);
    
    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - mapAll
 */
int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
                               uint *argc)
{
    const vm_val_t *func_val;
    size_t idx;
    CVmObjVector *new_vec;
    static CVmNativeCodeDesc desc(1);
    vm_rcdesc rc(vmg_ "Vector.mapAll", self, 10, G_stk->get(0), argc);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the function pointer argument, but leave it on the stack */
    func_val = G_stk->get(0);

    /* push a self-reference while working to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* 
     *   allocate a new vector for the return value - the new vector will
     *   have the same size as the original, since we're mapping each
     *   element of the old vector to the corresponding element of the new
     *   vector 
     */
    retval->set_obj(create(vmg_ FALSE, get_allocated_count()));

    /* get the return value as an vector */
    new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);

    /* 
     *   push a reference to the new list to protect it from the garbage
     *   collector, which could be invoked in the course of executing the
     *   user callback 
     */
    G_stk->push(retval);

    /*
     *   Go through each element of our vector, and invoke the callback on
     *   each element, storing the result in the corresponding element of
     *   the new vector.  Note that we re-check the element count on each
     *   iteration, in case the callback changes it on us.  
     */
    for (idx = 0 ; idx < get_element_count() ; ++idx)
    {
        /* push the element as the callback's argument */
        push_element(vmg_ idx);

        /* invoke the callback */
        G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0);

        /* 
         *   replace this element with the result (there's no need to save
         *   undo, since the whole vector is new) 
         */
        new_vec->set_element(idx, G_interpreter->get_r0());
        new_vec->set_element_count(idx + 1);
    }

    /* discard our gc protection (self, new vector) and our arguments */
    G_stk->discard(3);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - indexOf 
 */
int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                uint *argc)
{
    /* use our general handler, going forwards */
    return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE);
}

/*
 *   property evaluator - lastIndexOf 
 */
int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                     uint *argc)
{
    /* use our general handler, going backwards */
    return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE);
}

/*
 *   general handler for indexOf and lastIndexOf - searches the vector
 *   either forwards of backwards for a given value 
 */
int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
                               uint *argc, int forward, int count_only)
{
    const vm_val_t *val;
    size_t cnt;
    size_t idx;
    int val_cnt;
    static CVmNativeCodeDesc desc(1);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the value, but leave it on the stack */
    val = G_stk->get(0);

    /* push a self-reference while working to protect from gc */
    G_interpreter->push_obj(vmg_ self);

    /* presume we won't find a matching element */
    retval->set_nil();
    val_cnt = 0;

    /* get the number of elements to visit */
    cnt = get_element_count();

    /* start at the first or last element, depending on the direction */
    idx = (forward ? 0 : cnt);

    /* scan the list, looking for the element */
    for (;;)
    {
        vm_val_t ele;

        /* if we've reached the last element, stop looking */
        if (forward ? idx >= cnt : idx == 0)
            break;

        /* if we're going backwards, move to the next element position */
        if (!forward)
            --idx;

        /* get this element */
        get_element(idx, &ele);

        /* if the element matches the search value, return its index */
        if (ele.equals(vmg_ val))
        {
            /* it matches - see what we're doing */
            if (count_only)
            {
                /* they only want the count */
                ++val_cnt;
            }
            else
            {
                /* this is the one - return its 1-based index */
                retval->set_int(idx + 1);
                
                /* foind it - no need to keep searching */
                break;
            }
        }

        /* if we're going forwards, move to the next element */
        if (forward)
            ++idx;
    }

    /* discard our gc protection (self) and our arguments */
    G_stk->discard(2);

    /* return the count if desired */
    if (count_only)
        retval->set_int(val_cnt);
    
    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - countOf
 */
int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                uint *argc)
{
    /* use our general handler to obtain the count */
    return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE);
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - countWhich
 */
int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                   uint *argc)
{
    /* use our general handler to obtain the count */
    vm_rcdesc rc(vmg_ "Vector.countWhich", self, 17, G_stk->get(0), argc);
    return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE, &rc);
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - getUnique; returns a new vector consisting of the
 *   unique elements of the original vector 
 */
int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                  uint *argc)
{
    CVmObjVector *new_vec;
    size_t cnt;
    static CVmNativeCodeDesc desc(0);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* put myself on the stack for GC protection */
    G_interpreter->push_obj(vmg_ self);

    /* 
     *   allocate a new vector for the return value - the new vector will
     *   never be any larger than the original 
     */
    cnt = get_element_count();
    retval->set_obj(create(vmg_ FALSE, cnt));

    /* push a reference to the new list for gc protection */
    G_stk->push(retval);

    /* get the return value as a vector */
    new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);

    /* start out with the result element count the same as my own */
    new_vec->set_element_count(cnt);

    /* copy my elements to the new vector */
    memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
           calc_alloc_ele(cnt));

    /* uniquify the result */
    new_vec->cons_uniquify(vmg0_);

    /* discard the gc protection */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/*
 *   For construction, eliminate repeated elements of the vector, leaving
 *   only the unique elements.  Reduces the size of the vector to the size
 *   required to accommodate the unique elements.  
 */
void CVmObjVector::cons_uniquify(VMG0_)
{
    size_t cnt;
    size_t src, dst;

    /* get the number of elements */
    cnt = get_element_count();

    /* loop through the list and look for repeated values */
    for (src = dst = 0 ; src < cnt ; ++src)
    {
        size_t idx;
        vm_val_t src_val;
        int found;

        /* 
         *   look for a copy of this source value already in the output list 
         */
        get_element(src, &src_val);
        for (idx = 0, found = FALSE ; idx < dst ; ++idx)
        {
            vm_val_t idx_val;

            /* get this value */
            get_element(idx, &idx_val);

            /* if it's equal to the current source value, note it */
            if (src_val.equals(vmg_ &idx_val))
            {
                /* note that we found it */
                found = TRUE;

                /* no need to look any further */
                break;
            }
        }

        /* if we didn't find the value, copy it to the output list */
        if (!found)
        {
            /* 
             *   add it to the output vector - since this is a
             *   construction-only method, there's no need to save undo (if
             *   we apply undo, we'll undo the entire construction of the
             *   vector, hence there's no need to track changes made since
             *   creation) 
             */
            set_element(dst, &src_val);

            /* count it in the output */
            ++dst;
        }
    }

    /* adjust the size of the result list */
    set_element_count(dst);
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - appendUnique 
 */
int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                     uint *argc)
{
    vm_val_t *val2;
    int cnt2;
    int cnt;
    static CVmNativeCodeDesc desc(1);
    int i;

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the other value, but leave it on the stack */
    val2 = G_stk->get(0);

    /* the return value is 'self' */
    retval->set_obj(self);

    /* put myself on the stack for GC protection */
    G_stk->push(retval);

    /* get the number of elements in the original */
    cnt = get_element_count();

    /* get the number of elements to append */
    cnt2 = (val2->is_listlike(vmg0_) ? val2->ll_length(vmg0_) : -1);

    /* expand myself to make room for the new elements */
    expand_by(vmg_ self, cnt2 < 0 ? 1 : cnt2);

    /* append each element of the right-hand side */
    if (cnt2 < 0)
    {
        /* the rhs isn't a list, so just append it directly */
        set_element_undo(vmg_ self, cnt, val2);
    }
    else
    {
        /* append each element of the rhs list */
        for (i = 1 ; i <= cnt2 ; ++i)
        {
            /* get this element */
            vm_val_t ele;
            val2->ll_index(vmg_ &ele, i);

            /* store it */
            set_element_undo(vmg_ self, cnt + i - 1, &ele);
        }
    }
    
    /* uniquify the result */
    uniquify_for_append(vmg_ self);

    /* discard the gc protection and arguments */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* 
 *   uniquify after an appendUnique operation 
 */
void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self)
{
    size_t cnt;
    size_t src, dst;

    /* get the number of elements */
    cnt = get_element_count();

    /* loop through the list and look for repeated values */
    for (src = dst = 0 ; src < cnt ; ++src)
    {
        size_t idx;
        vm_val_t src_val;
        int found;

        /* look for a copy of this value elsewhere in the vector */
        get_element(src, &src_val);
        for (idx = 0, found = FALSE ; idx < dst ; ++idx)
        {
            vm_val_t idx_val;

            /* get this value */
            get_element(idx, &idx_val);

            /* if it's equal to the current source value, note it */
            if (src_val.equals(vmg_ &idx_val))
            {
                /* note that we found it */
                found = TRUE;

                /* no need to look any further */
                break;
            }
        }

        /* if we didn't find the value, copy it to the output list */
        if (!found)
        {
            /* 
             *   Add it to the output vector if we're actually changing this
             *   element (i.e., the destination and source indices are
             *   unequal).  
             */
            if (dst != src)
                set_element_undo(vmg_ self, dst, &src_val);

            /* count it in the output */
            ++dst;
        }
    }

    /* adjust the size of the result list */
    set_element_count_undo(vmg_ self, dst);
}

/* ------------------------------------------------------------------------ */
/*
 *   sorter for vector
 */
class CVmQSortVector: public CVmQSortVal
{
public:
    CVmQSortVector()
    {
        vec_ = 0;
        self_ = VM_INVALID_OBJ;
    }

    /* get an element from the vector */
    void get_ele(VMG_ size_t idx, vm_val_t *val)
        { vec_->get_element(idx, val); }

    /* store an element */
    void set_ele(VMG_ size_t idx, const vm_val_t *val)
        { vec_->set_element_undo(vmg_ self_, idx, val); }

    /* our vector object */
    CVmObjVector *vec_;
    vm_obj_id_t self_;
};

/*
 *   property evaluator - sort 
 */
int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval,
                            uint *in_argc)
{
    size_t len;
    uint argc = (in_argc == 0 ? 0 : *in_argc);
    CVmQSortVector sorter;
    static CVmNativeCodeDesc desc(0, 2);

    /* check arguments */
    if (get_prop_check_argc(retval, in_argc, &desc))
        return TRUE;

    /* remember the length of my list */
    len = get_element_count();

    /* set the vector object in the sorter */
    sorter.vec_ = this;
    sorter.self_ = self;

    /* if we have an 'descending' flag, note it */
    if (argc >= 1)
        sorter.descending_ = (G_stk->get(0)->typ != VM_NIL);

    /* 
     *   if we have a comparison function, note it, but leave it on the
     *   stack for gc protection 
     */
    if (argc >= 2)
    {
        /* get the function */
        sorter.compare_fn_ = *G_stk->get(1);

        /* initialize the recursive caller descriptor for it */
        sorter.rc.init(vmg_ "Vector.sort", self, 20, G_stk->get(0), argc);
    }

    /* put myself on the stack for GC protection */
    G_interpreter->push_obj(vmg_ self);

    /* sort the vector, if we have any elements */
    if (len != 0)
        sorter.sort(vmg_ 0, len - 1);

    /* discard the gc protection and arguments */
    G_stk->discard(1 + argc);

    /* return myself */
    retval->set_obj(self);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - set the length 
 */
int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                  uint *argc)
{
    size_t old_len;
    int32_t new_len;
    int32_t idx;
    vm_val_t nil_val;
    static CVmNativeCodeDesc desc(1);
    
    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* the return value is 'self' */
    retval->set_obj(self);

    /* note the old length */
    old_len = get_element_count();

    /* get the new length */
    new_len = CVmBif::pop_long_val(vmg0_);

    /* can't go less than zero */
    if (new_len < 0 || new_len >= VEC_MAX_ELEMENTS)
        err_throw(VMERR_BAD_VAL_BIF);

    /* set the vector to its new size */
    set_element_count_undo(vmg_ self, (size_t)new_len);

    /* 
     *   Set each newly added element to nil.  Note that we don't bother
     *   saving undo for these changes: to undo this change, the only thing
     *   we'll have to do is reduce the vector size to its previous size,
     *   and we've already saved undo for the size change.  
     */
    nil_val.set_nil();
    for (idx = old_len ; idx < new_len ; ++idx)
        set_element((size_t)idx, &nil_val);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - insert one or more elements at a given index
 */
int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                 uint *in_argc)
{
    /* we must have at least two arguments */
    uint argc = (in_argc != 0 ? *in_argc : 0);
    static CVmNativeCodeDesc desc(2, 0, TRUE);
    if (get_prop_check_argc(retval, in_argc, &desc))
        return TRUE;

    /* note the original size of the vector */
    int old_cnt = get_element_count();

    /* 
     *   get the starting insertion index; negatives count from the end, and
     *   zero means to insert after the last existing element 
     */
    int ins_idx = CVmBif::pop_int_val(vmg0_);
    if (ins_idx <= 0)
        ins_idx += old_cnt + 1;

    /* the return value is 'self' */
    retval->set_obj(self);

    /* 
     *   note the number of items we're adding - we're adding each
     *   argument, other than the first 
     */
    int add_cnt = argc - 1;

    /* 
     *   if the starting index is out of range, it's an error - it can
     *   range from 1 to one higher than the current element count (the
     *   top end of the range allows us to insert elements after all of
     *   the current elements) 
     */
    if (ins_idx < 1 || ins_idx > old_cnt + 1)
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* adjust the starting index to a zero-based value */
    --ins_idx;

    /* do the insertion */
    insert_elements_undo(vmg_ self, ins_idx, add_cnt);

    /* set the new element values */
    for (int idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt)
    {
        /* pop the next argument value */
        vm_val_t val;
        G_stk->pop(&val);

        /* store the item, keeping undo only if it's in the old size range */
        if (idx < old_cnt)
            set_element_undo(vmg_ self, idx, &val);
        else
            set_element(idx, &val);
    }

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - append an element 
 */
int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval,
                               uint *argc)
{
    /* check arguments */
    static CVmNativeCodeDesc desc(1);
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* insert one element at the start of the vector */
    insert_elements_undo(vmg_ self, 0, 1);

    /* 
     *   set the first element - note that there's no need to save undo,
     *   since undoing this operation will simply discard this element (in
     *   other words, there's no previous value for this element since it's
     *   being created anew here) 
     */
    set_element(0, G_stk->get(0));

    /* the return value is 'self' */
    retval->set_obj(self);

    /* discard arguments */
    G_stk->discard(1);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - remove an element at the given position
 */
int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self,
                                         vm_val_t *retval, uint *argc)
{
    /* check arguments */
    static CVmNativeCodeDesc desc(1);
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the deletion index; a negative index counts from the end */
    int start_idx = CVmBif::pop_int_val(vmg0_);
    if (start_idx < 0)
        start_idx += get_element_count() + 1;

    /* make sure the index is in range */
    if (start_idx < 1 || start_idx > (int)get_element_count())
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* adjust to a zero-based index */
    --start_idx;

    /* the return value is 'self' */
    retval->set_obj(self);

    /* delete one element */
    remove_elements_undo(vmg_ self, start_idx, 1);


    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - remove element(s) matching a given value 
 */
int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self,
                                      vm_val_t *retval, uint *argc)
{
    vm_val_t *del_val;
    static CVmNativeCodeDesc desc(1);
    size_t src;
    size_t dst;
    size_t old_cnt;

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the value to be removed */
    del_val = G_stk->get(0);

    /* the return value is 'self'; leave it on the stack */
    retval->set_obj(self);
    G_stk->push(retval);

    /* get the original number of elements */
    old_cnt = get_element_count();

    /* delete each element matching the given value */
    for (src = dst = 0 ; src < old_cnt ; ++src)
    {
        vm_val_t ele_val;

        /* get this element */
        get_element(src, &ele_val);

        /* 
         *   if this element doesn't match the value to be deleted, add it
         *   to the result vector 
         */
        if (!ele_val.equals(vmg_ del_val))
        {
            /* 
             *   This element is a keeper - if we're moving it to a new
             *   position (i.e., we've deleted any elements before this
             *   one), store it at its new position, keeping undo
             *   information.  If we're storing it at its same position,
             *   there's no need to re-store the value or keep any undo,
             *   since no change is involved.  
             */
            if (dst != src)
                set_element_undo(vmg_ self, dst, &ele_val);

            /* increment the write index past this element */
            ++dst;
        }
    }

    /* if the element count changed, set the new element count */
    if (dst != old_cnt)
        set_element_count_undo(vmg_ self, dst);

    /* discard gc protection and arguments */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   property evaluator - remove a range of elements
 */
int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self,
                                    vm_val_t *retval, uint *argc)
{
    int start_idx;
    int end_idx;
    static CVmNativeCodeDesc desc(2);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the starting index; a negative value counts from the end */
    start_idx = CVmBif::pop_int_val(vmg0_);
    if (start_idx < 0)
        start_idx += get_element_count() + 1;

    /* 
     *   make sure the index is in range - it must refer to an existing
     *   element 
     */
    if (start_idx < 1 || start_idx > (int)get_element_count())
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* get the ending index, and adjust for negative values */
    end_idx = CVmBif::pop_int_val(vmg0_);
    if (end_idx < 0)
        end_idx += get_element_count() + 1;

    /* 
     *   make sure it's in range - it must refer to an existing element,
     *   and it must be greater than or equal to the starting index 
     */
    if (end_idx < start_idx || end_idx > (int)get_element_count())
        err_throw(VMERR_INDEX_OUT_OF_RANGE);

    /* adjust to zero-based indices */
    --start_idx;
    --end_idx;

    /* the return value is 'self' */
    retval->set_obj(self);

    /* 
     *   delete the elements - the number of elements we're deleting is
     *   one higher than the difference of the starting and ending indices
     *   (if the two indices are the same, we're deleting just the one
     *   element) 
     */
    remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - append an element 
 */
int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval,
                              uint *argc)
{
    vm_val_t *valp;
    size_t cnt;
    static CVmNativeCodeDesc desc(1);

    /* check arguments */
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the value to append, but leave it on the stack */
    valp = G_stk->get(0);

    /* get the old size */
    cnt = get_element_count();

    /* the result object is 'self' */
    retval->set_obj(self);

    /* push a self-reference for gc protection */
    G_stk->push(retval);

    /* expand myself by one element to make room for the addition */
    expand_by(vmg_ self, 1);

    /* add the new element, saving undo */
    set_element_undo(vmg_ self, cnt, valp);

    /* discard the argument and gc protection */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - append elements from a collection, or append a
 *   single non-collection element 
 */
int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
                                  uint *argc)
{
    /* check arguments */
    static CVmNativeCodeDesc desc(1);
    if (get_prop_check_argc(retval, argc, &desc))
        return TRUE;

    /* get the value to append, but leave it on the stack */
    vm_val_t *valp = G_stk->get(0);

    /* the result object is 'self' */
    retval->set_obj(self);

    /* push a self-reference for gc protection */
    G_stk->push(retval);

    /* get the old size */
    int cnt = get_element_count();

    /* get the number of items to add */
    int add_cnt = valp->ll_length(vmg0_);

    /* expand myself to make room for the new elements */
    expand_by(vmg_ self, add_cnt);

    /* add the new elements */
    for (int i = 1 ; i <= add_cnt ; ++i)
    {
        /* get the next new element */
        vm_val_t ele;
        valp->ll_index(vmg_ &ele, i);
        
        /* 
         *   add the new element - note that we don't need to keep undo
         *   because we created this new element in this same operation, and
         *   hence undoing the operation will truncate the vector to exclude
         *   the element, and hence we don't need an old value for the
         *   element 
         */
        set_element(cnt + i - 1, &ele);
    }

    /* discard the argument and gc protection */
    G_stk->discard(2);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/* 
 *   property evaluator - splice
 */
int CVmObjVector::getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval,
                              uint *oargc)
{
    /* check arguments */
    uint argc = (oargc != 0 ? *oargc : 0);
    static CVmNativeCodeDesc desc(2, 0, TRUE);
    if (get_prop_check_argc(retval, oargc, &desc))
        return TRUE;

    /* get the current vector size */
    int old_cnt = get_element_count();

    /* get the starting index, and adjust a zero or negative index */
    int start_idx = CVmBif::pop_int_val(vmg0_);
    if (start_idx <= 0)
        start_idx += old_cnt + 1;

    /* adjust to a zero-based starting index */
    --start_idx;

    /* make sure the starting index is in range */
    if (start_idx < 0 || start_idx > old_cnt)
        err_throw(VMERR_INDEX_OUT_OF_RANGE);;

    /* get the deletion count */
    int del_cnt = CVmBif::pop_int_val(vmg0_);

    /* adjust the deletion count to the number of elements remaining */
    if (del_cnt > old_cnt - start_idx)
        del_cnt = old_cnt - start_idx;

    /* get a pointer to the first inserted item, and the number of items */
    vm_val_t *ins = G_stk->get(0);
    int ins_cnt = argc - 2;

    /* the result object is 'self'; push self for gc protection */
    retval->set_obj(self);
    G_stk->push(retval);

    /* 
     *   If we're doing a net deletion, contract the vector.  If we're doing
     *   a net expansion, expand it. 
     */
    if (del_cnt > ins_cnt)
    {
        /* net deletion - remove the excess of deletions over insertions */
        remove_elements_undo(
            vmg_ self, start_idx + ins_cnt, del_cnt - ins_cnt);
    }
    else if (del_cnt < ins_cnt)
    {
        /* net insertion - add room for the excess inserted items */
        insert_elements_undo(
            vmg_ self, start_idx + del_cnt, ins_cnt - del_cnt);
    }

    /* set the new elements */
    for (int i = 0 ; i < ins_cnt ; ++i, --ins)
    {
        /* 
         *   If this element is within the old vector's size range, save
         *   undo, since we're overwriting a pre-existing element.  If it's
         *   beyond the end of the old vector, there's no need to keep undo
         *   since the overall undo operation will simply shrink the vector
         *   to eliminate the slot. 
         */
        if (i < old_cnt)
            set_element_undo(vmg_ self, start_idx + i, ins);
        else
            set_element(start_idx + i, ins);
    }

    /* discard the arguments and gc protection */
    G_stk->discard(ins_cnt + 1);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   Insert room for new elements.  Inserts the given number of elements at
 *   the given starting index.  Leaves the old elements in the added slots
 *   unchanged; the caller is responsible for setting the element values.  
 */
void CVmObjVector::insert_elements_undo(VMG_ vm_obj_id_t self,
                                        int ins_idx, int add_cnt)
{
    /* note the new and old sizes */
    int old_cnt = get_element_count();
    int new_cnt = old_cnt + add_cnt;

    /* create the new elements */
    set_element_count_undo(vmg_ self, new_cnt);

    /* 
     *   Move the existing elements to their new slots.  Start with the
     *   starting index, and move it to its new position, since the starting
     *   index is the first element that needs to be moved.  Note that we
     *   start at the top and work down, to avoid overwriting any overlapping
     *   part of the new position block and the old position block.
     *   
     *   If we're inserting after the last element, there's no moving that
     *   needs to be done.  
     */
    if (ins_idx < old_cnt)
    {
        for (int idx = old_cnt - 1 ; idx >= ins_idx ; --idx)
        {
            /* get the value at this slot */
            vm_val_t ele;
            get_element(idx, &ele);

            /* calculate the new slot */
            int new_idx = idx + add_cnt;

            /* 
             *   if the new index is within the old size range, keep undo for
             *   the change; otherwise, no undo is necessary, since when we
             *   undo we'll be completely dropping this new slot 
             */
            if (new_idx < old_cnt)
                set_element_undo(vmg_ self, new_idx, &ele);
            else
                set_element(new_idx, &ele);
        }
    }
}


/*
 *   Delete a range of elements 
 */
void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self,
                                        size_t start_idx, size_t del_cnt)
{
    /* note the original size of the vector */
    size_t cnt = get_element_count();

    /* move all of the elements to their new slots, keeping undo */
    for (size_t idx = start_idx ; idx < cnt ; ++idx)
    {
        /* 
         *   calculate the index of the element to replace this one - it's
         *   past us by the number of items being deleted 
         */
        size_t move_idx = idx + del_cnt;

        /* 
         *   if we're moving from a valid index in the old vector, get the
         *   element; otherwise, replace the value with nil (but still
         *   replace the value, since we want to keep undo for its original
         *   value) 
         */
        vm_val_t val;
        if (move_idx < cnt)
            get_element(move_idx, &val);
        else
            val.set_nil();

        /* store the element at its new position */
        set_element_undo(vmg_ self, idx, &val);
    }

    /*
     *   Reduce the size, keeping undo.  Note that it's important we do
     *   this last: undo is applied in reverse order, so when applying
     *   undo, we first want to increase the vector's size to its original
     *   size, then we want to apply the element value restorations.
     */
    set_element_count_undo(vmg_ self, cnt - del_cnt);
}

/* ------------------------------------------------------------------------ */
/*
 *   Property evaluator - join into a string 
 */
int CVmObjVector::getp_join(VMG_ vm_obj_id_t self, vm_val_t *retval,
                            uint *argc)
{
    /* use the generic list joiner */
    vm_val_t vself;
    vself.set_obj(self);
    return CVmObjList::getp_join(vmg_ retval, &vself, 0, argc);
}

/*
 *   C++ interface to join() 
 */
void CVmObjVector::join(VMG_ vm_val_t *retval, vm_obj_id_t self,
                        const char *sep, size_t sep_len) const
{
    vm_val_t vself;
    vself.set_obj(self);
    return CVmObjList::join(vmg_ retval, &vself, sep, sep_len);
}

/* ------------------------------------------------------------------------ */
/*
 *   Explicitly convert to a string 
 */
const char *CVmObjVector::explicit_to_string(
    VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const
{
    /* use the generic list converter */
    vm_val_t vself;
    vself.set_obj(self);
    return CVmObjList::list_to_string(vmg_ new_str, &vself, radix, flags);
}

/* ------------------------------------------------------------------------ */
/*
 *   Property evaluator - generate a new vector
 */
int CVmObjVector::static_getp_generate(VMG_ vm_val_t *retval, uint *in_argc)
{
    /* check arguments */
    uint argc = (in_argc == 0 ? 0 : *in_argc);
    static CVmNativeCodeDesc desc(2);
    if (get_prop_check_argc(retval, in_argc, &desc))
        return TRUE;

    /* get the callback function argument */
    vm_val_t *func = G_stk->get(0);

    /* make sure it's a function; set up a header pointer if it is */
    CVmFuncPtr funcdesc;
    if (!funcdesc.set(vmg_ func))
        err_throw(VMERR_BAD_TYPE_BIF);

    /* get the count */
    int32_t cnt = G_stk->get(1)->num_to_int(vmg0_);

    /* make sure it's not negative */
    if (cnt < 0)
        err_throw(VMERR_BAD_VAL_BIF);

    /* create the vector */
    retval->set_obj(CVmObjVector::create(vmg_ FALSE, cnt));
    CVmObjVector *vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
    
    /* push the new vector for gc protection */
    G_stk->push(retval);
    
    /* find out how many arguments the function wants */
    int fargc = funcdesc.is_varargs() ? -1 : funcdesc.get_max_argc();

    /* set up our recursive call descriptor */
    vm_rcdesc rc(vmg_ "Vector.generate",
                 CVmObjVector::metaclass_reg_->get_class_obj(vmg0_),
                 PROPIDX_generate, func, argc);

    /* generate the elements */
    for (int i = 0 ; i < cnt ; ++i)
    {
        /* push the callback arguments */
        const int pushed_argc = 1;
        G_stk->push()->set_int(i+1);
        
        /* adjust the arguments for what the callback actually wants */
        int sent_argc = fargc < 0 || fargc > pushed_argc ? pushed_argc : fargc;
        
        /* call the callback */
        G_interpreter->call_func_ptr(vmg_ func, sent_argc, &rc, 0);
        
        /* discard excess arguments */
        G_stk->discard(pushed_argc - sent_argc);
        
        /* add the result to the list */
        vec->set_element(i, G_interpreter->get_r0());
        vec->set_element_count(i+1);
    }

    /* discard arguments and gc protection */
    G_stk->discard(argc + 1);

    /* handled */
    return TRUE;
}

/* ------------------------------------------------------------------------ */
/*
 *   Property evaluator: get the index of the element with the minimum value 
 */
int CVmObjVector::getp_indexOfMin(VMG_ vm_obj_id_t self,
                                  vm_val_t *retval, uint *argc)
{
    /* do the general calculation to select the minimum value ("-1") */
    vm_rcdesc rc(vmg_ "Vector.indexOfMin", self, 32, G_stk->get(0),
                 argc != 0 ? *argc : 0);
    return get_minmax(vmg_ self, retval, argc, &rc, -1, TRUE);
}

/*
 *   Property evaluator: get the value of the element with the minimum value 
 */
int CVmObjVector::getp_minVal(VMG_ vm_obj_id_t self,
                              vm_val_t *retval, uint *argc)
{
    /* do the general calculation to select the minimum value ("-1") */
    vm_rcdesc rc(vmg_ "Vector.minVal", self, 33, G_stk->get(0),
                 argc != 0 ? *argc : 0);
    return get_minmax(vmg_ self, retval, argc, &rc, -1, FALSE);
}

/*
 *   Property evaluator: get the index of the element with the maximum value 
 */
int CVmObjVector::getp_indexOfMax(VMG_ vm_obj_id_t self,
                                  vm_val_t *retval, uint *argc)
{
    /* do the general calculation to select the maximum value ("+1") */
    vm_rcdesc rc(vmg_ "Vector.indexOfMax", self, 34, G_stk->get(0),
                 argc != 0 ? *argc : 0);
    return get_minmax(vmg_ self, retval, argc, &rc, 1, TRUE);
}

/*
 *   Property evaluator: get the value of the element with the maximum value 
 */
int CVmObjVector::getp_maxVal(VMG_ vm_obj_id_t self,
                              vm_val_t *retval, uint *argc)
{
    /* do the general calculation to select the maximum value ("+1") */
    vm_rcdesc rc(vmg_ "Vector.maxVal", self, 35, G_stk->get(0),
                 argc != 0 ? *argc : 0);
    return get_minmax(vmg_ self, retval, argc, &rc, 1, FALSE);
}

/*
 *   General handling for minIndex, minVal, maxIndex, and maxVal.  This finds
 *   the highest/lowest value in the list, optionally mapped through a
 *   callback function.  Returns the winning element's index and value.
 *   
 *   'sense' is the sense of the search: -1 for minimum, 1 for maximum.
 *   'want_index' is true if 'retval' should be set to the index of the
 *   winning element, false if 'retval' should be set to the winning
 *   element's value.  
 */
int CVmObjVector::get_minmax(VMG_ vm_obj_id_t self, vm_val_t *retval,
                             uint *in_argc, const vm_rcdesc *rc,
                             int sense, int want_index)
{
    /* check arguments */
    uint argc = (in_argc == 0 ? 0 : *in_argc);
    static CVmNativeCodeDesc desc(0, 1);
    if (get_prop_check_argc(retval, in_argc, &desc))
        return TRUE;

    /* make sure the 'func' argument is valid */
    const vm_val_t *cb = 0;
    if (argc >= 1)
    {
        cb = G_stk->get(0);
        if (!cb->is_func_ptr(vmg0_))
            err_throw(VMERR_BAD_TYPE_BIF);
    }

    /* push 'self' for gc protection */
    G_stk->push()->set_obj(self);

    /* if there are no elements, this is an error */
    int cnt = get_element_count();
    if (cnt == 0)
        err_throw(VMERR_BAD_VAL_BIF);

    /* we don't have a winner yet */
    int winner_idx = 0;
    vm_val_t winner;

    /* run through the list */
    for (int i = 0 ; i < cnt ; ++i)
    {
        /* get this element */
        vm_val_t ele;
        get_element(i, &ele);

        /* if there's a callback, invoke it */
        if (cb != 0)
        {
            /* invoke the callback with the element value as the argument */
            G_stk->push(&ele);
            G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0);

            /* use the result as the comparison value */
            ele = *G_interpreter->get_r0();
        }

        /* compare it to the winner so far, and keep the lower value */
        if (i == 0)
        {
            /* it's the first element, so it's the default winner so far */
            winner_idx = i;
            winner = ele;
        }
        else
        {
            /* compare it to the winner so far */
            int cmp = ele.compare_to(vmg_ &winner);

            /* keep it if it's the min/max so far */
            if ((sense > 0 && cmp > 0) || (sense < 0 && cmp < 0))
            {
                winner_idx = i;
                winner = ele;
            }
        }
    }

    /* remove gc protection and arguments */
    G_stk->discard(argc + 1);

    /* return the winning index (note: 1-based) or value, as desired */
    if (want_index)
        retval->set_int(winner_idx + 1);
    else
        *retval = winner;
        
    /* handled */
    return TRUE;
}