File: cabsint.ml

package info (click to toggle)
why 2.13-2
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 12,608 kB
  • ctags: 16,817
  • sloc: ml: 102,672; java: 7,173; ansic: 4,439; makefile: 1,409; sh: 585
file content (4090 lines) | stat: -rw-r--r-- 148,942 bytes parent folder | download
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
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
(**************************************************************************)
(*                                                                        *)
(*  The Why platform for program certification                            *)
(*  Copyright (C) 2002-2008                                               *)
(*    Romain BARDOU                                                       *)
(*    Jean-Franois COUCHOT                                               *)
(*    Mehdi DOGGUY                                                        *)
(*    Jean-Christophe FILLITRE                                           *)
(*    Thierry HUBERT                                                      *)
(*    Claude MARCH                                                       *)
(*    Yannick MOY                                                         *)
(*    Christine PAULIN                                                    *)
(*    Yann RGIS-GIANAS                                                   *)
(*    Nicolas ROUSSET                                                     *)
(*    Xavier URBAIN                                                       *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU General Public                   *)
(*  License version 2, as published by the Free Software Foundation.      *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(*  See the GNU General Public License version 2 for more details         *)
(*  (enclosed in the file GPL).                                           *)
(*                                                                        *)
(**************************************************************************)

(* $Id: cabsint.ml,v 1.26 2008/02/05 12:10:47 marche Exp $ *)

(* TO DO:

   - call to [Ceffect] functions to compute effects of loops expects
   previous name disambiguation. This is not done, which can lead to
   errors, e.g.

   declare_heap_var : result ; oldtype = ()double ; newtype = ()global pointer 
   Anomaly: File "c/ceffect.ml", line 98, characters 6-12: Assertion failed

   - in [propagate], when doing backward prop, do not propagate on incoming
   edge with [None] value, control does not flow through this edge !

   - add location to the hooks for invariants/preconditions ?

   - problem with construction of graph for "do-while", not knowing
   if [fwd_node] or [bwd_node] is the place the invariant is checked.
   If both, problem.

   - see why code cannot be shared between [Make_PointWiseSemiLattice]
   and [Make_PointWiseLattice]. Problem with abstraction [t].

   - consider a declaration without initializer as a definition to some top
   value. Currently such declarations are ignored, which allows to translate
       char* q;
       if (b) q = p;
       *q = 0;
   by
       if (b) ;
       *p = 0;

   - treat as a test the expression in a switch statement. The difficulty here
   is that cases with multiple constants, e.g.
       case 1:
       case 2:
   should be translated by a test of the form
       (e == 1) || (e == 2)
   but [e] could be performing some side-effects. In this case a local variable
   (with switch scope) should be introduced, e.g.
       (tmp = e; (tmp == 1) || (tmp == 2))

   - check that the [default] case in a switch is properly defined by an empty
   set of constants, and use it in the definition of the operational graph.

*)

open Info
open Clogic
open Cast
open Cutil

let debug = Coptions.debug
let debug_more = false


(*****************************************************************************
 *                                                                           *
 * 		Basic modules for abstract interpretation                    *
 *                                                                           *
 *****************************************************************************)

(* type of widening performed, if any:
   - WidenFast : to infinity
   - WidenZero : if possible to 0, otherwise to infinity
   - WidenUnit : if possible to -1 or 0 or 1, otherwise to infinity
   - WidenSteps il : if possible to the first integer in [il] that fits,
                     otherwise to infinity
   Taken from Min's example analysis.
 *)
type widening_t = WidenFast | WidenZero | WidenUnit | WidenSteps of int list

module type ELEMENT_OF_CONTAINER = sig
  type t
  val pretty : Format.formatter -> t -> unit
  val compare : t -> t -> int
  val equal : t -> t -> bool
  val hash : t -> int
end
 
module type VARIABLE = sig
  include ELEMENT_OF_CONTAINER
  val to_string : t -> string
end

(* general interface of any module representing integers *)

module type INT_VALUE = sig

  (* same as Int32/Int64 *)
  type t
  val compare : t -> t -> int
  val add : t -> t -> t
  val sub : t -> t -> t
  val mul : t -> t -> t
  val div : t -> t -> t
  val rem : t -> t -> t
  val abs : t -> t
  val zero : t
  val one : t
  val minus_one : t
  val of_int : int -> t
  val to_int : t -> int
  val of_string : string -> t
  val to_string : t -> string
  val neg : t -> t
  val succ : t -> t
  val pred : t -> t

  (* added w.r.t. Int32/Int64 *)
  val pretty: Format.formatter -> t -> unit
  val lt : t -> t -> bool
  val le : t -> t -> bool
  val gt : t -> t -> bool
  val ge : t -> t -> bool
  val eq : t -> t -> bool
  val is_zero : t -> bool
  val is_one : t -> bool
  val min : t -> t -> t
  val max : t -> t -> t
  val length : t -> t -> t (* b - a + 1 *) 
end

module type SEMI_LATTICE = sig
  type t
    (* [top] and [bottom] made functions so that they can depend 
       on the context, typed as [dim_t] (usually a dimension) *)
  type dim_t
  val top : dim_t -> t
  val bottom : dim_t -> t
  val init : dim_t -> t
  val equal : t -> t -> bool
  val pretty : Format.formatter -> t -> unit
  val join : ?backward:bool -> t -> t -> t    
    (* performs widening according to the widening strategy given as 1st
       argument, between the stored abstract value (2nd argument) and
       the new value computed (3rd argument).
       The new value should always be above the stored value in the lattice
       (or less precise), which should always be the case with monotone
       transfer functions. *)
  val widening : widening_t -> old_value:t -> new_value:t -> t
end

module type LATTICE = sig
  include SEMI_LATTICE
  val meet : t -> t -> t
end

module type POINT_WISE_SEMI_LATTICE = sig
  include SEMI_LATTICE
  type var_t
  type value_t
  type map_t
    (* creates a singleton map *)
  val singleton : var_t -> value_t -> t
    (* replace ignores the value already present for this variable, if any *)
  val replace : var_t -> value_t -> t -> t
    (* [add] performs a join if a value was already present for this variable, 
       otherwise a simple add *)
  val union : var_t -> value_t -> t -> t
    (* [addmap] performs an [add] for every entry in the map *)
  val union_map : map_t -> t -> t
    (* [find] never raises an exception, returns the bottom value to signify 
       the variable was not found *)
  val find : var_t -> t -> value_t
    (* [iter] not defined for [top] value *)
  val iter : (var_t -> value_t -> unit) -> t -> unit
    (* [fold] not defined for [top] value *)
  val fold : (var_t -> value_t -> 'a -> 'a) -> t -> 'a -> 'a
  val mapi : (var_t -> value_t -> value_t) -> t -> t
end

module type POINT_WISE_LATTICE = sig
  include POINT_WISE_SEMI_LATTICE
  val meet : t -> t -> t
end

module Make_LatticeFromSemiLattice (L : SEMI_LATTICE) =
struct
  include L
  let meet x y = failwith "Semi-lattice does not implement meet"
end

module Make_PairSemiLattice (L1 : SEMI_LATTICE with type dim_t = unit)
    (L2 : SEMI_LATTICE with type dim_t = unit) 
    : SEMI_LATTICE with type dim_t = unit and type t = L1.t * L2.t =
struct
  type t = L1.t * L2.t
  type dim_t = unit

  let bottom () = L1.bottom (),L2.bottom ()
  let top () = L1.top (),L2.top ()
  let init () = L1.init (),L2.init ()

  let equal p1 p2 = L1.equal (fst p1) (fst p2) && L2.equal (snd p1) (snd p2)

  let pretty fmt p =
    Format.fprintf fmt "(%a,%a)" L1.pretty (fst p) L2.pretty (snd p)

  let join ?(backward=false) p1 p2 = 
    L1.join ~backward (fst p1) (fst p2),L2.join ~backward (snd p1) (snd p2)

  let widening ws ~old_value ~new_value =
    L1.widening ws (fst old_value) (fst new_value),
    L2.widening ws (snd old_value) (snd new_value)
end

module Make_PairLattice (L1 : LATTICE with type dim_t = unit)
    (L2 : LATTICE with type dim_t = unit) 
    : LATTICE with type dim_t = unit and type t = L1.t * L2.t =
struct
  include Make_PairSemiLattice (L1) (L2)
  let meet p1 p2 = L1.meet (fst p1) (fst p2),L2.meet (snd p1) (snd p2)
end

(* public type so that it can be used in both [Make_PointWiseSemiLattice]
   and [Make_PointWiseLattice] *)
type 'map pointwise_t =
  | PWempty
  | PWmap of 'map
  | PWall
      
module Make_PointWiseSemiLattice
    (V : VARIABLE) (L : SEMI_LATTICE with type dim_t = unit) 
    (* no information needed to create L's bottom and top elements *)
  : POINT_WISE_SEMI_LATTICE with type var_t = V.t and type value_t = L.t 
	(* [t] is made public to allow its reuse in [Make_PointWiseLattice] *)
	and type dim_t = unit and type t = L.t Map.Make(V).t pointwise_t =
struct

  module VMap = Map.Make (V)

  type var_t = V.t
  type value_t = L.t
  type map_t = L.t VMap.t 

  type t = L.t VMap.t pointwise_t

  type dim_t = unit
  let bottom () = PWempty
  let top () = PWall
  let init () = PWempty

  let equal pw1 pw2 = match pw1,pw2 with
    | PWempty,PWempty -> true
    | PWmap m1,PWmap m2 -> VMap.equal L.equal m1 m2
    | PWall,PWall -> true
    | _ -> false

  let pretty fmt pw = match pw with
    | PWempty -> Format.fprintf fmt "PWempty"
    | PWmap m -> Format.fprintf fmt "PWmap{%a}"
	(fun fmt m -> VMap.iter 
	   (fun v a -> Format.fprintf fmt "(%a,%a); " 
	      V.pretty v L.pretty a) m) m
    | PWall -> Format.fprintf fmt "PWall"

  let singleton var value =
    PWmap (VMap.add var value VMap.empty)

  let replace var value pw = match pw with
    | PWempty -> PWmap (VMap.add var value VMap.empty)
    | PWmap m -> PWmap (VMap.add var value m)
    | PWall -> PWall

  let find var pw = match pw with
    | PWempty -> L.bottom ()
    | PWmap m ->
	begin 
	  try VMap.find var m with Not_found -> L.bottom ()
	end
    | PWall -> L.top ()

  let union var value pw = match pw with
    | PWempty -> PWmap (VMap.add var value VMap.empty)
    | PWmap m -> 
	let new_value = L.join value (find var pw) in
	PWmap (VMap.add var new_value m)
    | PWall -> PWall

  let union_map m1 pw = match pw with
    | PWempty -> PWmap m1
    | PWmap m2 -> 
	(* union all elements of [m1] in [m2] *)
	VMap.fold union m1 pw
    | PWall -> PWall

  (* ideally, [pw2] should be smaller than [pw1] when unioning maps *)
  let join ?(backward=false) pw1 pw2 = match pw1,pw2 with
    | PWempty,pw | pw,PWempty -> pw
    | PWall,_ | _,PWall -> PWall
    | PWmap m1,PWmap m2 -> 
	(* union all elements of [m2] in [m1] *)
	union_map m2 pw1
    
  let iter f pw = match pw with
    | PWempty -> ()
    | PWmap m -> VMap.iter f m
    | PWall -> failwith "[iter] should not be called on [PWall]"

  let fold f pw init = match pw with
    | PWempty -> init
    | PWmap m -> VMap.fold f m init
    | PWall -> failwith "[fold] should not be called on [PWall]"

  let mapi f pw = match pw with
    | PWempty -> PWempty
    | PWmap m -> PWmap (VMap.mapi f m)
    | PWall -> PWall

  let widening ws ~old_value ~new_value = match old_value,new_value with
    | PWempty,new_value -> new_value
    | PWmap m1,PWmap m2 -> 
	VMap.fold 
	  (fun v a2 m -> 
	     try 
	       let a1 = VMap.find v m1 in
	       let a = L.widening ws a1 a2 in
	       replace v a m
	     with Not_found -> 
	       m (* keep current binding from [new_value] *)
	  ) m2 new_value (* yes, both from [new_value] *)
    | _,PWall -> PWall
    | _ -> 
	(* the stored value [pw1] is less precise than the new computed value
	   [pw2], which should not be the case *)
	assert false
end

module Make_PointWiseLattice
    (V : VARIABLE) (L : LATTICE with type dim_t = unit) 
    : POINT_WISE_LATTICE with type var_t = V.t and type value_t = L.t 
    and type dim_t = unit =
struct

  include Make_PointWiseSemiLattice(V)(L) 

  module VMap = Map.Make (V)

  let intersect pw var value acc =
    let new_value = L.meet value (find var pw) in
    union var new_value acc

  let intersect_map m1 pw = match pw with
    | PWempty -> PWempty
    | PWmap m2 -> 
	(* intersect all elements of [m1] in [m2] *)
	VMap.fold (intersect pw) m1 PWempty
    | PWall -> PWmap m1

  (* ideally, [pw2] should be smaller than [pw1] when intersecting maps *)
  let meet pw1 pw2 = match pw1,pw2 with
    | PWempty,_ | _,PWempty -> PWempty
    | PWall,pw | pw,PWall -> pw
    | PWmap m1,PWmap m2 -> 
	(* intersect all elements of [m2] in [m1] *)
	intersect_map m2 pw1
end

      
(*****************************************************************************
 *                                                                           *
 * 		Abstract interpretation core                                 *
 *                                                                           *
 *****************************************************************************)

type direction_t = Forward | Backward

(* type of pair that allows a missing part *)
type 'a pair_t = Fst of 'a | Snd of 'a | Both of 'a * 'a

(* gives operations on a control-flow graph *)

module type INTER_LANG = sig
  type ilvar_t
  type ilfun_t
  type decl_t

  (* type of declaration/statement/expression in the intermediate language *)
  module Node : ELEMENT_OF_CONTAINER
  module NodeSet : Set.S with type elt = Node.t
  module NodeMap : Map.S with type key = Node.t
  module NodeHash : sig
    include Hashtbl.S with type key = Node.t
    val find_both : 'a pair_t t -> Node.t -> 'a option * 'a option
    val find_pre : 'a pair_t t -> Node.t -> 'a option
    val find_post : 'a pair_t t -> Node.t -> 'a option
    val replace_both : 'a pair_t t -> Node.t -> 'a -> 'a -> unit
    val replace_pre : 'a pair_t t -> Node.t -> 'a -> unit
    val replace_post : 'a pair_t t -> Node.t -> 'a -> unit
    val remove_both : 'a pair_t t -> Node.t -> unit
    val remove_pre : 'a pair_t t -> Node.t -> unit
    val remove_post : 'a pair_t t -> Node.t -> unit
    val iter_both : 
      (Node.t -> 'a option -> 'a option -> unit) -> 'a pair_t t -> unit
    val iter_pre : (Node.t -> 'a -> unit) -> 'a pair_t t -> unit
    val iter_post : (Node.t -> 'a -> unit) -> 'a pair_t t -> unit
    val fold_both : 
      (Node.t -> 'a option -> 'a option -> 'b -> 'b) -> 'a pair_t t -> 'b -> 'b
    val fold_pre : (Node.t -> 'a -> 'b -> 'b) -> 'a pair_t t -> 'b -> 'b
    val fold_post : (Node.t -> 'a -> 'b -> 'b) -> 'a pair_t t -> 'b -> 'b
  end

    (* is this node the function precondition ? *)
  val is_function_precondition_node : Node.t -> bool
    (* returns true if the argument is a widening node, i.e. a node where 
       the analysis is expected to perform widening if it does not
       naturally converge *)
  val is_widening_node : Node.t -> bool
    (* returns the value of the counter associated to a widening node *)
  val get_widening_count : Node.t -> int
    (* increment the counter associated to a widening node *)
  val incr_widening_count : Node.t -> unit
    (* reset the counter associated to a widening node *)
  val reset_widening_count : Node.t -> unit
    (* list of successors in the operational graph, e.g.
       - surrounding expression for a sub-expression
       - [else] and [then] branches for an [if] test expression
       ... 
       order does not matter *)
  val successors : ?ignore_looping:bool -> Node.t -> Node.t list
    (* list of predecessors in the operational graph.
       order does not matter *)
  val predecessors : ?ignore_looping:bool -> Node.t -> Node.t list
    (* is this node merging the upward and backward flows in a loop ? *)
  val is_loop_backward_destination_node : Node.t -> bool
    (* associates to some looping destination node its source node *)
  val looping_source : Node.t -> Node.t
    (* iterators *)
  val iter_operational : 
      direction_t -> roots:Node.t list -> (Node.t -> unit) -> unit
  val fold_operational : 
      direction_t -> roots:Node.t list -> (Node.t -> 'a -> 'a) -> 'a -> 'a
end

(* defines program points where result of analysis is useful *)
  
module type WATCH_POINT = sig
  (* replicates type of INTER_LANG *)
  type node_t
    (* defines watch-points *)
  val is_watch_node : node_t -> bool
end
  
(* connects the concrete and the abstract levels *)
  
module type CONNECTION = sig
    (* replicates type of INTER_LANG *)
  type node_t
  type 'a node_hash_t
    (* type of abstract value map *)
  type absval_t
    (* type of additional information used by [transform] *)
  type transform_t
    (* parametric type for the result of an analysis.
       It is an association from nodes to abstract values.
       First abstract value is the value before the node is entered.
       Second abstract value is the value after the node is exited. *)
  type 'a analysis_t = 'a pair_t node_hash_t
    (* result of abstract interpretation analysis *)
  type absint_analysis_t = absval_t analysis_t
    (* kind of widening performed, if any *)
  val widening_strategy : widening_t
    (* number of times a widening node can be updated before widening
       is performed. Setting it to [None] means that no widening will be
       ever performed. *)
  val widening_threshold : int option
    (* transfer function *)
  val transfer : 
      ?backward:bool -> ?with_assert:bool -> ?one_pass:bool
	-> ?previous_value:absval_t -> node_t -> absval_t -> absval_t
    (* takes the initial program and the formatted analysis, as well as 
       additional information if necessary.
       returns a transformed program. *)
  val transform : 
      absint_analysis_t -> transform_t -> node_t list -> node_t list
end

module type DATA_FLOW_ANALYSIS = sig
  (* replicates type of INTER_LANG *)
  type node_t
  type 'a node_hash_t
  type 'a node_map_t
    (* replicates types of CONNECTION *)
  type absval_t
  type absval_pair_t = absval_t * absval_t
  type 'a analysis_t
  type absint_analysis_t
    (* 
       core propagation function.
       It takes as 1st argument a record of type ['a propagate_t] that gives
       all the necessary basic blocks for the propagation algorithm.
       The list of nodes is used to initialize its working list. It may be 
       all the nodes in the graph, or only the root nodes, depending on
       the analysis. 
       The optional argument is the initial analysis. It will be used as is, 
       not copied (useful to remember as it is an hash-table).
       It produces an analysis that depends on 'a. 
     *)
  type 'a propagate_t =
      { 
	  (* direction *)
	direction : direction_t;
	  (* one-pass or until convergence *)
	one_pass : bool;
	  (* initialization points *)
	init : 'a node_map_t;
          (* transfer function *)
	transfer : ?previous_value:'a -> node_t -> 'a -> 'a;
	  (* default initial value *)
	bottom : unit -> 'a;
	  (* test of equality *)
	equal : 'a -> 'a -> bool;
	  (* pretty-printer *)
	pretty : Format.formatter -> 'a -> unit;
	  (* join function *)
	join : ?backward:bool -> 'a -> 'a -> 'a;
          (* join function on contexts *)
	join_ctxt : 'a -> 'a -> 'a;
	  (* widening function (if any) *)
	widening : widening_t -> old_value:'a -> new_value:'a -> 'a;
	  (* additional action after propagation *)
	action : node_t -> 'a -> unit;
      }
  val propagate : 
      ?analysis:'a analysis_t -> ?roots:node_t list -> 'a propagate_t
	-> 'a analysis_t
    (* 
       computes the result of an analysis on the implicit program.
       It is based on [propagate]. 
     *)
  val compute : node_t list -> absint_analysis_t
    (* compute backward from exit nodes *)
  val compute_back : node_t list -> absint_analysis_t
    (*
       same as compute, with additional assertion propagation.
       Takes as input the function that selects nodes to keep before assertion
       propagation.
    *)
  val compute_with_assert : 
    (node_t -> bool) -> node_t list -> absint_analysis_t
    (* 
       takes a program and computes the result of propagating forward
       the information, and then propagating backward/forward again from
       the nodes selected by [backward_select], with this new information
       being merged with the previous one only at nodes selected by
       [merge_select] using the function [merge_analyses]. 
     *)
  type compute_bnf_t = 
     {
         (* initial computation before back-and-forth propagation *)
       compute : node_t list -> absint_analysis_t;
         (* joins contexts and add conditionals, or default join otherwise *)
       join_context : absval_t -> absval_t -> absval_t;
         (* select nodes on which to propagate backward *)
       backward_select : node_t -> absval_t -> bool;
	 (* modifier to apply before initiating backward propagation *)
       backward_modify : node_t -> absval_t -> absval_t;
	 (* select nodes on which to keep forward information *)
       keep_select : node_t -> bool;
	 (* select nodes on which to merge forward and backward informations *)
       merge_select : node_t -> bool;
	 (* do merge forward and backward informations *)
       merge_analyses : absval_t -> absval_t -> absval_t;
     }
  val compute_back_and_forth :
    compute_bnf_t -> node_t list -> absint_analysis_t
end

(* very simple dataflow analysis, with fixed characteristics:
   forward, intra-procedural, context-insensitive *)

module Make_DataFlowAnalysis
    (V : VARIABLE) (IL : INTER_LANG) (L : LATTICE with type dim_t = unit)
    (* [L.dim_t] does not need to be [unit], change code if needed *)
    (* (W : WATCH_POINT with type node_t = IL.Node.t) *)
    (C : CONNECTION with type node_t = IL.Node.t and type absval_t = L.t
                    and type 'a node_hash_t = 'a IL.NodeHash.t)
  : DATA_FLOW_ANALYSIS
      with type node_t = IL.Node.t 
      and type 'a node_hash_t = 'a IL.NodeHash.t
      and type 'a node_map_t = 'a IL.NodeMap.t
      and type absval_t = C.absval_t
      and type absval_pair_t = C.absval_t * C.absval_t
      and type 'a analysis_t = 'a C.analysis_t
      and type absint_analysis_t = C.absint_analysis_t
= struct

  open IL

  type node_t = Node.t
  type 'a node_hash_t = 'a NodeHash.t
  type 'a node_map_t = 'a NodeMap.t
  type absval_t = L.t
  type absval_pair_t = absval_t * absval_t
  type 'a analysis_t = 'a C.analysis_t
  type absint_analysis_t = C.absint_analysis_t

  type 'a propagate_t =
      { 
	  (* direction *)
	direction : direction_t;
	  (* one-pass or until convergence *)
	one_pass : bool;
	  (* initialization points *)
	init : 'a node_map_t;
          (* transfer function *)
	transfer : ?previous_value:'a -> node_t -> 'a -> 'a;
	  (* default initial value *)
	bottom : unit -> 'a;
	  (* test of equality *)
	equal : 'a -> 'a -> bool;
	  (* pretty-printer *)
	pretty : Format.formatter -> 'a -> unit;
	  (* join function *)
	join : ?backward:bool -> 'a -> 'a -> 'a;
          (* join function on contexts *)
	join_ctxt : 'a -> 'a -> 'a;
	  (* widening function (if any) *)
	widening : widening_t -> old_value:'a -> new_value:'a -> 'a;
	  (* additional action after propagation *)
	action : node_t -> 'a -> unit;
      }

  type compute_bnf_t = 
     {
         (* initial computation before back-and-forth propagation *)
       compute : node_t list -> absint_analysis_t;
         (* joins contexts and add conditionals, or default join otherwise *)
       join_context : absval_t -> absval_t -> absval_t;
         (* select nodes on which to propagate backward *)
       backward_select : node_t -> absval_t -> bool;
	 (* modifier to apply before initiating backward propagation *)
       backward_modify : node_t -> absval_t -> absval_t;
	 (* select nodes on which to keep forward information *)
       keep_select : node_t -> bool;
	 (* select nodes on which to merge forward and backward informations *)
       merge_select : node_t -> bool;
	 (* do merge forward and backward informations.
	    Forward is [post] value while backward is [pre] value, which only
	    makes sense in general if the backward transfer function on
	    the node is the identity.
	 *)
       merge_analyses : absval_t -> absval_t -> absval_t;
     }

  let propagate ?analysis ?(roots=[]) params =

    if debug_more then Coptions.lprintf 
      "[propagate] %s@." (match params.direction with
      | Forward -> "forward"
      | Backward -> "backward");

    let change = ref true in
    let visited_set = ref NodeSet.empty in

    (* result of the analysis *)
    let res : 'a C.analysis_t = match analysis with
    | None -> NodeHash.create 0 
    | Some analysis -> analysis
    in

    (* find current values associated to [node] *)
    let res_val node =
      match NodeHash.find_both res node with
      | None,None ->
	  begin try 
	    let init_val = NodeMap.find node params.init in
	    begin match params.direction with
	    | Forward -> Some init_val,None,true
	    | Backward -> None,Some init_val,true
	    end
	  with Not_found -> None,None,false end
      | pre_val,post_val -> pre_val,post_val,false
    in

    let treat_node cur_node =
      if not (NodeSet.mem cur_node (!visited_set)) then
	begin 
	  visited_set := NodeSet.add cur_node (!visited_set);
	  if IL.is_widening_node cur_node then
	    (* reset the counter for widening *)
	    IL.reset_widening_count cur_node
	end;

      (* find value associated to [cur_node] *)
      let pre_val,post_val,is_init = res_val cur_node in

      let pre_val =
	if params.one_pass && is_loop_backward_destination_node cur_node then
	  match params.direction with
	    | Forward -> 
		let sce_node = looping_source cur_node in
		let _,sce_val,_ = res_val sce_node in
		begin match pre_val,sce_val with
		  | None,_ -> pre_val
		  | Some _,None -> pre_val
		  | Some pre_val,Some sce_val ->
		      Some (params.join_ctxt pre_val sce_val)
		end
	    | Backward -> pre_val
	else pre_val
      in

      let from_val,to_val = match params.direction with
        | Forward -> pre_val,post_val
	| Backward -> post_val,pre_val
      in
      if debug_more then Coptions.lprintf 
	"[propagate] take node in working list %a from val %a@."
	Node.pretty cur_node (Option.pretty params.pretty) from_val;

      let backward = match params.direction with
	| Backward -> true | Forward -> false
      in

      (* compute next value and replace existing one *)
      let cur_val = 
	(* do not call transfer function on init node in backward mode
	   (useful for access that is also a test, e.g. "if (t[i])") *)
	if backward && is_init then
	  from_val
	else match to_val with
	  | None -> 
	      Option.app (params.transfer cur_node) from_val
	  | Some to_val ->
	      Option.app (params.transfer ~previous_value:to_val cur_node) 
		from_val
      in
      (* perform widening if necessary *)
      let cur_val = match cur_val with
	| None -> None
	| Some cur_val ->
	    if IL.is_widening_node cur_node then
	      match C.widening_threshold with
		| None -> 
		    (* analysis does not require widening to converge *)
		    Some cur_val
		| Some threshold ->
		    let cur_count = IL.get_widening_count cur_node in
		    IL.incr_widening_count cur_node;
		    if cur_count = threshold then
		      (* perform widening *)
		      match to_val with
			| None -> Some cur_val
			| Some to_val -> 
			    if debug_more then Coptions.lprintf 
			      "[propagate] perform widening@.";
			    Some (params.widening C.widening_strategy 
				    ~old_value:to_val ~new_value:cur_val)
		    else if cur_count > threshold then
		      (* needed ???????? *)
		      to_val
		    else 
		      (* not yet time for widening *)
		      Some cur_val
	    else 
	      (* not a widening node *)
	      Some cur_val
      in
      if debug_more then Coptions.lprintf 
	"[propagate] computed value: %a@." 
	(Option.pretty params.pretty) cur_val;
      if debug_more then Coptions.lprintf 
	"[propagate] previous value: %a@." 
	(Option.pretty params.pretty) to_val;

      (* change value if different in fixpoint case, or in all cases when
	 doing a one-pass propagation. In the last case, the value of the node
	 is not modified, but the successors' values are. *)
      if not (Option.equal params.equal cur_val None) 
	&& (params.one_pass
	    || not (Option.equal params.equal cur_val to_val)) then
	begin
	  (* [from_val] and [cur_val] are [Some] options here *)
	  let from_val = 
	    match from_val with Some v -> v | None -> assert false in
	  let cur_val = 
	    match cur_val with Some v -> v | None -> assert false in

	  change := true;

	  if debug_more then Coptions.lprintf 
	    "[propagate] new value is different@.";
	  
	  begin match params.direction with
	    | Forward -> 
		NodeHash.replace_both res cur_node from_val cur_val;
	    | Backward -> 
		NodeHash.replace_both res cur_node cur_val from_val
	  end;

	  let next_nodes = match params.direction with
	    | Forward -> 
                IL.successors ~ignore_looping:params.one_pass cur_node 
	    | Backward -> 
                IL.predecessors ~ignore_looping:params.one_pass cur_node 
	  in
	  if debug_more then Coptions.lprintf 
	    "[propagate] node has %i successor(s)@." (List.length next_nodes);
	  if debug_more then Coptions.lprintf 
	    "[propagate] %a@." (fun fmt -> List.iter 
				(Coptions.lprintf "%a " Node.pretty))
	    next_nodes;
	  List.iter (fun nx_node ->
		       let nx_pre,nx_post,_ = res_val nx_node in
		       let nx_from,nx_to = match params.direction with
			 | Forward -> nx_pre,nx_post
			 | Backward -> nx_post,nx_pre
		       in
		       if debug_more then Coptions.lprintf 
			 "[propagate] succ prev value: %a@." 
			 (Option.pretty params.pretty) nx_from;
		       let nx_val = match nx_from with
			 | None -> cur_val 
			 | Some nx_from -> 
			     if is_function_precondition_node nx_node then
			       (* function precondition is non-empty, use it
				  rather than propagating initial value *)
			       nx_from (* ignore [cur_val] *)
			     else
			       params.join ~backward nx_from cur_val 
		       in
		       if debug_more then Coptions.lprintf 
			 "[propagate] succ cur value: %a@."
			   params.pretty nx_val;

		       (* change value if different *)
		       if not (Option.equal 
				 params.equal (Some nx_val) nx_from) then
			 begin
			   if debug_more then Coptions.lprintf 
			     "[propagate] new value for successor \
			      is different@.";
			   if debug_more then Coptions.lprintf 
			     "[propagate] new value: %a@." 
			       params.pretty nx_val;
			   let nx_to = match nx_to with
			     | None -> params.bottom ()
			     | Some nx_to -> nx_to
			   in
			   begin match params.direction with
			     | Forward ->
				 NodeHash.replace_both res nx_node 
				   nx_val nx_to
			     | Backward ->
				 NodeHash.replace_both res nx_node
				   nx_to nx_val
			   end;
(*
			   (* add node in working list/set if not present *)
			   add_node nx_node
*)
			 end
		    ) next_nodes
	end;

      (* perform additional action (if any) *)
      match cur_val with
	| None -> ()
	| Some cur_val -> params.action cur_node cur_val
    in

    while !change do
      change := false;
      if debug_more then Coptions.lprintf 
	  "[propagate] one more round of propagation@.";
      IL.iter_operational params.direction ~roots treat_node;
      (* immediately stop iteration in one-pass propagation *)
      if params.one_pass then change := false
    done;
    res

  let forward_params ?join_context
      ?(one_pass=false) ?(with_assert=false) init =
    { 
        (* direction *)
      direction = Forward;
        (* one-pass or until convergence *)
      one_pass = one_pass;
        (* initialization points *)
      init = init;
        (* transfer function *)
      transfer = C.transfer ~backward:false ~with_assert ~one_pass;
        (* default initial value *)
      bottom = L.bottom;
        (* test of equality *)
      equal = L.equal;
        (* pretty-printer *)
      pretty = L.pretty;
        (* join function *)
      join = L.join;
        (* join function on contexts *)
      join_ctxt = 
	begin match join_context with 
	  | None -> L.join ~backward:false 
	  | Some f -> f 
	end;
        (* widening function (if any) *)
      widening = L.widening;
        (* additional action after propagation *)
      action = fun _ _ -> ();
    }

  let backward_params ?join_context nodes cstr action =
    { 
        (* direction *)
      direction = Backward;
        (* one-pass or until convergence *)
      one_pass = true;
        (* initialization points *)
      init = List.fold_left (fun map node -> NodeMap.add node cstr map)
	NodeMap.empty nodes;
        (* transfer function *)
      transfer = C.transfer
	~backward:true ~with_assert:false ~one_pass:true;
        (* default initial value *)
      bottom = L.bottom;
        (* test of equality *)
      equal = L.equal;
        (* pretty-printer *)
      pretty = L.pretty;
        (* join function *)
      join = L.join;
        (* join function on contexts, only used in forward propagation *)
      join_ctxt = 
	begin match join_context with 
	  | None -> L.join ~backward:false 
	  | Some f -> f 
	end;
        (* widening function (if any) *)
      widening = L.widening;
        (* additional action after propagation *)
      action = action;
    }

  let keep_only_selected keep_select analysis =
    IL.iter_operational Forward ~roots:[] (fun node ->
      if keep_select node then
	NodeHash.remove_pre analysis node
      else
	NodeHash.remove analysis node
    )

  let compute decls =
    let init = List.fold_left (fun m decl -> NodeMap.add decl (L.init ()) m) 
      NodeMap.empty decls in
    propagate (forward_params init)

  let compute_back nodes =
    propagate (backward_params nodes (L.init()) (fun _ _ -> ()))

  (* propagation of assertions (e.g. resulting from verification conditions)
     should be done in one pass, without going through back edges in loops.
     Indeed, on the following code 
          int i = 0;
          int p[10]; // pp1
          while (i++) { // pp2
            p[i] = 0; // pp3
          }
     At program point 1 (pp1)  : i == 0 && arrlen(p) == 10
     At pp3 without assertions : i > 0 && arrlen(p) == 10
     At pp3 with assertions    : 0 < i < arrlen(p) == 10
     If we join pp1 and pp3 with assertions, we get a buggy assume-invariant
          0 <= i < arrlen(p) == 10
     The correct thing to do is to join pp1 and pp3 without assertions, to get
     the correct assume-invariant
          0 <= i && arrlen(p) == 10
     And then to propagate the assertions without going through back edges.
   *)
  let compute_with_assert keep_select decls =
    (* 1st step: propagate forward information *)
    let fwd_analysis = compute decls in

    (* 2nd step: propagate forward assertions *)
    keep_only_selected keep_select fwd_analysis;
    let init = List.fold_left (fun m decl -> NodeMap.add decl (L.init ()) m) 
      NodeMap.empty decls in
    propagate (forward_params ~with_assert:true ~one_pass:true init)
      ~analysis:fwd_analysis

  let compute_back_and_forth params decls =
    (* 1st step: propagate initial information *)
    let fwd_analysis = params.compute decls in

    (* 2nd step: propagate backward/forward again from every selected node *)
    IL.fold_operational Forward ~roots:[] (fun node cur_analysis ->

      let node_value_in_analysis analysis node =
	let pre_cur_val = match NodeHash.find_pre analysis node with
	| None -> 
	    (* use here top value, in order to correctly meet with value
	       from forward analysis *)
	    L.top ()
	| Some pre_val -> pre_val
	in
	let post_cur_val =  match NodeHash.find_post analysis node with
	| None -> 
	    (* use here top value, in order to correctly meet with value
	       from forward analysis *)
	    L.top ()
	| Some post_val -> post_val
	in
	(* always refer to [fwd_analysis] to keep initial fixpoint 
	   results. [analysis] may not have an appropriate value,
	   due to the fact some backward analysis may not have 
	   considered this node, and thus have "forgotten" the initial 
	   results. *)
	let pre_fwd_val,post_fwd_val =
	  match NodeHash.find_both fwd_analysis node with
	  | None,None -> 
	      (* use here top value to allow [meet] below *)
	      L.top (),L.top ()
	  | Some pre_val,None ->
	      pre_val,L.top ()
	  | None,Some post_val ->
	      L.top (),post_val
	  | Some pre_val,Some post_val ->
	      pre_val,post_val
	in
	let pre_cur_val = L.meet pre_cur_val pre_fwd_val in
	let post_cur_val = L.meet post_cur_val post_fwd_val in
	pre_cur_val,post_cur_val
      in

      let pre_val,_ = node_value_in_analysis cur_analysis node in

      if params.backward_select node pre_val then

	(* create an empty new analysis *)
	let mix_analysis = NodeHash.create (NodeHash.length cur_analysis) in
	let pre_val = params.backward_modify node pre_val in
	let bwd_action node pre_bwd_val =
	  if params.merge_select node || params.keep_select node then
	    let pre_cur_val,post_cur_val = 
	      node_value_in_analysis cur_analysis node
	    in
	    if params.merge_select node then
	      (* use [post] value, only one valid for previous analysis *)
	      let pre_mix_val = params.merge_analyses post_cur_val pre_bwd_val
	      in
	      (* for merge nodes, set same value as [pre] and [post] value *)
	      NodeHash.replace_both mix_analysis node pre_mix_val pre_mix_val
	    else if params.keep_select node then
	      NodeHash.replace_both mix_analysis node pre_cur_val post_cur_val
	    else assert false
	  else ()
	in
	(* propagate backward on the path that leads to this node *)
	let bwd_params = backward_params [node] pre_val bwd_action in
	let bwd_analysis = propagate bwd_params ~roots:[node] in
	ignore (bwd_analysis);
	(* add appropriate assume invariant when "forgotten" by last
	   backward analysis *)
	IL.iter_operational Forward ~roots:[] (fun node ->
	  if params.keep_select node then
	    let pre_mix_val,post_mix_val = 
	      node_value_in_analysis mix_analysis node
	    in
	    NodeHash.replace_both mix_analysis node pre_mix_val post_mix_val
	);
	(* propagate forward again *)
	keep_only_selected params.keep_select mix_analysis;
	let init =
	  List.fold_left (fun m decl -> NodeMap.add decl (L.init ()) m)
	    NodeMap.empty decls in
	let fwd_params = forward_params ~join_context:params.join_context 
	  ~with_assert:true ~one_pass:true init in
	propagate fwd_params ~analysis:mix_analysis

      else cur_analysis
    ) fwd_analysis

end


(*****************************************************************************
 *                                                                           *
 * 		Concrete intermediate language for analysis                  *
 *                                                                           *
 *****************************************************************************)

(* type used for offset from pointer *)
let int_offset_type = Ctypes.c_int
let int_offset_kind = Ctypes.Signed,Ctypes.Int
let int_offset_addop = Badd_int int_offset_kind

(* type to represent a function in the normalized code.
   It has the same elements as the hash-table [Cenv.c_functions]. *)
type func_t = 
    {
      name : string;
      spec : Cast.nspec; 
      typ  : Ctypes.ctype;
      f    : Info.fun_info;
      s    : Cast.nstatement option;
      loc  : Loc.position
    }

module ILVar : VARIABLE with type t = var_info = struct
  type t = var_info
  let pretty fmt v = Format.fprintf fmt "%s" v.var_name
  let to_string v = v.var_name
  let compare v1 v2 = Pervasives.compare v1.var_uniq_tag v2.var_uniq_tag
  let equal v1 v2 = compare v1 v2 = 0
  let hash v = v.var_uniq_tag
end

module ILVarMap = Map.Make (ILVar)
module ILVarSet = Set.Make (ILVar)

(* translation targetted at very simple syntaxical analysis on paths,
   since effects of calls are not considered, and unspecified evaluation order
   is translated in parallel edges in the CFG *)

module type CFG_LANG_INTERNAL = sig
  type node_t
  type var_tt
  type intern_t = 
   | InternOperational
   | InternOperationalBwdSrc
   | InternOperationalBwdDest
   | InternStructural
   | InternLogical
   | InternLogicalScope
  type count_t = { mutable count : int }
  type lvalue_t = Assign | OpAssign | IncrDecr
  type write_t = 
      { 
	(* list of variables written variables in the loop *)
	write_vars : var_tt list; 
	(* list of pointers whose content is read in the loop *)
	read_under_pointers : var_tt list; 
	(* list of pointers whose content is modified in the loop *)
	write_under_pointers : var_tt list; 
      }
  type norm_node = 
      (* coding constructs *)
    | Nexpr of nexpr
      (* execution proceeds to next nodes only if test succeeds. 
	 Replaces [Nexpr] where a test controls the flow: if, loop (switch ?).
	 The [then] branch will be preceded by the normal test of [if], while
	 the [else] branch will be preceded by the negation of this test.
	 The same occurs for loops.
	 This kind of node is produced for analysis on the operational graph
	 only. In particular, the subsequent transformations on the underlying
	 code are not supposed to return a [Ntest] node from a [Ntest] node.
	 They can return simply a [Nexpr] node.
       *)
    | Ntest of nexpr 
    | Nlvalue of nexpr * lvalue_t
    | Nstat of nstatement
    | Ndecl of func_t

      (* logical constructs *)
    | Nspec of nspec
    | Nannot of nloop_annot
    | Nterm of nterm
    | Npred of npredicate
      (* Various logical constructs act as "assume": assume statement,
	 function precondition, call postcondition, etc.
	 Each of these may already be part of an enclosing logical or
	 coding construct, e.g. the function precondition is already part of
	 a specification. Then the same [Nassume] node is used both 
	 in the operational graph and as a sub-node of an enclosing node
	 in the logical or the structural graph, e.g. an [Nspec] node 
	 in the logical graph for the function precondition. *)
    | Nassume of npredicate
      (* loop assume invariant is a special kind of assume *)
    | Nassinv of npredicate * write_t
      (* function precondition is a special kind of assume *)
    | Npre of npredicate
      (* Various logical constructs act as "assert": assert statement,
	 loop invariant, function postcondition, call precondition, etc.
	 Each of these may already be part of an enclosing logical or
	 coding construct, e.g. the loop invariant is already part of
	 an annotation. Then the same [Nassert] node is used both 
	 in the operational graph and as a sub-node of an enclosing node
	 in the logical or the structural graph, e.g. an [Nannot] node 
	 in the logical graph for the loop invariant. *)
    | Nassert of npredicate
      (* loop invariant is a special kind of assert *)
    | Ninv of npredicate * write_t

      (* special constructs *)
      (* "forward" node used in both hierarchical graphs (structural + logical)
	 and in the operational graph so far. 
	 The element it carries defines which graph it is part of.
	 This is used when changing the graph as the result of an analysis. *)
    | Nintern of intern_t
      (* widening node added in the operational lattice, to allow unbounded
	 analyses to terminate. The integer is a counter initialized to 0 and
	 incremented each time a new abstract value is computed for this node.
	 Depending on the analysis, some widening may be performed on 
	 the abstract value if the counter is above some threshold. *)
    | Nwiden of count_t

    (* create a temporary node that will not be part of either graph. 
       In particular, it will not be possible to add edges from/to this node.
       This is convenient for nodes created during the transformation of
       the code, that only store intermediate results. *)
  val create_tmp_node : norm_node -> node_t

  val get_e : node_t -> nexpr
  val get_expr : node_t -> nexpr_node
  val get_s : node_t -> nstatement
  val get_stat : node_t -> nstatement_node
  val get_decl : node_t -> func_t
  val get_spec : node_t -> nspec
  val get_annot : node_t -> nloop_annot
  val get_t : node_t -> nterm
  val get_term : node_t -> Ctypes.ctype nterm_node
  val get_p : node_t -> npredicate
  val get_predicate : node_t -> Ctypes.ctype npredicate_node

    (* is this expression an assignment ? *)
  val sub_expr_is_assign : nexpr -> bool
    (* if the left-hand side of this assignment is a variable, return it *)
  val sub_assign_get_lhs_var : nexpr -> var_tt option
    (* remove casts on top of the expression *)
  val sub_skip_casts : nexpr -> nexpr

  val decode_decl_list : nexpr -> nexpr c_initializer
  val change_sub_components_in_stat : node_t -> node_t list -> node_t
  val change_sub_components_in_expr : node_t -> node_t list -> node_t
  val change_sub_components_in_term : node_t -> node_t list -> node_t
  val change_sub_components_in_pred : node_t -> node_t list -> node_t
  val change_sub_components : node_t -> node_t list -> node_t

end

module type CFG_LANG_EXTERNAL = sig

  include INTER_LANG with type ilvar_t = ILVar.t 
		     and type ilfun_t = fun_info
		     and type decl_t = func_t

  (* successors in both structural and logical graph *)

    (* list of successors in the structural graph, e.g.
       - list of operands of an operation
       - caller and arguments of a function call
       - list of statements in a block statement 
       ... *)
  val code_children : Node.t -> Node.t list (* order matters *)
    (* list of successors in the logical graph, e.g.
       - loop annotation of a loop
       - spec of block of code
       - invariant of a loop annotation
       - sub-predicate in an invariant
       - term in a predicate
       ... *)
  val logic_children : Node.t -> Node.t list (* order matters *)
    (* beginning of logical block
       Used for \old annotations and specifications. *)
  val logic_begin : Node.t -> Node.t
    (* end of logical block
       Used for annotations and specifications. *)
  val logic_end : Node.t -> Node.t
    (* associated invariant relation, that relates corresponding invariant and
       assume invariant nodes for a specific loop *)
  val logic_invariant : Node.t -> Node.t option
    (* is it the beginning or the end of a logical scope ? *)
  val is_logic_scope : Node.t -> bool  

  (* 11 kinds of nodes: 
     for code: expression/test/statement/declaration
     for logic: specification/loop annotation/term/predicate/assume/assert
     for internal use: internal
  *)
  type node_kind = 
    (* coding *)  | NKexpr | NKtest | NKlvalue | NKstat | NKdecl 
    (* logical *) | NKspec | NKannot | NKterm | NKpred | NKassume | NKassert 
    (* special *) | NKnone

  (* query functions *)

    (* returns the node's kind *)
  val get_node_kind : Node.t -> node_kind
    (* is this node a [Ninv] node for a loop invariant ? *)
  val is_invariant_node : Node.t -> bool
    (* is this node a [Nassinv] node for a loop invariant ? *)
  val is_assume_invariant_node : Node.t -> bool
    (* is this node a [Nintern InternOperationalBwdSrc] node
       for collecting values on the backward edge of a loop before
       merging with the upward value ? *)
  val is_loop_backward_source_node : Node.t -> bool
  (* put in INTER_LANG interface :
     is_loop_backward_destination_node : Node.t -> bool
  *)
    (* get the list of variables modified in this loop *) 
  val get_loop_write_vars : Node.t -> ilvar_t list
    (* get the list of pointers whose content is read in this loop *) 
  val get_loop_read_under_pointers : Node.t -> ilvar_t list
    (* get the list of pointers whose content is modified in this loop *) 
  val get_loop_write_under_pointers : Node.t -> ilvar_t list
    (* returns the function's parameters *)
  val decl_get_params : Node.t -> ilvar_t list
    (* is it a return statement ? *)
  val stat_is_return : Node.t -> bool
    (* is it an assert statement ? *)
  val stat_is_assert : Node.t -> bool
    (* is it a loop statement ? *)
  val stat_is_loop : Node.t -> bool
    (* is it an if statement ? *)
  val stat_is_if : Node.t -> bool
    (* is it a spec statement ? *)
  val stat_is_spec : Node.t -> bool
    (* is it a label statement ? *)
  val stat_is_label : Node.t -> bool
    (* is it a declaration statement ? *)
  val stat_is_decl : Node.t -> bool
    (* get the label associated with this label statement *)
  val stat_get_label : Node.t -> string
    (* is it a jump statement ? *)
  val stat_is_jump : Node.t -> bool
    (* is it a block statement ? *)
  val stat_is_block : Node.t -> bool
    (* is it a no-op statement ? *)
  val stat_is_nop : Node.t -> bool
    (* get the variable associated with this declaration statement *)
  val decl_stat_get_var : Node.t -> ilvar_t
    (* get the next statement of this declaration statement *)
  val decl_stat_get_next : Node.t -> Node.t
    (* is this expression a -local- variable ? *)
  val expr_is_local_var : Node.t -> bool
    (* returns the variable in this term/expression over a variable *)
  val termexpr_var_get : Node.t -> ilvar_t
    (* is this expression an integer constant ? *)
  val expr_is_int_constant : Node.t -> bool
    (* returns the integer in this expression over an integer constant *)
  val expr_int_constant_get : Node.t -> int
    (* is this expression an assignment ? *)
  val expr_is_assign : Node.t -> bool
    (* get the right operand of this assignment (maybe the ony one, in case of
       an increment/decrement *)
  val assign_get_rhs_operand : Node.t -> Node.t
    (* get the left operand of this assignment (maybe the ony one, in case of
       an increment/decrement *)
  val assign_get_lhs_operand : Node.t -> Node.t
    (* is this a local variable ? *)
  val var_is_local : ilvar_t -> bool
    (* is this variable an integer ? *)
  val var_is_integer : ilvar_t -> bool
    (* is the type of this expression an integer type ? *)
  val expr_type_is_int : Node.t -> bool
    (* is the type of this expression a character type ? *)
  val expr_type_is_char : Node.t -> bool
    (* is this expression an integer assignment ? *)
  val expr_is_int_assign : Node.t -> bool
    (* is this variable a pointer ? *)
  val var_is_pointer : ilvar_t -> bool
    (* is the type of this expression a pointer type ? *)
  val expr_type_is_ptr : Node.t -> bool
    (* remove casts on top of the expression *)
  val skip_casts : Node.t -> Node.t
    (* is this term/expression a -local- variable ? *)
  val termexpr_is_local_var : Node.t -> bool
    (* is this expression a pointer assignment ? *)
  val expr_is_ptr_assign : Node.t -> bool
    (* if the left-hand side of this assignment is a -local- variable, 
       return it *)
  val assign_get_local_lhs_var : Node.t -> ilvar_t option
    (* is this expression a dereference ? *)
  val expr_is_deref : Node.t -> bool
    (* get the variable and the offset dereferenced, if any *)
  val deref_get_variable_and_offset : 
      Node.t -> (ilvar_t * Node.t option) option
    (* if the dereferenced expression is a -local- variable, return it *)
  val deref_get_local_var : Node.t -> ilvar_t option
    (* is this expression a call ? *)
  val expr_is_call : Node.t -> bool
    (* get the function called, if any *)
  val call_get_function : Node.t -> ilfun_t option
    (* get the arguments for the call, if any *)
  val call_get_args : Node.t -> Node.t list
    (* get precondition for function *)
  val function_get_precondition : ilfun_t -> Node.t option
    (* get parameters for function *)
  val function_get_params : ilfun_t -> ilvar_t list
    (* is this term/predicate an \old one ? *)
  val termpred_is_old : Node.t -> bool
    (* is this term/predicate an \at one ? *)
  val termpred_is_at : Node.t -> bool
    (* get the label associated with this \at term/predicate *)
  val termpred_get_label : Node.t -> string

  (* constructors.
     - [make_] functions operate directly on their arguments.
     - [change_] functions take a first node as context, and operate on 
     other arguments.
  *)

    (* create a new sequence expression node *)
  val make_seq_expr : Node.t -> Node.t -> Node.t
    (* create a new node expression + constant *)
  val make_int_expr_add_cst : Node.t -> int -> Node.t
    (* create a new node expression + variable *)
  val make_int_expr_add_var : Node.t -> ilvar_t -> Node.t
    (* create a new node term/expression + term/expression *)
  val make_int_termexpr_add_termexpr : Node.t -> Node.t -> Node.t
    (* create a new declaration statement for the variable, with the given
       statement as next statement *)
  val make_var_decl : Node.t -> ilvar_t -> Node.t
    (* create a new block statement node *)
  val make_seq_stat : Node.t -> Node.t -> Node.t
    (* make this node be an integer constant to be added to some pointer *)
  val change_in_int_offset_cst : Node.t -> int -> Node.t
    (* make this node be an assignment variable = constant *)
  val change_in_int_var_assign_cst : Node.t -> ilvar_t -> int -> Node.t
    (* make this node be an assignment variable = variable *)
  val change_in_int_var_assign_var : Node.t -> ilvar_t -> ilvar_t -> Node.t
    (* make this node be an assignment variable = expression *)
  val change_in_int_var_assign_expr : Node.t -> ilvar_t -> Node.t -> Node.t
    (* change the structural sub-components of this node *)
  val change_sub_components : Node.t -> Node.t list -> Node.t 

  (* language interface *)
  (* returns the list of roots *)
  val from_file : decl_t list -> (Node.t * Node.t) list
  val to_file : Node.t list -> decl_t list
    (* collect variables used and declared in the code *)
  val collect_vars : unit -> ILVarSet.t * ILVarSet.t
end

module CFGLangFromNormalized : sig
  include CFG_LANG_EXTERNAL
  include CFG_LANG_INTERNAL with type node_t = Node.t and type var_tt = ILVar.t
end = struct
  
  type var_tt = ILVar.t
  type ilvar_t = ILVar.t
  type ilfun_t = fun_info
  type decl_t = func_t

  type node_kind = 
    (* coding *)  | NKexpr | NKtest | NKlvalue | NKstat | NKdecl 
    (* logical *) | NKspec | NKannot | NKterm | NKpred | NKassume | NKassert 
    (* special *) | NKnone

  (* special node [Nintern] used for special purposes:
     - during construction to reference future nodes
     - to translate [switch] into appropriate structural tree
     - in loops to create a destination node for back edge
     ...
     It carries an element of type [intern_t] that defines which graph
     it is part of.
  *)
  type intern_t = 
   | InternOperational
   | InternOperationalBwdSrc
   | InternOperationalBwdDest
   | InternStructural
   | InternLogical
   | InternLogicalScope

  type count_t = { mutable count : int }
  type lvalue_t = Assign | OpAssign | IncrDecr
  type write_t = 
      { write_vars : ILVar.t list; 
	read_under_pointers : ILVar.t list;
	write_under_pointers : ILVar.t list; }

  let w_empty = { write_vars = []; 
		  read_under_pointers = [];
		  write_under_pointers = []; }

  type norm_node = 
      (* coding constructs *)
    | Nexpr of nexpr
    | Ntest of nexpr 
    | Nlvalue of nexpr * lvalue_t
    | Nstat of nstatement
    | Ndecl of func_t
      (* logical constructs *)
    | Nspec of nspec
    | Nannot of nloop_annot
    | Nterm of nterm
    | Npred of npredicate
    | Nassume of npredicate
    | Nassinv of npredicate * write_t
    | Npre of npredicate
    | Nassert of npredicate
    | Ninv of npredicate * write_t
      (* special constructs *)
    | Nintern of intern_t
    | Nwiden of count_t

  (* type of labels on edges in the CFG *)
  module NodeRelation = struct
    type t =
      | OperationalFwd  (* forward edge in the operational graph *)
      | OperationalBwd  (* backward edge in the operational graph *)
      | StructuralDown  (* edge to first sub-node in the structural graph *)
      | StructuralSame  (* other edges in the structural graph *)
      | LogicalDown     (* edge to first sub-node in the logical graph *)
      | LogicalSame     (* other edges in the logical graph *)
      | LogicScopeBegin (* edge to first node in logical block *)
      | LogicScopeEnd   (* edge to last node in logical block *)
      | LogicInvariant  (* edge to relate invariant and assume invariant *)
    (* arbitrary index to provide total ordering *)
    let index r = match r with
      | OperationalFwd  -> 0
      | OperationalBwd  -> 1
      | StructuralDown  -> 2
      | StructuralSame  -> 3
      | LogicalDown     -> 4
      | LogicalSame     -> 5
      | LogicScopeBegin -> 6
      | LogicScopeEnd   -> 7
      | LogicInvariant  -> 8
    let compare r1 r2 = Pervasives.compare (index r1) (index r2)
    (* if not stated otherwise, an edge is a forward operational one *)
    let default = OperationalFwd
  end

  (* We use here an abstract imperative labeled graph.
     Labels are of 4 kinds :
         - operational labels form a graph that respects evaluation order,
     to facilitate dataflow analysis
         - structural labels form a graph that respects the hierarchical order
     between expressions and statements, e.g. with [StructuralDown] labels from
     an expression to its first sub-expression, and [StructuralSame] label 
     from a sub-expression to the next sub-expression at the same level.
         - logical labels form a graph similar to the structural graph, except
     it is used for logical properties on the code instead of the code itself.
         - logical scope labels decorate some nodes in the structural or
     logical graph with information on the beginning and end of their logical
     scope. This is used to interpret correctly the logical annotations.

     Beware that since the operational graph is used for dataflow analysis 
     computation, the action associated to the node will be repeated when
     going through this node in the graph. This makes it necessary sometimes
     to create internal nodes whose sole purpose is to connect parts of 
     the graph with no associated action.
     It may be also necessary to create such internal nodes in the structural
     or logical graph, in order to encode/decode more easily the underlying
     structural/logical constructs.
  *)

  module Self = 
    Graph.Imperative.Digraph.AbstractLabeled
      (struct type t = norm_node end) (NodeRelation)
  module Node = struct
    include Self.V
    let pretty fmt n =
      match label n with
      | Nexpr e -> Cprint.nexpr fmt e
      | Ntest e -> Cprint.nexpr fmt e
      | Nlvalue (e,_) -> Cprint.nexpr fmt e
      | Nstat s -> 
	  begin match s.nst_node with
	    | NSnop -> Format.fprintf fmt "NSnop"
	    | NSassert _ -> Format.fprintf fmt "NSassert"
	    | NSassume _ -> Format.fprintf fmt "NSassume"
	    | NSlogic_label _ -> Format.fprintf fmt "NSlogic_label"
	    | NSexpr e -> Format.fprintf fmt "NSexpr(%a)" Cprint.nexpr e 
	    | NSif (e,s1,s2) -> Format.fprintf fmt "NSif"
	    | NSwhile (annot,e,s1) -> Format.fprintf fmt "NSwhile"
	    | NSdowhile (annot,s1,e) -> Format.fprintf fmt "NSdowhile"
	    | NSfor (annot,einit,etest,eincr,s1) -> Format.fprintf fmt "NSfor"
	    | NSblock sl -> Format.fprintf fmt "NSblock"
	    | NSreturn None -> Format.fprintf fmt "NSreturn"
	    | NSreturn (Some e) -> Format.fprintf fmt "NSreturn" 
	    | NSbreak -> Format.fprintf fmt "NSbreak"
	    | NScontinue -> Format.fprintf fmt "NScontinue" 
	    | NSgoto _ -> Format.fprintf fmt "NSgoto"
	    | NSlabel (str,s1) -> Format.fprintf fmt "NSlabel"
	    | NSspec (spec,s1) -> Format.fprintf fmt "NSspec"
	    | NSdecl (typ,var,None,s1) -> Format.fprintf fmt "NSdecl"
	    | NSdecl (typ,var,Some cinit,s1) -> Format.fprintf fmt "NSdecl"
	    | NSswitch (e,c,cases) -> Format.fprintf fmt "NSswitch" 
	  end
      | Ndecl _ -> Format.fprintf fmt "Ndecl"
      | Nspec _ -> Format.fprintf fmt "Nspec"
      | Nannot _ -> Format.fprintf fmt "Nannot"
      | Nterm t -> Cprint.nterm fmt t
      | Npred p -> Cprint.npredicate fmt p
      | Nassume _ -> Format.fprintf fmt "Nassume"
      | Nassinv _ -> Format.fprintf fmt "Nassinv"
      | Npre _ -> Format.fprintf fmt "Npre"
      | Nassert _ -> Format.fprintf fmt "Nassert"
      | Ninv _ -> Format.fprintf fmt "Ninv"
      | Nintern fk -> 
          begin match fk with
            | InternOperational -> 
		Format.fprintf fmt "Nintern(InternOperational)"
            | InternOperationalBwdSrc -> 
		Format.fprintf fmt "Nintern(InternOperationalBwdSrc)"
            | InternOperationalBwdDest -> 
		Format.fprintf fmt "Nintern(InternOperationalBwdDest)"
            | InternStructural ->
		Format.fprintf fmt "Nintern(InternStructural)"
            | InternLogical ->
		Format.fprintf fmt "Nintern(InternLogical)"
            | InternLogicalScope ->
		Format.fprintf fmt "Nintern(InternLogicalScope)"
          end
      | Nwiden c -> Format.fprintf fmt "Nwiden(%d)" c.count
  end
  module Edge = Self.E

  module NodeSet = Set.Make (Node)
  module NodeMap = Map.Make (Node)
    (* It is necessary to make it a hash-table based on [Node.equal] and
       [Node.hash], otherwise mutated nodes (e.g. the invariant node with
       its mutable field) are not recognized equal. *)
  module NodeHash = struct

    include Hashtbl.Make (Node)

    let find_both analysis node =
      try 
	match find analysis node with
	| Fst v -> Some v,None
	| Snd v -> None,Some v
	| Both (v1,v2) -> Some v1,Some v2
      with Not_found -> None,None
    let find_pre analysis node = fst (find_both analysis node)
    let find_post analysis node = snd (find_both analysis node)

    let replace_both analysis node v1 v2 = 
      replace analysis node (Both (v1,v2))
    let replace_pre analysis node v1 =
      match find_post analysis node with
      | None -> replace analysis node (Fst v1)
      | Some v2 -> replace analysis node (Both (v1,v2))
    let replace_post analysis node v2 =
      match find_pre analysis node with
      | None -> replace analysis node (Snd v2)
      | Some v1 -> replace analysis node (Both (v1,v2))

    let remove_both = remove
    let remove_pre analysis node =
      match find_post analysis node with
	| None -> remove analysis node
	| Some v -> remove analysis node; replace_post analysis node v
    let remove_post analysis node =
      match find_pre analysis node with
	| None -> remove analysis node
	| Some v -> remove analysis node; replace_pre analysis node v

    let iter_both f analysis =
      iter (fun node v -> match v with
	      | Fst v -> f node (Some v) None
	      | Snd v -> f node None (Some v)
	      | Both (v1,v2) -> f node (Some v1) (Some v2)
	   ) analysis
    let iter_pre f analysis =
      iter (fun node v -> match v with
	      | Fst v -> f node v
	      | Snd v -> ()
	      | Both (v1,v2) -> f node v1
	   ) analysis
    let iter_post f analysis =
      iter (fun node v -> match v with
	      | Fst v -> ()
	      | Snd v -> f node v
	      | Both (v1,v2) -> f node v2
	   ) analysis
	
    let fold_both f analysis init =
      fold (fun node v acc -> match v with
	      | Fst v -> f node (Some v) None acc
	      | Snd v -> f node None (Some v) acc
	      | Both (v1,v2) -> f node (Some v1) (Some v2) acc
	   ) analysis init
    let fold_pre f analysis init =
      fold (fun node v acc -> match v with
	      | Fst v -> f node v acc
	      | Snd v -> acc
	      | Both (v1,v2) -> f node v1 acc
	   ) analysis init
    let fold_post f analysis init =
      fold (fun node v acc -> match v with
	      | Fst v -> acc
	      | Snd v -> f node v acc
	      | Both (v1,v2) -> f node v2 acc
	   ) analysis init
  end

  let internal_graph = ref None
  let graph () = match !internal_graph with
  | None -> failwith "attempting to access graph before creation"
  | Some g -> g

    (* add a node *)
  type node_t = Node.t
  let add_vertex node = Self.add_vertex (graph ()) node
  let create_tmp_node node = Node.create node
    (* create a node and add it to the graph *)
  let create_node node = 
    let node = create_tmp_node node in
    add_vertex node; 
    node

  (* shortcut query functions *)

  let get_node_kind node =
    match Node.label node with
      | Nexpr _   -> NKexpr
      | Ntest _   -> NKtest
      | Nlvalue _ -> NKlvalue
      | Nstat _   -> NKstat
      | Ndecl _   -> NKdecl
      | Nspec _   -> NKspec
      | Nannot _  -> NKannot
      | Nterm _   -> NKterm
      | Npred _   -> NKpred
        (* [Npre] and [Nassinv] are special cases of [Nassume] *)
      | Nassume _ -> NKassume
      | Nassinv _ -> NKassume
      | Npre    _ -> NKassume
        (* [Ninv] is a special case of [Nassert] *)
      | Nassert _ -> NKassert
      | Ninv _    -> NKassert
        (* both forward and widening nodes belong to 
	   the "default" kind [NKnone] *)
      | Nintern _    -> NKnone
      | Nwiden _  -> NKnone

  let get_e node = 
    match Node.label node with
      | Nexpr e -> e
      | Ntest e -> e
      | Nlvalue (e,_) -> e
      | _ -> failwith "[get_e] should be called only on expression or test"
  let get_expr node = (get_e node).nexpr_node

  let get_s node = 
    match Node.label node with
      | Nstat s -> s
      | _ -> failwith "[get_s] should be called only on statement"
  let get_stat node = (get_s node).nst_node

  let get_decl node = 
    match Node.label node with
      | Ndecl d -> d
      | _ -> failwith "[get_decl] should be called only on declaration"

  let get_spec node = 
    match Node.label node with
      | Nspec s -> s
      | _ -> failwith "[get_spec] should be called only on specification"
	  
  let get_annot node = 
    match Node.label node with
      | Nannot a -> a
      | _ -> failwith "[get_annot] should be called only on loop annotation"

  let get_t node = 
    match Node.label node with
      | Nterm t -> t
      | _ -> failwith "[get_t] should be called only on term"
  let get_term node = (get_t node).nterm_node

  let get_p node = 
    match Node.label node with
      | Npred p | Nassume p | Nassinv (p,_) 
      | Npre p | Nassert p | Ninv (p,_) 
	-> p
      | _ -> failwith "[get_p] should be called only on predicate"
  let get_predicate node = (get_p node).npred_node

  let fwd_is_structural node = match Node.label node with
    | Nintern InternStructural -> true
    | Nintern _ -> false
    | _ -> failwith "[fwd_is_structural] should be called only on forward node"
  let fwd_is_logical node = match Node.label node with
    | Nintern InternLogical -> true
    | Nintern _ -> false
    | _ -> failwith "[fwd_is_logical] should be called only on forward node"

  let is_invariant_node node = match Node.label node with
    | Ninv _ -> true
    | _ -> false

  let is_assume_invariant_node node = match Node.label node with
    | Nassinv _ -> true
    | _ -> false

  let is_loop_backward_source_node node = match Node.label node with
    | Nintern InternOperationalBwdSrc -> true
    | _ -> false

  let is_loop_backward_destination_node node = match Node.label node with
    | Nintern InternOperationalBwdDest -> true
    | _ -> false

  let get_loop_write_vars node = match Node.label node with
    | Ninv (_,w) | Nassinv (_,w) -> w.write_vars
    | _ -> assert false

  let get_loop_read_under_pointers node = match Node.label node with
    | Ninv (_,w) | Nassinv (_,w) -> w.read_under_pointers
    | _ -> assert false

  let get_loop_write_under_pointers node = match Node.label node with
    | Ninv (_,w) | Nassinv (_,w) -> w.write_under_pointers
    | _ -> assert false

  let is_function_precondition_node node = match Node.label node with
    | Npre _ -> true
    | _ -> false

  let is_widening_node node = match Node.label node with
    | Nwiden _ -> true
    | _ -> false

  let get_widening_count node =
    match Node.label node with
      | Nwiden c -> c.count
      | _ -> failwith "[get_counter] should be called only on widening node"

  let incr_widening_count node =
    match Node.label node with
      | Nwiden c -> c.count <- c.count + 1
      | _ -> failwith ("[incr_widening_count] should be called only"
		       ^ " on widening node")

  let reset_widening_count node =
    match Node.label node with
      | Nwiden c -> c.count <- 0
      | _ -> failwith ("[reset_widening_count] should be called only"
		       ^ " on widening node")

  (* more elaborate query functions related to pointer usage *)

  let is_pointer_type ctyp = match ctyp.Ctypes.ctype_node with
    | Ctypes.Tvar _ -> 
	assert false (* not allowed in code *)
    | Ctypes.Tarray _ | Ctypes.Tpointer _ -> 
	true
    | Ctypes.Tvoid | Ctypes.Tint _ | Ctypes.Tfloat _ | Ctypes.Tfun _ 
    | Ctypes.Tstruct _ | Ctypes.Tunion _ | Ctypes.Tenum _ ->
	false

  let var_is_local var =
    not var.var_is_static

  let is_integer_type ctyp = match ctyp.Ctypes.ctype_node with
    | Ctypes.Tvar _ -> 
	assert false (* not allowed in code *)
    | Ctypes.Tint _ | Ctypes.Tenum _ ->
	true
    | Ctypes.Tarray _ | Ctypes.Tpointer _ 
    | Ctypes.Tvoid | Ctypes.Tfloat _ | Ctypes.Tfun _ 
    | Ctypes.Tstruct _ | Ctypes.Tunion _ ->
	false

  let is_character_type ctyp = match ctyp.Ctypes.ctype_node with
    | Ctypes.Tvar _ -> 
	assert false (* not allowed in code *)
    | Ctypes.Tint (_,Ctypes.Char) ->
	true
    | Ctypes.Tint _ | Ctypes.Tenum _ ->
	false
    | Ctypes.Tarray _ | Ctypes.Tpointer _ 
    | Ctypes.Tvoid | Ctypes.Tfloat _ | Ctypes.Tfun _ 
    | Ctypes.Tstruct _ | Ctypes.Tunion _ ->
	false

  let var_is_integer var = is_integer_type var.var_type
  let sub_expr_type_is_int e = is_integer_type e.nexpr_type
  let expr_type_is_int node = sub_expr_type_is_int (get_e node)

  let sub_expr_type_is_char e = is_character_type e.nexpr_type
  let expr_type_is_char node = sub_expr_type_is_char (get_e node)

  let is_pointer_type ctyp = match ctyp.Ctypes.ctype_node with
    | Ctypes.Tvar _ -> 
	assert false (* not allowed in code *)
    | Ctypes.Tarray _ | Ctypes.Tpointer _ -> 
	true
    | Ctypes.Tvoid | Ctypes.Tint _ | Ctypes.Tfloat _ | Ctypes.Tfun _ 
    | Ctypes.Tstruct _ | Ctypes.Tunion _ | Ctypes.Tenum _ ->
	false

  let var_is_pointer var = is_pointer_type var.var_type
  let sub_expr_type_is_ptr e = is_pointer_type e.nexpr_type
  let expr_type_is_ptr node = sub_expr_type_is_ptr (get_e node)

  let rec sub_skip_casts e = match e.nexpr_node with
    | NEcast (_,e1) -> sub_skip_casts e1
    | NEunary ((Ufloat_of_int | Uint_of_float
	       | Ufloat_conversion | Uint_conversion),e1) -> sub_skip_casts e1
    | _ -> e
    
  let skip_casts node = 
    let e = get_e node in
    let new_e = sub_skip_casts e in
    create_tmp_node (Nexpr new_e)

  let termexpr_is_local_var node =
    match get_node_kind node with
      | NKexpr | NKtest | NKlvalue -> 
	  begin match get_expr node with
	    | NEvar (Var_info var) -> var_is_local var
	    | _ -> false
	  end
      | NKterm ->
	  begin match get_term node with
	    | NTvar var -> var_is_local var
	    | _ -> false
	  end
      | _ -> assert false

  let termpred_is_old node = 
    match get_node_kind node with
      | NKpred | NKassume | NKassert -> 
	  begin match get_predicate node with
	    | NPold _ -> true
	    | _ -> false
	  end
      | NKterm ->
	  begin match get_term node with
	    | NTold _ -> true
	    | _ -> false
	  end
      | _ -> false

  let termpred_is_at node = 
    match get_node_kind node with
      | NKpred | NKassume | NKassert -> 
	  begin match get_predicate node with
	    | NPat _ -> true
	    | _ -> false
	  end
      | NKterm ->
	  begin match get_term node with
	    | NTat _ -> true
	    | _ -> false
	  end
      | _ -> false

  let termpred_get_label node = 
    match get_node_kind node with
      | NKpred | NKassume | NKassert -> 
	  begin match get_predicate node with
	    | NPat (_,lab) -> lab
	    | _ -> assert false
	  end
      | NKterm ->
	  begin match get_term node with
	    | NTat (_,lab) -> lab
	    | _ -> assert false
	  end
      | _ -> assert false

  let decl_get_params node =
    (get_decl node).f.args

  let stat_is_return node =
    match get_stat node with NSreturn _ -> true | _ -> false

  let stat_is_assert node =
    match get_stat node with NSassert _ -> true | _ -> false

  let stat_is_loop node = match get_stat node with 
      | NSwhile _ | NSdowhile _ | NSfor _ -> true | _ -> false

  let stat_is_if node = 
      match get_stat node with NSif _ -> true | _ -> false

  let stat_is_spec node =
    match get_stat node with NSspec _ -> true | _ -> false

  let stat_is_label node =
    match get_stat node with NSlabel _ | NSlogic_label _ -> true | _ -> false

  let stat_is_decl node =
    match get_stat node with NSdecl _ -> true | _ -> false

  let stat_get_label node =
    match get_stat node with 
      | NSlabel (lab,_) -> lab.label_info_name 
      | NSlogic_label lab -> lab
      | _ -> assert false

  let stat_is_jump node = match get_stat node with
    | NSreturn _ | NSbreak | NScontinue | NSgoto _ -> 
	true
    | NSnop | NSassert _ | NSassume _ | NSlogic_label _ | NSexpr _ 
    | NSif _ | NSwhile _ | NSdowhile _ | NSfor _ | NSblock _ 
    | NSlabel _ | NSspec _ | NSdecl _ | NSswitch _ ->
	false

  let stat_is_block node = 
    match get_stat node with NSblock _ -> true | _ -> false

  let stat_is_nop node =
    match get_stat node with NSnop -> true | _ -> false

  let decl_stat_get_var node =
    match get_stat node with NSdecl (_,var,_,_) -> var | _ -> assert false

  let decl_stat_get_next node =
    match get_stat node with 
      | NSdecl (_,_,_,ns) -> 
	  create_tmp_node (Nstat ns)
      | _ -> assert false
	  
  let expr_is_local_var node = match get_expr node with
    | NEvar (Var_info var) -> var_is_local var
    | _ -> false

  let termexpr_var_get node =
    match get_node_kind node with
      | NKexpr | NKtest | NKlvalue -> 
	  begin match get_expr node with
	    | NEvar (Var_info var) -> var
	    | _ -> assert false
	  end
      | NKterm ->
	  begin match get_term node with
	    | NTvar var -> var
	    | _ -> assert false
	  end
      | _ -> assert false

  let expr_is_int_constant node = match get_expr node with
    | NEconstant (IntConstant s) ->
	begin 
	  try ignore(int_of_string s); true 
	  with Failure "int_of_string" -> false
	end
    | _ -> false

  let expr_int_constant_get node = match get_expr node with
    | NEconstant (IntConstant s) -> 
	begin 
	  try int_of_string s
	  with Failure "int_of_string" -> assert false
	end
    | _ -> assert false

  let expr_is_deref node = match get_expr node with
    | NEarrow _ | NEunary (Ustar,_) -> true
    | _ -> false

  let expr_is_call node = match get_expr node with
    | NEcall _ -> true
    | _ -> false

  let call_get_function node = match get_expr node with
    | NEcall { ncall_fun = fe } -> 
	begin match fe.nexpr_node with
	  | NEvar (Fun_info f) -> Some f
	  | _ -> None
	end
    | _ -> assert false

  let call_get_args node = match get_expr node with
    | NEcall { ncall_args = el } -> 
	List.map (fun e -> create_tmp_node (Nexpr e)) el
    | _ -> assert false

  let function_get_precondition func =
    let func_name = func.fun_name in
    try
      let (spec,_,_,_,_) = Hashtbl.find Cenv.c_functions func_name in
      begin match spec.requires with
	| None -> None
	| Some p -> Some (create_tmp_node (Npred p))
      end
    with Not_found -> None

  let function_get_params func =
    let func_name = func.fun_name in
    try
      let (_,_,f,_,_) = Hashtbl.find Cenv.c_functions func_name in
      f.args
    with Not_found -> []

  let array_and_pointer_types_match arr_typ ptr_typ =
    match arr_typ.Ctypes.ctype_node,ptr_typ.Ctypes.ctype_node with
    | Ctypes.Tarray (avalid,atyp,_),Ctypes.Tpointer (pvalid,ptyp) ->
	avalid = pvalid && (atyp = ptyp)
    | _ -> false

  let change_in_int_offset_cst node index =
    let e = get_e node in
    let cst_e = NEconstant (IntConstant (string_of_int index)) in
    let cst_e = { e with nexpr_node = cst_e; nexpr_type = int_offset_type } in
    create_tmp_node (Nexpr cst_e)

  let deref_get_variable_and_offset node = match get_expr node with
    | NEarrow (e1,_,_) | NEunary (Ustar,e1) -> 
	(* exhaustive case analysis needed here, in order to 
	   guarantee no dereference can be missed *)
	let rec destruct e = match e.nexpr_node with
	    (* dereference from some variable [v] *)
	  | NEvar (Var_info v) -> Some (v,None)
	  | NEcast (typ,e1) ->
	      begin match destruct e1 with
	        | None -> None
		| Some (v,off) ->
		    if array_and_pointer_types_match v.var_type typ then
		      Some (v,off)
		    else None
	      end
	  | NEbinary (e1,Badd_pointer_int,_) | NEincr (_,e1) ->
	      let e2opt = match e.nexpr_node with
		| NEbinary (_,_,e2) -> Some e2
		| NEincr ((Upostfix_inc | Upostfix_dec),_) -> None
		| NEincr (Uprefix_inc,_) ->
		    Some (get_e (change_in_int_offset_cst node 1))
		| NEincr (Uprefix_dec,_) ->
		    Some (get_e (change_in_int_offset_cst node (-1)))
		| _ -> assert false
	      in		    
	      begin match destruct e1 with
	        | None -> None
		| Some (v,None) -> Some (v,e2opt)
		| Some (v,Some off) as e1destr ->
		    begin match e2opt with
		      | None -> e1destr
		      | Some e2 ->
			  let new_off = NEbinary (off,int_offset_addop,e2) in
			  let new_off = { off with nexpr_node = new_off } in
			  Some (v,Some new_off)
		    end
	      end
	    (* not a dereference from a variable *)
	  | NEarrow _ | NEunary (Ustar,_) | NEcall _ -> None
	    (* other cases should not be possible *)
	  | _ -> assert false
	in
	(* return a node as offset *)
	begin match destruct e1 with
	  | None -> None
	  | Some (v,None) -> Some (v,None)
	  | Some (v,Some e) -> Some (v,Some (create_tmp_node (Nexpr e)))
	end
    | _ -> assert false

  let deref_get_local_var node =
    match deref_get_variable_and_offset node with
      | None -> None
      | Some (var,_) -> if var_is_local var then Some var else None
    
  let sub_expr_is_assign e = match e.nexpr_node with
    | NEincr (_,e1) | NEassign (e1,_) | NEassign_op (e1,_,_) -> true
    | _ -> false      
	  
  let expr_is_assign node = 
    sub_expr_is_assign (get_e node)

  let sub_expr_is_int_assign e =
    sub_expr_is_assign e && (sub_expr_type_is_int e)
	
  let expr_is_int_assign node =
    sub_expr_is_int_assign (get_e node)

  let sub_expr_is_ptr_assign e =
    sub_expr_is_assign e && (sub_expr_type_is_ptr e)
    
  let expr_is_ptr_assign node =
    sub_expr_is_ptr_assign (get_e node)

  let assign_get_rhs_operand node = match (get_expr node) with
    | NEincr (_,e2) | NEassign (_,e2) ->
	create_tmp_node (Nexpr e2)
    | NEassign_op (e1,op,e2) -> 
	let rhs_e = NEbinary (e1,op,e2) in
	let rhs_e = { e1 with nexpr_node = rhs_e } in
	create_tmp_node (Nexpr rhs_e)
    | _ -> assert false

  let assign_get_lhs_operand node = match (get_expr node) with
    | NEincr (_,e1) | NEassign (e1,_) | NEassign_op (e1,_,_) -> 
	create_tmp_node (Nexpr e1)
    | _ -> assert false

  let sub_assign_get_lhs_var e = match e.nexpr_node with
    | NEincr (_,e1) | NEassign (e1,_) | NEassign_op (e1,_,_) ->
	begin
	  (* exhaustive case analysis needed here, in order to 
	     guarantee no assignment can be missed *)
	  match e1.nexpr_node with
	      (* cast not allowed here for the moment, it is checked
		 and error possibly reported in an earlier stage *)
	    | NEcast _ -> assert false
		(* not an assignment to a variable *)
	    | NEarrow _ | NEunary (Ustar,_) -> None
		(* assignment to some variable [var] *)
	    | NEvar (Var_info var) -> Some var
		(* other cases should not be possible *)
	    | _ -> assert false
	end
    | _ -> failwith ("[sub_assign_get_lhs_var] should be called"
		     ^ " only on assignment")

  let assign_get_lhs_var node =
    sub_assign_get_lhs_var (get_e node)

  let assign_get_local_lhs_var node =
    match assign_get_lhs_var node with
      | None -> None
      | Some var -> if var_is_local var then Some var else None

  (* construction of the graph(s), i.e. operational + structural *)

  (* [successors n] returns the operational successors of node [n] 
     for the dataflow analysis. Only [OperationalFwd] and [OperationalBwd]
     labels should be taken into account. 
     It is also the case for [predecessors]. *)
  let successors ?(ignore_looping=false) n = 
    let el = Self.succ_e (graph ()) n in
    let el = List.filter 
	(fun e -> 
	  Edge.label e = NodeRelation.OperationalFwd
	  || ((not ignore_looping)
              && (Edge.label e = NodeRelation.OperationalBwd))
        ) el in
    List.map Edge.dst el

  let predecessors ?(ignore_looping=false) n =
    let el = Self.pred_e (graph ()) n in
    let el = List.filter 
	(fun e -> 
	  Edge.label e = NodeRelation.OperationalFwd
	  || ((not ignore_looping)
              && (Edge.label e = NodeRelation.OperationalBwd))
        ) el in
    List.map Edge.src el

  let looping_source n =
    assert (is_loop_backward_destination_node n);
    let pl = predecessors n in
    let pl = List.filter is_loop_backward_source_node pl in
    match pl with
      | [sn] -> sn
      | _ -> assert false

  (* hierarchical successors. Used for structural and logical successors. *)
  let succ edge n = 
    let el = Self.succ_e (graph ()) n in
    let el = List.filter (fun e -> Edge.label e = edge) el in
    match List.map Edge.dst el with
      | [] -> None
      | [a] -> Some a
      | _ -> failwith ("[succ edge n] should find at most one successor"
		       ^ " for node [n]")
  (* hierarchical predecessors. Used for logical scopes. *)
  let has_pred edge n = 
    let el = Self.pred_e (graph ()) n in
    let el = List.filter (fun e -> Edge.label e = edge) el in
    match List.map Edge.src el with
      | [] -> false
      | _ -> true

  let child ?(structural=false) ?(logical=false) n = 
    if structural then
      succ NodeRelation.StructuralDown n
    else if logical then
      succ NodeRelation.LogicalDown n
    else (* no graph was specified, it is an error *)
      assert false
  let sibling ?(structural=false) ?(logical=false) n = 
    if structural then
      succ NodeRelation.StructuralSame n
    else if logical then 
      succ NodeRelation.LogicalSame n
    else (* no graph was specified, it is an error *)
      assert false
  let rec siblings ?(structural=false) ?(logical=false) n =
    match sibling ~structural ~logical n with
      | None -> []
      | Some m -> m :: (siblings ~structural ~logical m)
  let children ?(structural=false) ?(logical=false) n =
    match child ~structural ~logical n with
      | None -> []
      | Some m -> m :: (siblings ~structural ~logical m)

  (* structural successors. Only [StructuralDown] and [StructuralSame]
     labels should be taken into account. *)
  let code_children = children ~structural:true ~logical:false

  (* logical successors. Only [LogicalDown] and [LogicalSame]
     labels should be taken into account. *)
  let logic_children = children ~structural:false ~logical:true

  let logic_begin n = 
    match succ NodeRelation.LogicScopeBegin n with
      | Some nb -> nb
      | None -> failwith "no logical beginning node found"
  let logic_end n = 
    match succ NodeRelation.LogicScopeEnd n with
      | Some ne -> ne
      | None -> failwith "no logical end node found"

  let logic_invariant n = succ NodeRelation.LogicInvariant n

  let is_logic_begin n = has_pred NodeRelation.LogicScopeBegin n

  let is_logic_end n = has_pred NodeRelation.LogicScopeEnd n

  let is_logic_scope n = is_logic_begin n || is_logic_end n

  let add_edge edge v1 v2 =
    let e = Edge.create v1 edge v2 in
    Self.add_edge_e (graph ()) e

  (* topological order used by operations of iteration below.
     - [direction] is either forward or backward.
     - [roots] is empty for a classical propagation, and a single element
     for a propagation from a distinguished point of the program. It could
     be used also to propagate from a set of distinguished points, although
     not useful for now.
     - [sub_graph] is used when propagating from (a) distinguished point(s),
     to identify nodes in the resulting DAG.
  *)

  module OperationalTopologicalOrder =
    struct
      type t = 
	 {
	   direction : direction_t;
	   roots : NodeSet.t;
	   mutable sub_graph : NodeSet.t;
	 }

      module V = Node

      let iter_vertex f ord =
	if NodeSet.is_empty ord.roots then
	  Self.iter_vertex f (graph ())
	else
	  (* roots are forced. Force the input-degree too. *)
	  let work_set = ref ord.roots in
	  while (not (NodeSet.is_empty (!work_set))) do
	    let cur_work_set = !work_set in
	    work_set := NodeSet.empty;
	    ord.sub_graph <- NodeSet.union ord.sub_graph cur_work_set;
	    NodeSet.iter (fun node ->
	      let succ_nodes = match ord.direction with
	      | Forward -> successors ~ignore_looping:true node
	      | Backward -> predecessors ~ignore_looping:true node
	      in
	      List.iter (fun nx_node ->
		if not (NodeSet.mem nx_node ord.sub_graph) then
		  work_set := NodeSet.add nx_node !work_set) succ_nodes
	    ) cur_work_set
	  done;
	  Self.iter_vertex f (graph ())

      let iter_succ f ord node =
	let succ_nodes = match ord.direction with
	| Forward -> successors ~ignore_looping:true node
	| Backward -> predecessors ~ignore_looping:true node
	in
	let succ_nodes = 
	  if NodeSet.is_empty ord.roots then succ_nodes
	  else List.filter 
	      (fun node -> not (NodeSet.mem node ord.roots)) succ_nodes
	in
	List.iter f succ_nodes

      let in_degree ord node = 
	let pred_nodes = match ord.direction with
	| Forward -> predecessors ~ignore_looping:true node
	| Backward -> successors ~ignore_looping:true node
	in
	if NodeSet.is_empty ord.roots then
	  let pred_length = List.length pred_nodes in
	  if pred_length = 0 then
	    let succ_nodes = match ord.direction with
	    | Forward -> successors ~ignore_looping:true node
	    | Backward -> predecessors ~ignore_looping:true node
	    in
	    let succ_length = List.length succ_nodes in
	    (* ignore nodes not in the operational graph *)
	    if succ_length = 0 then 1
	    else pred_length
	  else pred_length
	else
	  (* only nodes in path should be taken into account *)
	  let pred_nodes = List.filter 
	      (fun node -> NodeSet.mem node ord.sub_graph) pred_nodes in
	  let pred_length = List.length pred_nodes in
	  (* force the nodes given as root to be such *)
	  if NodeSet.mem node ord.roots then 0
	  (* real root nodes or nodes not in the operational graph 
	     should be ignored in that case *)
	  else if pred_length = 0 then 1
	  else pred_length
    end

  module OperationalIterator = 
    Graph.Topological.Make (OperationalTopologicalOrder)

  let iter_operational direction ~roots f =
    let ord = {
      OperationalTopologicalOrder.direction = direction;
      OperationalTopologicalOrder.roots = 
      List.fold_right NodeSet.add roots NodeSet.empty;
      OperationalTopologicalOrder.sub_graph = NodeSet.empty;
    } in
    OperationalIterator.iter f ord

  let fold_operational direction ~roots f init =
    let ord = {
      OperationalTopologicalOrder.direction = direction;
      OperationalTopologicalOrder.roots =
      List.fold_right NodeSet.add roots NodeSet.empty;
      OperationalTopologicalOrder.sub_graph = NodeSet.empty;
    } in
    OperationalIterator.fold f ord init

  (* add an operational edge.
     - [force_add_opedge] should be used for edges that originate in 
     a jumping statement like a return/break/continue/goto.
     - [add_opedge] should be used in all other cases, with the knowledge
     that an edge will be added only if the source statement is not
     a jumping one. *)
  let force_add_opedge v1 v2 = Self.add_edge (graph ()) v1 v2
  let add_opedge v1 v2 =
    match get_node_kind v1 with
      | NKstat ->
	  if stat_is_jump v1 then
	    (* normal operational edges should not originate from
	       a jumping statement, in which case [force_add_opedge]
	       is used. Do nothing. *)
	    ()
	  else force_add_opedge v1 v2
      | _ -> force_add_opedge v1 v2

  (* add backward operational edge, for loops *)
  let add_backedge = add_edge NodeRelation.OperationalBwd
    
  (* add hierarchical edges. Used for adding structural and logical edges. *)
  let add_edges is_structural v vl = 
    let add_down_edge = 
      if is_structural then
	add_edge NodeRelation.StructuralDown 
      else
	add_edge NodeRelation.LogicalDown
    in
    let add_same_edge = 
      if is_structural then
	add_edge NodeRelation.StructuralSame
      else
	add_edge NodeRelation.LogicalSame
    in
    let rec add_same_edges vl = match vl with
      | a::b::r -> add_same_edge a b; add_same_edges (b::r)
      | _ -> ()
    in
    match vl with
      | [] -> ()
      | a::r -> add_down_edge v a; add_same_edges vl

  (* add structural edges: a [StructuralDown] edge from [v] to the first
     node in the list [vl], and [StructuralSame] edges between successive
     nodes in [vl]. Nothing to do with your hat's edge. *)
  let add_stedge = add_edges (* is_structural= *)true

  (* add logical edges: a [LogicalDown] edge from [v] to the first
     node in the list [vl], and [LogicalSame] edges between successive
     nodes in [vl]. Do not misinterpret it as a command to add luggage. *)
  let add_logedge = add_edges (* is_structural= *)false

  (* add logical block edge *)
  let add_begedge = add_edge NodeRelation.LogicScopeBegin
  let add_endedge = add_edge NodeRelation.LogicScopeEnd
  let add_invedge = add_edge NodeRelation.LogicInvariant

  (* constructors *)

  let make_seq_expr node1 node2 =
    let e1 = get_e node1 in
    let e2 = get_e node2 in
    let new_e = NEseq (e1,e2) in
    let new_e = { e2 with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  let make_int_expr_add_cst node cst =
    let e = get_e node in
    let typ = e.nexpr_type in
    let op = match typ.Ctypes.ctype_node with 
      | Ctypes.Tint ik -> Badd_int ik
      | _ -> failwith ("[make_int_expr_add_cst] should only be called on"
		       ^ " integer expression") in
    let cst_e = NEconstant (IntConstant (string_of_int cst)) in
    let cst_e = { e with nexpr_node = cst_e } in
    let new_e = NEbinary (e,op,cst_e) in
    let new_e = { e with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  let make_int_expr_add_var node var =
    let e = get_e node in
    let typ = e.nexpr_type in
    let op = match typ.Ctypes.ctype_node with 
      | Ctypes.Tint ik -> Badd_int ik
      | _ -> failwith ("[make_int_expr_add_cst] should only be called on"
		       ^ " integer expression") in
    let cst_e = NEvar (Var_info var) in
    let cst_e = { e with nexpr_node = cst_e } in
    (* prefer var + expr to expr + var, for ergonomic issues *)
    let new_e = NEbinary (cst_e,op,e) in
    let new_e = { e with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  let make_int_termexpr_add_termexpr node1 node2 =
    match get_node_kind node1 with
      | NKexpr | NKtest | NKlvalue -> 
	  let e1 = get_e node1 in
	  let e2 = get_e node2 in
	  let typ = e1.nexpr_type in
	  let op = match typ.Ctypes.ctype_node with 
	    | Ctypes.Tint ik -> Badd_int ik
	    | _ -> failwith ("[make_int_termexpr_add_termexpr] should only"
			     ^ " be called on integer expression") in
	  let new_e = NEbinary (e1,op,e2) in
	  let new_e = { e1 with nexpr_node = new_e } in
	  create_tmp_node (Nexpr new_e)    
      | NKterm ->
	  let t1 = get_t node1 in
	  let t2 = get_t node2 in
	  let op = Clogic.Badd in
	  let new_t = NTbinop (t1,op,t2) in
	  let new_t = { t1 with nterm_node = new_t } in
	  create_tmp_node (Nterm new_t)
      | _ -> assert false

  let make_var_decl node var =
    let s = get_s node in
    let new_s = NSdecl (var.var_type,var,None,s) in
    let new_s = { s with nst_node = new_s } in
    create_tmp_node (Nstat new_s)

  let make_seq_stat node1 node2 =
    let s1 = get_s node1 in
    let s2 = get_s node2 in
    let new_s = NSblock [s1;s2] in
    let new_s = { s1 with nst_node = new_s } in
    create_tmp_node (Nstat new_s)

  (* changes the expression's type if necessary *)
  let change_in_int_var_assign_cst node var index =
    let e = get_e node in
    let var_e = NEvar (Var_info var) in
    let typ_e = var.var_type in
    let var_e = { e with nexpr_node = var_e; nexpr_type = typ_e } in
    let cst_e = NEconstant (IntConstant (string_of_int index)) in
    let cst_e = { e with nexpr_node = cst_e; nexpr_type = typ_e } in
    let new_e = NEassign (var_e, cst_e) in
    let new_e = { e with nexpr_node = new_e; nexpr_type = typ_e } in
    create_tmp_node (Nexpr new_e)
    
  (* changes the expression's type if necessary *)
  let change_in_int_var_assign_var node var other_var =
    let e = get_e node in
    let var_e = NEvar (Var_info var) in
    let typ_e = var.var_type in
    let var_e = { e with nexpr_node = var_e; nexpr_type = typ_e } in
    let cst_e = NEvar (Var_info other_var) in
    let cst_e = { e with nexpr_node = cst_e; nexpr_type = typ_e } in
    let new_e = NEassign (var_e, cst_e) in
    let new_e = { e with nexpr_node = new_e; nexpr_type = typ_e } in
    create_tmp_node (Nexpr new_e)
      
  (* changes the expression's type if necessary *)
  let change_in_int_var_assign_expr node var sub_node =
    let e = get_e node in
    let var_e = NEvar (Var_info var) in
    let typ_e = var.var_type in
    let var_e = { e with nexpr_node = var_e; nexpr_type = typ_e } in
    let cst_e = get_e sub_node in
    let new_e = NEassign (var_e, cst_e) in
    let new_e = { e with nexpr_node = new_e; nexpr_type = typ_e } in
    create_tmp_node (Nexpr new_e)    

  (* to be matched with [encode_decl_list] below *)
  let rec decode_decl_list einit = match einit.nexpr_node with
    | NEcall ce ->
	let caller = ce.ncall_fun in
	if caller.nexpr_node = NEnop then
	  (* encoding of an initialization list *)
	  let args = ce.ncall_args in
	  Ilist (List.map decode_decl_list args)
	else
	  (* only other possibility is expression *)
	  Iexpr einit
    | _ -> Iexpr einit

  let change_sub_components_in_stat node sub_nodes =
    let s = get_s node in
    let new_s = match s.nst_node with
      | NSnop | NSlogic_label _ ->
	  assert (List.length sub_nodes = 0);
	  s.nst_node
      | NSassert _ ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NSassert new_p1	  
      | NSassume _ ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NSassume new_p1	  
      | NSexpr e -> 
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NSexpr new_e
      | NSif (e,s1,s2) ->
	  assert (List.length sub_nodes = 3);
	  let new_e,new_s1,new_s2 = list3 sub_nodes in
	  let new_e,new_s1,new_s2 
	    = get_e new_e,get_s new_s1,get_s new_s2 in
	  NSif (new_e,new_s1,new_s2)
      | NSwhile (annot,e,s1) ->
	  assert (List.length sub_nodes = 3);
	  let new_e,new_s1,new_a = list3 sub_nodes in
	  let new_e,new_s1,new_a = get_e new_e,get_s new_s1,get_annot new_a in
	  NSwhile (new_a,new_e,new_s1)
      | NSdowhile (annot,s1,e) ->
	  assert (List.length sub_nodes = 3);
	  let new_s1,new_e,new_a = list3 sub_nodes in
	  let new_s1,new_e,new_a = get_s new_s1,get_e new_e,get_annot new_a in
	  NSdowhile (new_a,new_s1,new_e)
      | NSfor (annot,einit,etest,eincr,s1) ->
	  assert (List.length sub_nodes = 5);
	  let new_einit,new_etest,new_eincr,new_s1,new_a = list5 sub_nodes in
	  let new_einit,new_etest,new_eincr,new_s1,new_a
	    = get_e new_einit,get_e new_etest,
	    get_e new_eincr,get_s new_s1,get_annot new_a in
	  NSfor (new_a,new_einit,new_etest,new_eincr,new_s1)
      | NSblock sl ->
	  let new_sl = List.map get_s sub_nodes in
	  NSblock new_sl
      | NSreturn None ->
	  assert (List.length sub_nodes = 0);
	  s.nst_node
      | NSreturn (Some e) -> 
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NSreturn (Some new_e)
      | NSbreak | NScontinue | NSgoto _ -> 
	  assert (List.length sub_nodes = 0);
	  s.nst_node
      | NSlabel (str,s1) ->
	  assert (List.length sub_nodes = 1);
	  let new_s = list1 sub_nodes in
	  let new_s = get_s new_s in
	  NSlabel (str,new_s)
      | NSspec (spec,s1) ->
	  assert (List.length sub_nodes = 2);
	  let new_s,new_spc = list2 sub_nodes in
	  let new_s,new_spc = get_s new_s,get_spec new_spc in
	  NSspec (new_spc,new_s)
      | NSdecl (typ,var,None,s1) ->
	  assert (List.length sub_nodes = 1);
	  let new_s = list1 sub_nodes in
	  let new_s = get_s new_s in
	  NSdecl (typ,var,None,new_s)
      | NSdecl (typ,var,Some cinit,s1) ->
	  assert (List.length sub_nodes = 2);
	  let new_e,new_s1 = list2 sub_nodes in
	  let new_e = get_e new_e in
	  let new_s1 = get_s new_s1 in
	  let lhs_expr,rhs_expr = match new_e.nexpr_node with 
	    | NEassign (lhs_expr,rhs_expr) -> lhs_expr,rhs_expr
	    | _ -> assert false in
	  let new_var = match lhs_expr.nexpr_node with
	    | NEvar (Var_info new_var) -> new_var
	    | _ -> assert false
	  in
	  assert (ILVar.equal var new_var);
	  let new_cinit = decode_decl_list rhs_expr in
	  NSdecl (typ,var,Some new_cinit,new_s1)
      | NSswitch (e,c,cases) -> 
	  let new_e = List.hd sub_nodes in
	  let new_cases = List.tl sub_nodes in
	  let new_e = get_e new_e in
	  (* remove [Nintern] node introduced for each [case] *)
	  let new_cases = 
	    List.map (fun n -> code_children n) new_cases in
	  let new_cases = List.map (List.map get_s) new_cases in
	  let new_cases = 
	    List.map2 (fun (cmap,_) sl -> (cmap,sl)) cases new_cases in
	  NSswitch (new_e,c,new_cases)
    in
    let new_s = { s with nst_node = new_s } in
    create_tmp_node (Nstat new_s)

  let change_sub_components_in_expr node sub_nodes =
    let e = get_e node in
    let new_e = match e.nexpr_node with
      | NEnop | NEconstant _ | NEstring_literal _ | NEvar _ ->
	  assert (List.length sub_nodes = 0);
	  e.nexpr_node
      | NEarrow (e1,zone,var) ->
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NEarrow (new_e,zone,var)
      | NEunary (op,e1) ->
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NEunary (op,new_e)
      | NEincr (op,e1) ->
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NEincr (op,new_e)
      | NEcast (typ,e1) ->
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NEcast (typ,new_e)
      | NEmalloc (typ,e1) ->
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  NEmalloc (typ,new_e)
      | NEseq (e1,e2) ->
	  assert (List.length sub_nodes = 2);
	  let new_e1,new_e2 = list2 sub_nodes in
	  let new_e1,new_e2 = get_e new_e1,get_e new_e2 in
	  NEseq (new_e1,new_e2)
      | NEassign (e1,e2) ->
	  assert (List.length sub_nodes = 2);
	  let new_e1,new_e2 = list2 sub_nodes in
	  let new_e1,new_e2 = get_e new_e1,get_e new_e2 in
	  NEassign (new_e1,new_e2)
      | NEassign_op (e1,op,e2) ->
	  assert (List.length sub_nodes = 2);
	  let new_e1,new_e2 = list2 sub_nodes in
	  let new_e1,new_e2 = get_e new_e1,get_e new_e2 in
	  NEassign_op (new_e1,op,new_e2)
      | NEbinary (e1,op,e2) ->
	  assert (List.length sub_nodes = 2);
	  let new_e1,new_e2 = list2 sub_nodes in
	  let new_e1,new_e2 = get_e new_e1,get_e new_e2 in
	  NEbinary (new_e1,op,new_e2)
      | NEcall c ->
	  let new_f = List.hd sub_nodes in
	  let new_args = List.tl sub_nodes in
	  let new_f = get_e new_f in
	  let new_args = List.map get_e new_args in
	  NEcall { c with ncall_fun = new_f; ncall_args = new_args }
      | NEcond (e1,e2,e3) ->
	  assert (List.length sub_nodes = 3);
	  let new_e1,new_e2,new_e3 = list3 sub_nodes in
	  let new_e1,new_e2,new_e3 
	    = get_e new_e1,get_e new_e2,get_e new_e3 in
	  NEcond (new_e1,new_e2,new_e3)
    in		
    let new_e = { e with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  let change_sub_components_in_term node sub_nodes =
    let t = get_t node in
    let new_t = match t.nterm_node with
      | NTconstant _ | NTvar _ | NTminint _ | NTmaxint _ -> 
	  assert (List.length sub_nodes = 0);
	  t.nterm_node
      | NTapp a ->
	  let new_args = sub_nodes in
	  let new_args = List.map get_t new_args in
	  NTapp { a with napp_args = new_args }
      | NTunop (op,t1) ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTunop (op,new_t)
      | NTarrow (t1,zone,var) ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTarrow (new_t,zone,var)
      | NTold t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTold new_t
      | NTat (t1,label) ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTat (new_t,label)
      | NTbase_addr t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTbase_addr new_t
      | NToffset t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NToffset new_t
      | NTblock_length t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTblock_length new_t
      | NTarrlen t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTarrlen new_t
      | NTstrlen (t1,zone,var) ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTstrlen (new_t,zone,var)
      | NTmin (t1,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NTmin (new_t1,new_t2)
      | NTmax (t1,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NTmax (new_t1,new_t2)
      | NTcast (typ,t1) ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NTcast (typ,new_t)
      | NTbinop (t1,op,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NTbinop (new_t1,op,new_t2)
      | NTif (t1,t2,t3) ->
	  assert (List.length sub_nodes = 3);
	  let new_t1,new_t2,new_t3 = list3 sub_nodes in
	  let new_t1,new_t2,new_t3 
	    = get_t new_t1,get_t new_t2,get_t new_t3 in
	  NTif (new_t1,new_t2,new_t3)
      | NTrange (t1,t2opt,t3opt,zone,info) ->
	  assert (List.length sub_nodes = 3);
	  let new_t1,new_t2,new_t3 = list3 sub_nodes in
	  let new_t1 = get_t new_t1 in
	  let new_t2 = match logic_children new_t2 with
	    | [new_t2] -> Some (get_t new_t2)
	    | [] -> None
	    | _ -> assert false (* bad encoding *)
	  in
	  let new_t3 = match logic_children new_t3 with
	    | [new_t3] -> Some (get_t new_t3)
	    | [] -> None
	    | _ -> assert false (* bad encoding *)
	  in
	  NTrange (new_t1,new_t2,new_t3,zone,info)
    in		
    let new_t = { t with nterm_node = new_t } in
    create_tmp_node (Nterm new_t)

  let change_sub_components_in_pred node sub_nodes =
    let p = get_p node in
    let new_p = match p.npred_node with
      | NPfalse | NPtrue -> 
	  assert (List.length sub_nodes = 0);
	  p.npred_node
      | NPapp a ->
	  let new_args = sub_nodes in
	  let new_args = List.map get_t new_args in
	  NPapp { a with napp_args = new_args }
      | NPrel (t1,rel,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NPrel (new_t1,rel,new_t2)
      | NPvalid_index (t1,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NPvalid_index (new_t1,new_t2)
      | NPand (p1,p2) ->
	  assert (List.length sub_nodes = 2);
	  let new_p1,new_p2 = list2 sub_nodes in
	  let new_p1,new_p2 = get_p new_p1,get_p new_p2 in
	  NPand (new_p1,new_p2)		
      | NPor (p1,p2) ->
	  assert (List.length sub_nodes = 2);
	  let new_p1,new_p2 = list2 sub_nodes in
	  let new_p1,new_p2 = get_p new_p1,get_p new_p2 in
	  NPor (new_p1,new_p2)		
      | NPimplies (p1,p2) ->
	  assert (List.length sub_nodes = 2);
	  let new_p1,new_p2 = list2 sub_nodes in
	  let new_p1,new_p2 = get_p new_p1,get_p new_p2 in
	  NPimplies (new_p1,new_p2)		
      | NPiff (p1,p2) ->
	  assert (List.length sub_nodes = 2);
	  let new_p1,new_p2 = list2 sub_nodes in
	  let new_p1,new_p2 = get_p new_p1,get_p new_p2 in
	  NPiff (new_p1,new_p2)		
      | NPnot p1 ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPnot new_p1
      | NPforall (q,p1) ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPforall (q,new_p1)
      | NPexists (q,p1) ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPexists (q,new_p1)
      | NPold p1 ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPold new_p1
      | NPat (p1,label) ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPat (new_p1,label)
      | NPnamed (name,p1) ->
	  assert (List.length sub_nodes = 1);
	  let new_p1 = list1 sub_nodes in
	  let new_p1 = get_p new_p1 in
	  NPnamed (name,new_p1)
      | NPif (t1,p2,p3) ->
	  assert (List.length sub_nodes = 3);
	  let new_t1,new_p2,new_p3 = list3 sub_nodes in
	  let new_t1,new_p2,new_p3
	    = get_t new_t1,get_p new_p2,get_p new_p3 in
	  NPif (new_t1,new_p2,new_p3)		
      | NPvalid t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NPvalid new_t
      | NPfresh t1 ->
	  assert (List.length sub_nodes = 1);
	  let new_t = list1 sub_nodes in
	  let new_t = get_t new_t in
	  NPfresh new_t
      | NPvalid_range (t1,t2,t3) ->
	  assert (List.length sub_nodes = 3);
	  let new_t1,new_t2,new_t3 = list3 sub_nodes in
	  let new_t1,new_t2,new_t3 
	    = get_t new_t1,get_t new_t2,get_t new_t3 in
	  NPvalid_range (new_t1,new_t2,new_t3)
      | NPseparated (t1,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NPseparated (new_t1,new_t2)
      | NPfull_separated (t1,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  NPfull_separated (new_t1,new_t2)
      | NPbound_separated (t1,t2,t3,t4) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2,new_t3,new_t4 = list4 sub_nodes in
	  let new_t1,new_t2,new_t3,new_t4 =
	    get_t new_t1,get_t new_t2,get_t new_t3,get_t new_t4 in
	  NPbound_separated (new_t1,new_t2,new_t3,new_t4)
    in		
    let new_p = { p with npred_node = new_p } in
    create_tmp_node (Npred new_p)

  let change_sub_components_in_annot node sub_nodes =
    let a = get_annot node in
    assert (List.length sub_nodes = 4);
    let new_inv,new_assinv,new_ass,new_var = list4 sub_nodes in
    let new_inv = match logic_children new_inv with
      | [new_inv] -> 
	  let new_p = get_p new_inv in
          (* remove useless assert, possibly inserted by the analysis *)
	  begin match new_p.npred_node with
	    | NPtrue -> None 
	    | _ -> Some new_p
	  end
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_assinv = match logic_children new_assinv with
      | [new_assinv] ->
	  let new_p = get_p new_assinv in
          (* remove useless assume, possibly inserted by the analysis *)
	  begin match new_p.npred_node with
	    | NPtrue -> None 
	    | _ -> Some new_p
	  end
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_ass = match logic_children new_ass with
      | [new_ass] ->
	  let new_ass = List.map get_t (logic_children new_ass) in
	  Some (Loc.dummy_position, new_ass)
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let name_var = match a.variant with
      | None -> None
      | Some (_,so) -> so in
    let new_var = match logic_children new_var with
      | [new_var] -> Some (get_t new_var,name_var)
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_a = { invariant = new_inv; assume_invariant = new_assinv;
		  loop_assigns = new_ass; variant = new_var } in
    create_tmp_node (Nannot new_a)

  let change_sub_components_in_spec node sub_nodes =
    let s = get_spec node in
    assert (List.length sub_nodes = 4);
    let new_req,new_ass,new_ens,new_dec = list4 sub_nodes in
    let new_req = match logic_children new_req with
      | [new_req] ->
	  let new_p = get_p new_req in
          (* remove useless assume, possibly inserted by the analysis *)
	  begin match new_p.npred_node with
	    | NPtrue -> None 
	    | _ -> Some new_p
	  end
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_ass = match logic_children new_ass with
      | [new_ass] ->
	  let new_ass = List.map get_t (logic_children new_ass) in
	  Some (Loc.dummy_position, new_ass)
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_ens = match logic_children new_ens with
      | [new_ens] -> Some (get_p new_ens)
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let name_dec = match s.decreases with
      | None -> None
      | Some (_,so) -> so in
    let new_dec = match logic_children new_dec with
      | [new_dec] -> Some (get_t new_dec,name_dec)
      | [] -> None
      | _ -> assert false (* bad encoding *)
    in
    let new_s = { requires = new_req; assigns = new_ass;
		  ensures = new_ens; decreases = new_dec } in
    create_tmp_node (Nspec new_s)

  let change_sub_components node sub_nodes =
    match get_node_kind node with
      | NKnone ->
	  (* forward node for upper level. Rebuild the correct one-level 
	     sub-graph so that the upper level can rely on it if necessary.
	     Chained forward nodes can recreate more than one level. *)
	  let is_structural = fwd_is_structural node in
	  let is_logical = fwd_is_logical node in
	  let fwd_node = 
             if is_structural then create_node (Nintern InternStructural)
             else if is_logical then create_node (Nintern InternLogical)
             else 
               (* only forward nodes from the hierarchical graphs should be
		  reaching here *)
               assert false
          in
	  if is_structural then
	    add_stedge fwd_node sub_nodes
	  else
	    add_logedge fwd_node sub_nodes;
	  fwd_node
	  
      | NKdecl ->
	  let d = get_decl node in
	  let new_d =
	    match d.s with
	      | Some s ->
		  let new_s,new_spec = list2 sub_nodes in
		  let new_s,new_spec = get_s new_s,get_spec new_spec in
		  { d with s = Some new_s; spec = new_spec }
	      | _ -> d
	  in
	  create_tmp_node (Ndecl new_d)

      | NKstat ->
	  change_sub_components_in_stat node sub_nodes
	    
      | NKexpr | NKtest | NKlvalue ->
	  change_sub_components_in_expr node sub_nodes
	    
      | NKterm ->
	  change_sub_components_in_term node sub_nodes
	    
      | NKpred | NKassume | NKassert -> 
	  change_sub_components_in_pred node sub_nodes

      | NKannot ->
	  change_sub_components_in_annot node sub_nodes
	    
      | NKspec ->
	  change_sub_components_in_spec node sub_nodes

  (* extraction of graph from normalized AST *)

  let rec from_term (t : nterm) = 
    let tnode = create_node (Nterm t) in
    begin
      match t.nterm_node with
	| NTconstant _ | NTvar _ | NTminint _ | NTmaxint _ -> ()
	| NTapp a ->
	    let args_nodes = List.map from_term a.napp_args in
	    (* logic *) add_logedge tnode args_nodes
	| NTunop (_,t1) | NTarrow (t1,_,_) | NTold t1 | NTat (t1,_)
	| NTbase_addr t1 | NToffset t1 | NTblock_length t1 
	| NTarrlen t1 | NTstrlen (t1,_,_) | NTcast (_,t1) ->
	    let t1node = from_term t1 in
	    (* logic *) add_logedge tnode [t1node]
	| NTbinop (t1,_,t2) | NTmin (t1,t2) | NTmax (t1,t2) ->
	    let t1node = from_term t1 in
	    let t2node = from_term t2 in
	    (* logic *) add_logedge tnode [t1node; t2node]
	| NTif (t1,t2,t3) ->
	    let t1node = from_term t1 in
	    let t2node = from_term t2 in
	    let t3node = from_term t3 in
	    (* logic *) add_logedge tnode [t1node; t2node; t3node]
	| NTrange (t1,t2opt,t3opt,_,_) ->
	    let t1node = from_term t1 in
	    let t2node = create_node (Nintern InternLogical) in
	    begin match t2opt with 
	      | Some t2 ->
		  let t2optnode = from_term t2 in
		  (* logic *) add_logedge t2node [t2optnode]
	      | None -> ()
	    end;
	    let t3node = create_node (Nintern InternLogical) in
	    begin match t3opt with 
	      | Some t3 ->
		  let t3optnode = from_term t3 in
		  (* logic *) add_logedge t3node [t3optnode]
	      | None -> ()
	    end;
	    (* logic *) add_logedge tnode [t1node; t2node; t3node]
    end;
    tnode

  let rec from_pred ?(is_assume=false) ?(is_funpre=false)
   ?(is_assert=false) ?(is_invariant=false) ?(writes=w_empty) 
   (p : npredicate) =
    let pnode = 
      if is_invariant then 
	if is_assume then Nassinv (p,writes)
	else if is_assert then Ninv (p,writes)
	else assert false
      else if is_assume then Nassume p
      else if is_funpre then Npre p
      else if is_assert then Nassert p
      else Npred p
    in
    let pnode = create_node pnode in
    begin
      match p.npred_node with
	| NPfalse | NPtrue -> ()
	| NPapp a ->
	    let args_nodes = List.map from_term a.napp_args in
	    (* logic *) add_logedge pnode args_nodes
	| NPrel (t1,_,t2) | NPvalid_index (t1,t2) | NPseparated (t1,t2) 
	| NPfull_separated (t1,t2) ->
	    let t1node = from_term t1 in
	    let t2node = from_term t2 in
	    (* logic *) add_logedge pnode [t1node; t2node]
	| NPbound_separated (t1,t2,t3,t4) ->
  	    let t1node = from_term t1 in
	    let t2node = from_term t2 in
	    let t3node = from_term t3 in
	    let t4node = from_term t4 in
	    (* logic *) add_logedge pnode [t1node; t2node; t3node; t4node]
	| NPand (p1,p2) | NPor (p1,p2) | NPimplies (p1,p2) | NPiff (p1,p2) ->
	    let p1node = from_pred p1 in
	    let p2node = from_pred p2 in
	    (* logic *) add_logedge pnode [p1node; p2node]
	| NPnot p1 | NPforall (_,p1) | NPexists (_,p1) | NPold p1 
	| NPat (p1,_) | NPnamed (_,p1) ->
	    let p1node = from_pred p1 in
	    (* logic *) add_logedge pnode [p1node]
	| NPif (t1,p2,p3) ->
	    let t1node = from_term t1 in
	    let p2node = from_pred p2 in
	    let p3node = from_pred p3 in
	    (* logic *) add_logedge pnode [t1node; p2node; p3node]
	| NPvalid t1 | NPfresh t1 ->
	    let t1node = from_term t1 in
	    (* logic *) add_logedge pnode [t1node]
	| NPvalid_range (t1,t2,t3) ->
  	    let t1node = from_term t1 in
	    let t2node = from_term t2 in
	    let t3node = from_term t3 in
	    (* logic *) add_logedge pnode [t1node; t2node; t3node]
    end;
    pnode

  let from_spec ?(is_function=false) (s : nspec) = 
    let requires_node = create_node (Nintern InternLogical) in 
    let reqnode_opt = match s.requires with
      | Some p ->
	  let reqnode = from_pred ~is_funpre:is_function p in
	  (* logic *) add_logedge requires_node [reqnode];
	  Some reqnode
      | None ->
	  if is_function then
	    (* create a node [requires true] that will serve as a hook to 
	       improve on the assumed precondition *)
	    let ptrue = { npred_node = NPtrue; npred_loc = Loc.dummy_position }
	    in
	    let reqnode = from_pred ~is_funpre:is_function ptrue in
	    (* logic *) add_logedge requires_node [reqnode];
	    Some reqnode
	  else
	    None
    in
    let assigns_node = create_node (Nintern InternLogical) in
    begin match s.assigns with
      | Some (_, tl) ->
	  (* differenciate [None] from [Some([])] *)
	  let assnode = create_node (Nintern InternLogical) in
	  (* logic *) add_logedge assigns_node [assnode];
	  let tnodes = List.map from_term tl in
	  add_logedge assnode tnodes
      | None -> ()
    end;
    let ensures_node = create_node (Nintern InternLogical) in
    begin match s.ensures with
      | Some p ->
	  let ensnode = from_pred p in
	  (* logic *) add_logedge ensures_node [ensnode]
      | None -> ()
    end;
    let decreases_node = create_node (Nintern InternLogical) in
    begin match s.decreases with
      | Some (t,_) ->
	  let decnode = from_term t in
	  (* logic *) add_logedge decreases_node [decnode]
      | None -> ()
    end;
    let snode = create_node (Nspec s) in
    (* logic *) add_logedge snode [requires_node; assigns_node;
				   ensures_node; decreases_node];
    snode,reqnode_opt

  let from_annot (a : nloop_annot) writes =
    let assigns_node = create_node (Nintern InternLogical) in
    begin match a.loop_assigns with
      | Some (_,tl) ->
	  (* differenciate [None] from [Some([])] *)
	  let assnode = create_node (Nintern InternLogical) in
	  (* logic *) add_logedge assigns_node [assnode];
	  let tnodes = List.map from_term tl in
	  add_logedge assnode tnodes
      | None -> ()
    end;
    let invariant_node = create_node (Nintern InternLogical) in
    let invnode_opt = match a.invariant with
      | Some p ->
	  let invnode = from_pred 
	      ~is_invariant:true ~writes ~is_assert:true p 
	  in
	  (* logic *) add_logedge invariant_node [invnode];
	  Some invnode
      | None ->
	  (* create a node [assert true] that will serve as a hook to improve
	     on the asserted invariant *)
	  let ptrue = { npred_node = NPtrue; npred_loc = Loc.dummy_position }
	  in
	  let invnode = from_pred 
	      ~is_invariant:true ~writes ~is_assert:true ptrue
	  in
	  (* logic *) add_logedge invariant_node [invnode];
	  Some invnode
    in
    let assume_invariant_node = create_node (Nintern InternLogical) in
    let assinvnode_opt = match a.assume_invariant with
      | Some p ->
	  let assinvnode = from_pred 
	      ~is_invariant:true ~writes ~is_assume:true p in
	  (* logic *) add_logedge assume_invariant_node [assinvnode];
	  Some assinvnode
      | None ->
	  (* create a node [assume true] that will serve as a hook to improve
	     on the assumed invariant *)
	  let ptrue = { npred_node = NPtrue; npred_loc = Loc.dummy_position }
	  in
	  let assinvnode = from_pred 
	      ~is_invariant:true ~writes ~is_assume:true ptrue
	  in
	  (* logic *) add_logedge assume_invariant_node [assinvnode];
	  Some assinvnode	  
    in
    let variant_node = create_node (Nintern InternLogical) in
    begin match a.variant with
      | Some (t,_) ->
	  let varnode = from_term t in
	  (* logic *) add_logedge variant_node [varnode]
      | None -> ()
    end;
    let anode = create_node (Nannot a) in 
    (* logic *) add_logedge anode
      [invariant_node; assume_invariant_node; assigns_node; variant_node];
    anode,invnode_opt,assinvnode_opt

  (* shared code between the creation of a simple expression node and 
     the creation of a test node. The parameter [is_test] tells if the node
     created should be an expression or a test. The parameter [neg_test] is 
     used to specify the opposite of the test should be created. 
     The parameter [is_lvalue] specifies a lvalue is created.
     [in_incr] is set for an lvalue inside an increment/decrement operation.
     [in_opassign] is set for an lvalue inside an op-assignment.
  *)
  let rec from_expr start_node ?(is_test=false) ?(neg_test=false) 
      ?(is_lvalue=false) ?(in_incr=false) ?(in_opassign=false) (e : nexpr) =
    let e = match e.nexpr_node with
      | NEassign_op (e1,op,e2) ->
	  (* create an expression [e12] for the right-hand side of 
	     the assignment, in order to get rid of the opassign node *)
	  let e12 = { e with nexpr_node = NEbinary (e1,op,e2) } in
	  { e with nexpr_node = NEassign (e1,e12) }
      | _ -> e
    in
    let make_test_to_zero e = 
      let sub_e = sub_skip_casts e in
      match sub_e.nexpr_node with
	| NEvar (Var_info v) -> 
	    if var_is_pointer v then
	      let null_var = Info.default_var_info "null" in 
	      (* Info.set_assigned null_var; *)
	      Cenv.set_var_type (Var_info null_var) v.var_type false;
	      let null_expr =
		{ sub_e with nexpr_node = NEvar (Var_info null_var) } in
	      { e with nexpr_node = NEbinary (e,Bneq_pointer,null_expr) }
	    else e
	| _ -> e
    in
    let rec pure e = match e.nexpr_node with
      | NEnop | NEconstant _ | NEstring_literal _ | NEvar _ 
	  -> true
      | NEarrow (e1,_,_) | NEunary (_,e1) | NEcast (_,e1) 
	  -> pure e1
      | NEbinary (e1,_,e2) | NEseq (e1,e2) 
	  -> pure e1 && pure e2
      | NEcond (e1,e2,e3)
	  -> pure e1 && pure e2 && pure e3
      | NEmalloc _ | NEincr _ | NEassign _ | NEassign_op _ | NEcall _
	  -> false
    in
    let negative_lazy = match e.nexpr_node with
      | NEbinary (_,(Band | Bor),_) when neg_test -> true
      | _ -> false
    in
    let negative_not = match e.nexpr_node with
      | NEunary (Unot,_) when neg_test -> true
      | _ -> false
    in
    let push_not_inside = match e.nexpr_node with
      | NEunary (Unot,e1) when is_test && not neg_test ->
	  begin match e1.nexpr_node with
	    | NEunary (Unot,_) -> true
	    | NEbinary (_,(Band | Bor),_) -> true
	    | _ -> false
	  end
      | _ -> false
    in
    let e = 
      if negative_lazy then
	match e.nexpr_node with
	  | NEbinary (e1,(Band | Bor as op),e2) ->
	      let not_e1 = { e1 with nexpr_node = NEunary (Unot, e1) } in
	      let not_e2 = { e2 with nexpr_node = NEunary (Unot, e2) } in
	      begin match op with
		| Bor -> 
		    { e with nexpr_node = NEbinary (not_e1,Band,not_e2) }
		| Band ->
		    { e with nexpr_node = NEbinary (not_e1,Bor,not_e2) }
		| _ -> assert false
	      end
	  | _ -> assert false
      else if negative_not then
	match e.nexpr_node with
	  | NEunary (Unot,e1) -> make_test_to_zero e1
	  | _ -> assert false
      else if push_not_inside then
	e (* code for [Unot] below takes care of the negation *)
      else if neg_test then
        { e with nexpr_node = NEunary (Unot, make_test_to_zero e) }
      else if is_test then
	make_test_to_zero e
      else e
    in
    (* effect of [neg_test] taken into account at this point,
       ignore it, except for [Unot], when not in [negative_lazy] mode *)
    let enode =
      if is_test then Ntest e
      else if is_lvalue then
	if in_incr then Nlvalue (e,IncrDecr)
	else if in_opassign then Nlvalue (e,OpAssign)
	else Nlvalue (e,Assign)
      else Nexpr e
    in
    let enode = create_node enode in 
    begin
      match e.nexpr_node with
	| NEnop | NEconstant _ | NEstring_literal _ | NEvar _ ->
	    (* oper *) add_opedge start_node enode
	| NEunary (Unot,e1) when push_not_inside ->
	    assert (is_test && not neg_test);
	    let e1node = 
	      from_expr ~is_test ~neg_test:(not neg_test) start_node e1 in
	    (* oper *) add_opedge e1node enode;
	    (* struct *) add_stedge enode [e1node]
	| NEarrow (e1,_,_) | NEunary (_,e1) 
	| NEcast (_,e1) | NEmalloc (_,e1) ->
	    let e1node = from_expr start_node e1 in
	    (* oper *) add_opedge e1node enode;
	    (* struct *) add_stedge enode [e1node]
	| NEincr (_,e1) ->
	    let e1node = 
	      from_expr ~is_lvalue:true ~in_incr:true start_node e1 in
	    (* oper *) add_opedge e1node enode;
	    (* struct *) add_stedge enode [e1node]
	| NEseq (e1,e2) ->
	    let e1node = from_expr start_node e1 in
	    let e2node = from_expr e1node e2 in
	    (* oper *) add_opedge e2node enode;
	    (* struct *) add_stedge enode [e1node; e2node]
	| NEassign (e1,e2) ->
	    let e1node,e2node =
	      if Coptions.evaluation_order.Coptions.assign_left_to_right then
		let e1node = from_expr ~is_lvalue:true start_node e1 in
		let e2node = from_expr e1node e2 in
		(* oper *) add_opedge e2node enode;
	        e1node,e2node
    	      else
		let e2node = from_expr start_node e2 in
		let e1node = from_expr ~is_lvalue:true e2node e1 in
		(* oper *) add_opedge e1node enode;
	        e1node,e2node
	    in
	    (* struct *) add_stedge enode [e1node; e2node]
	| NEassign_op (e1,op,e2) ->
	    assert false (* expression should have been modified above *)
	| NEbinary (e1,Band,e2) when is_test ->
	    (* do not take [neg_test] into account, already done *)
	    (* AND succeeds only when both [e1] and [e2] succeed *)
	    let e1node = from_expr ~is_test start_node e1 in
	    let e2node = from_expr ~is_test e1node e2 in
	    (* oper *) add_opedge e2node enode;
	    (* if the and-test is free from side-effects, the order of
	       evaluation of [e1] and [e2] should not matter. We ensure this
	       by merging a path where [e1] is evaluated before [e2] with
	       a path where [e2] is evaluated before [e1]. *)
	    if pure e then
	      begin
		let e2node = from_expr ~is_test start_node e2 in
		let e1node = from_expr ~is_test e2node e1 in
		(* oper *) add_opedge e1node enode
	      end;
	    (* struct *) add_stedge enode [e1node; e2node]
	| NEbinary (e1,Bor,e2) when is_test ->
	    (* do not take [neg_test] into account, already done *)
	    (* OR succeeds whenever [e1] succeeds, 
	       or [e1] fails and [e2] succeeds *)
	    let e1node = from_expr ~is_test start_node e1 in
	    let e2node =
	      if pure e then
		from_expr ~is_test start_node e2
	      else
		let nege1node = 
		  from_expr ~is_test ~neg_test:true start_node e1 in
		from_expr ~is_test nege1node e2 
	    in
	    (* oper *) add_opedge e1node enode;
	    (* oper *) add_opedge e2node enode;
	    (* struct *) add_stedge enode [e1node; e2node]
	| NEbinary (e1,_,e2) ->
	    let e1node,e2node =
	      if Coptions.evaluation_order.Coptions.binary_left_to_right then
		let e1node = from_expr start_node e1 in
		let e2node = from_expr e1node e2 in
		(* oper *) add_opedge e2node enode;
	        e1node,e2node
    	      else
		let e2node = from_expr start_node e2 in
		let e1node = from_expr e2node e1 in
		(* oper *) add_opedge e1node enode;
	        e1node,e2node
	    in
	    (* struct *) add_stedge enode [e1node; e2node]
	| NEcall c ->
	    let expr_list = c.ncall_fun :: c.ncall_args in
	    let expr_list,list_reversal =
	      if Coptions.evaluation_order.Coptions.call_left_to_right then
		expr_list,false
	      else 
		List.rev expr_list,true
	    in
	    let anode,anodes = 
	      List.fold_left 
		(fun (startn,nlist) e -> 
		  let en = from_expr startn e in
		  en,en::nlist
		) (start_node,[]) expr_list
	    in
	    (* get back the function and the arguments in the correct order.
	       Take into account the fact that the call to [List.fold_left] 
	       already reverses the list. *)
	    let anodes = if list_reversal then anodes else List.rev anodes in
	    (* oper *) add_opedge anode enode;
	    (* struct *) add_stedge enode anodes
	| NEcond (e1,e2,e3) ->
	    let true_node = from_expr ~is_test:true start_node e1 in
	    let false_node =
	      from_expr ~is_test:true ~neg_test:true start_node e1 in
	    let e2node = from_expr true_node e2 in
	    let e3node = from_expr false_node e3 in
	    (* oper *) add_opedge e2node enode;
	    (* oper *) add_opedge e3node enode;
	    (* struct *) add_stedge enode [true_node; e2node; e3node]
    end;
    enode

  type context_descr = 
      { 
	(* target for [continue] statements in loops *)
	loop_starts : Node.t list; 
	(* target for [break] statements in loops and switches *)
	loop_switch_ends : Node.t list;
	(* context of \old logical terms *)
	function_begin : Node.t;
	(* target of return and last statement of function.
	   Set only if needed. *)
	function_end : Node.t option;
	(* targets for [goto] statements, and sources for [label] ones *)
	label_aliases : Node.t StringMap.t ref
      }

  (* Whenever a [goto] or [label] is found, this function is called 
     and returns a node that represents the label, and updates 
     globally all the contexts *)
  let update_context_for_label ctxt lab =
    try
      StringMap.find lab (!(ctxt.label_aliases))
    with Not_found ->
      let lab_node = create_node (Nintern InternOperational) in
      ctxt.label_aliases := StringMap.add lab lab_node (!(ctxt.label_aliases));
      lab_node

  let rec from_stat (ctxt : context_descr) start_node (s : nstatement) =
    (* structural statement node, for statement [s] *)
    let stsnode = create_node (Nstat s) in
    (* operational statement node for [s]. It is equal to [stsnode] in the cases
       where the control flow ends up in the node representing the statement. *)
    let opsnode = match s.nst_node with
      | NSdecl _ -> 
	  (* A declaration should be put in the operational graph before 
	     the statements that follow it. Since these statements are
	     a sub-field of the declaration, we create an internal node 
	     to serve in place of the declaration as end-node. *)
	  create_node (Nintern InternOperational)
      | _ -> stsnode
    in 
    begin
      match s.nst_node with
	| NSnop | NSlogic_label _ ->
	    (* oper *) add_opedge start_node opsnode
	| NSassert p ->
	    let pnode = from_pred ~is_assert:true p in
	    (* oper *) add_opedge start_node pnode;
	    (* oper *) add_opedge pnode opsnode;
	    (* logic *) add_logedge stsnode [pnode];
	    (* assert node is -only- its self-end *)
	    (* logic *) add_begedge stsnode ctxt.function_begin;
	    (* logic *) add_endedge stsnode opsnode
	| NSassume p ->
	    let pnode = from_pred ~is_assume:true p in
	    (* oper *) add_opedge start_node pnode;
	    (* oper *) add_opedge pnode opsnode;
	    (* logic *) add_logedge stsnode [pnode];
	    (* assume node is -only- its self-end *)
	    (* logic *) add_begedge stsnode ctxt.function_begin;
	    (* logic *) add_endedge stsnode opsnode
	| NSexpr e -> 
	    let enode = from_expr start_node e in
	    (* oper *) add_opedge enode opsnode;
	    (* struct *) add_stedge stsnode [enode]
	| NSif (e,s1,s2) ->
	    let then_node = from_expr ~is_test:true start_node e in
	    let else_node =
	      from_expr ~is_test:true ~neg_test:true start_node e in
	    let sts1node,ops1node = from_stat ctxt then_node s1 in
	    let sts2node,ops2node = from_stat ctxt else_node s2 in
	    (* oper *) add_opedge ops1node opsnode;
	    (* oper *) add_opedge ops2node opsnode;
	    (* struct *) add_stedge stsnode [then_node; sts1node; sts2node]
	| NSwhile (a,e,s1) ->
	    (* source of backward edge in loop *)
	    let bwd_node = create_node (Nintern InternOperationalBwdSrc) in
	    (* node that merges backward and upward flows *)
	    let mrg_node = create_node (Nintern InternOperationalBwdDest) in
	    (* connect backward edge *)
	    (* oper *) add_opedge start_node mrg_node;
	    (* oper *) add_backedge bwd_node mrg_node;
	    let loop_effect = 
	      Ceffect.ef_union (Ceffect.statement ~with_local:true s1)
		(Ceffect.expr ~with_local:true e) in
	    let write_vars =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_var) in
	    let write_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_under_pointer)
	    in
	    let read_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.reads_under_pointer)
	    in
	    let writes = { write_vars = write_vars; 
			   read_under_pointers = read_under_pointers;
			   write_under_pointers = write_under_pointers; } in
	    let anode,invnode_opt,assinvnode_opt = from_annot a writes in
	    let loop_node = match invnode_opt,assinvnode_opt with
	      | None,None -> mrg_node
	      | Some inode,None | None,Some inode ->
		  (* oper *) add_opedge mrg_node inode;
		  inode
	      | Some invnode,Some assinvnode ->
		  (* assume part of invariant has no successor *)
		  (* oper *) add_opedge mrg_node assinvnode;
		  (* oper *) add_opedge mrg_node invnode;
		  (* logic *) add_invedge invnode assinvnode;
		  (* logic *) add_invedge assinvnode invnode;
		  invnode
	    in
	    let test_node = from_expr ~is_test:true loop_node e in
	    let stop_node = 
	      from_expr ~is_test:true ~neg_test:true loop_node e in
	    let upd_ctxt =
	      { ctxt with 
		  loop_starts = bwd_node :: ctxt.loop_starts;
		  loop_switch_ends = opsnode :: ctxt.loop_switch_ends } in
            (* widening node is first node -in- the loop *)
            let widen_node = create_node (Nwiden { count = 0 }) in 
            (* oper *) add_opedge test_node widen_node;
	    let sts1node,ops1node = from_stat upd_ctxt widen_node s1 in
	    (* oper *) add_opedge ops1node bwd_node; (* before [e]'s eval *)
	    (* oper *) add_opedge stop_node opsnode; (* after [e]'s eval *)
	    (* struct *) add_stedge stsnode [test_node; sts1node];
	    (* logic *) add_logedge stsnode [anode];
	    (* the logical "annot" node is linked to the start and end nodes *)
	    (* [bwd_node] is the end node of the loop *)
	    (* logic *) add_begedge anode ctxt.function_begin;
	    (* logic *) add_endedge anode bwd_node
	| NSdowhile (a,s1,e) ->
	    (* source of backward edge in loop *)
	    let bwd_node = create_node (Nintern InternOperationalBwdSrc) in
	    (* node that merges backward and upward flows *)
	    let mrg_node = create_node (Nintern InternOperationalBwdDest) in
	    (* connect backward edge *)
	    (* oper *) add_opedge start_node mrg_node;
	    (* oper *) add_backedge bwd_node mrg_node;
	    let fwd_node = create_node (Nintern InternOperational) in
	    let upd_ctxt =
	      { ctxt with 
		  loop_starts = fwd_node :: ctxt.loop_starts;
		  loop_switch_ends = opsnode :: ctxt.loop_switch_ends } in
            (* widening node is first node -in- the loop *)
            let widen_node = create_node (Nwiden { count = 0 }) in 
            (* oper *) add_opedge mrg_node widen_node;
	    let sts1node,ops1node = from_stat upd_ctxt widen_node s1 in
	    let loop_effect = 
	      Ceffect.ef_union (Ceffect.statement ~with_local:true s1)
		(Ceffect.expr ~with_local:true e) in
	    let write_vars =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_var) in
	    let write_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_under_pointer)
	    in
	    let read_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.reads_under_pointer)
	    in
	    let writes = { write_vars = write_vars; 
			   read_under_pointers = read_under_pointers;
			   write_under_pointers = write_under_pointers; } in
	    let anode,invnode_opt,assinvnode_opt = from_annot a writes in
	    let loop_node = match invnode_opt,assinvnode_opt with
	      | None,None -> fwd_node
	      | Some inode,None | None,Some inode ->
		  (* oper *) add_opedge fwd_node inode;
		  inode
	      | Some invnode,Some assinvnode ->
		  (* assume part of invariant has no successor *)
		  (* oper *) add_opedge fwd_node assinvnode;
		  (* oper *) add_opedge fwd_node invnode;
		  (* logic *) add_invedge invnode assinvnode;
		  (* logic *) add_invedge assinvnode invnode;
		  invnode
	    in
	    let test_node = from_expr ~is_test:true loop_node e in
	    let stop_node = 
	      from_expr ~is_test:true ~neg_test:true loop_node e in
	    (* oper *) add_opedge ops1node fwd_node;
	    (* oper *) add_opedge test_node bwd_node;(* before [s1]'s eval *)
	    (* oper *) add_opedge stop_node opsnode;
	    (* struct *) add_stedge stsnode [sts1node; test_node];
	    (* logic *) add_logedge stsnode [anode];
	    (* the logical "annot" node is linked to the start and end nodes *)
	    (* [bwd_node] is the end node of the loop *)
	    (* logic *) add_begedge anode ctxt.function_begin;
	    (* logic *) add_endedge anode bwd_node
	| NSfor (a,einit,etest,eincr,s1) ->
	    (* source of backward edge in loop *)
	    let bwd_node = create_node (Nintern InternOperationalBwdSrc) in
	    (* node that merges backward and upward flows *)
	    let mrg_node = create_node (Nintern InternOperationalBwdDest) in
	    (* connect backward edge *)
	    let einit_node = from_expr start_node einit in
	    (* oper *) add_opedge einit_node mrg_node;
	    (* oper *) add_backedge bwd_node mrg_node;
	    let loop_effect = 
	      Ceffect.ef_union (Ceffect.expr ~with_local:true etest)
		(Ceffect.ef_union (Ceffect.statement ~with_local:true s1)
		   (Ceffect.expr ~with_local:true eincr))
	    in
	    let write_vars =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_var) in
	    let write_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.assigns_under_pointer)
	    in
	    let read_under_pointers =
	      HeapVarSet.elements (loop_effect.Ceffect.reads_under_pointer)
	    in
	    let writes = { write_vars = write_vars; 
			   read_under_pointers = read_under_pointers;
			   write_under_pointers = write_under_pointers; } in
	    let anode,invnode_opt,assinvnode_opt = from_annot a writes in
	    let loop_node = match invnode_opt,assinvnode_opt with
	      | None,None -> mrg_node
	      | Some inode,None | None,Some inode ->
		  (* oper *) add_opedge mrg_node inode;
		  inode
	      | Some invnode,Some assinvnode ->
		  (* assume part of invariant has no successor *)
		  (* oper *) add_opedge mrg_node assinvnode;
		  (* oper *) add_opedge mrg_node invnode;
		  (* logic *) add_invedge invnode assinvnode;
		  (* logic *) add_invedge assinvnode invnode;
		  invnode
	    in
	    let test_node = from_expr ~is_test:true loop_node etest in
	    let stop_node = 
	      from_expr ~is_test:true ~neg_test:true loop_node etest in
	    let fwd_node = create_node (Nintern InternOperational) in
	    let upd_ctxt =
	      { ctxt with 
		  loop_starts = fwd_node :: ctxt.loop_starts;
		  loop_switch_ends = opsnode :: ctxt.loop_switch_ends } in
            (* widening node is first node -in- the loop *)
            let widen_node = create_node (Nwiden { count = 0 }) in 
            (* oper *) add_opedge test_node widen_node;
	    let sts1node,ops1node = from_stat upd_ctxt widen_node s1 in
	    let eincr_node = from_expr fwd_node eincr in
	    (* oper *) add_opedge ops1node fwd_node;
	    (* oper *) add_opedge eincr_node bwd_node; (* before [etest] *)
	    (* oper *) add_opedge stop_node opsnode; (* after [etest]'s eval *)
	    (* struct *) add_stedge stsnode [einit_node; test_node;
					     eincr_node; sts1node];
	    (* logic *) add_logedge stsnode [anode];
	    (* the logical "annot" node is linked to the start and end nodes *)
	    (* [bwd_node] is the end node of the loop *)
	    (* logic *) add_begedge anode ctxt.function_begin;
	    (* logic *) add_endedge anode bwd_node
	| NSblock sl ->
	    let (opbnode,stsnodes) = 
	      List.fold_left 
		(fun (startopnode,sts1nodes) s1 -> 
		   let sts1node,ops1node = from_stat ctxt startopnode s1 in
		   ops1node,sts1node::sts1nodes
		) (start_node,[]) sl in
	    let stsnodes = List.rev stsnodes in
	    (* oper *) add_opedge opbnode opsnode;
	    (* struct *) add_stedge stsnode stsnodes
	| NSreturn None ->
	    (* oper *) add_opedge start_node opsnode;
	    begin match ctxt.function_end with
	      | Some function_end ->
		  (* logic *) force_add_opedge stsnode function_end
	      | None -> ()
	    end
	| NSreturn (Some e) -> 
	    let enode = from_expr start_node e in
	    (* oper *) add_opedge enode opsnode;
	    (* struct *) add_stedge stsnode [enode];
	    begin match ctxt.function_end with
	      | Some function_end ->
		  (* logic *) force_add_opedge stsnode function_end
	      | None -> ()
	    end
	| NSbreak -> 
	    (* oper *) add_opedge start_node opsnode;
	    (* oper *) force_add_opedge opsnode (List.hd ctxt.loop_switch_ends);
	| NScontinue -> 
	    (* oper *) add_opedge start_node opsnode;
	    (* Originally this edge was a backward one, crucial to make 
	       the topological walk work properly. Not the case anymore since
	       loops have 2 special nodes for the back edge. *)
	    (* oper *) add_opedge opsnode (List.hd ctxt.loop_starts)
	| NSgoto (_,lab) ->
            (* no problem of widening here since only forward gotos are 
	       accepted. Otherwise we should add a widening node in the induced
	       loop. *)
	    (* oper *) add_opedge start_node opsnode;
	    let label_node = update_context_for_label ctxt lab.label_info_name in
	    (* oper *) force_add_opedge opsnode label_node
	| NSspec (spc,s1) ->
	    let sts1node,ops1node = from_stat ctxt start_node s1 in
	    (* oper *) add_opedge ops1node opsnode;
	    (* struct *) add_stedge stsnode [sts1node];
	    (* ignore precondition for the moment *)
	    let spcnode,_ = from_spec spc in
	    (* logic *) add_logedge stsnode [spcnode];
	    (* the logical "spec" node is linked to the start and end nodes *)
	    (* logic *) add_begedge spcnode start_node;
	    (* logic *) add_endedge spcnode stsnode
	| NSlabel (lab,s1) ->
	    let label_node = update_context_for_label ctxt lab.label_info_name in
	    (* oper *) add_opedge start_node label_node;
	    let sts1node,ops1node = from_stat ctxt label_node s1 in
	    (* oper *) add_opedge ops1node opsnode;
	    (* struct *) add_stedge stsnode [sts1node]
	| NSdecl (_,_,None,s1) ->
	    (* here structural statement node and operational statement node
	       are not the same. [stsnode] is used in the operational graph
	       before nodes for [s1]. *)
	    (* oper *) add_opedge start_node stsnode;
	    let sts1node,ops1node = from_stat ctxt stsnode s1 in
	    (* oper *) add_opedge ops1node opsnode;
	    (* struct *) add_stedge stsnode [sts1node]
	| NSdecl (_,var,Some cinit,s1) ->
	    (* create an assignment expression node so that the treatment
	       of this initialization is shared with normal assignment *)
	    let rec first_expr cinit = match cinit with
	      | Iexpr e -> Some e
	      | Ilist clinit ->
		  List.fold_left (fun e_opt cinit ->
				    begin match e_opt with
				      | None -> first_expr cinit
				      | _ -> e_opt
				    end) None clinit
	    in
	    let base_expr = match cinit with
	      | Iexpr e -> e
	      | Ilist il -> 
		  begin match first_expr (Ilist il) with
		    | None -> 
			(* initialization list cannot be totally empty *)
			assert false 
		    | Some e -> e
		  end 
	    in
	    (* to be matched with [decode_decl_list] above *)
	    let rec encode_decl_list cinit = match cinit with
	      | Iexpr e -> e
	      | Ilist clinit ->
		  (* special encoding of [Ilist il] in the form of a fake call
		     to [NEnop] so that:
		     - assignments in the initialization expression are taken 
		     into account
		     - decoding is easy and safe *)
		  let encoded_clinit = List.map encode_decl_list clinit in
		  let fake_caller = { base_expr with nexpr_node = NEnop } in
		  let fake_call = 
		    { ncall_fun = fake_caller;
		      ncall_args = encoded_clinit;
		      ncall_zones_assoc = [] } in
		  { base_expr with nexpr_node = NEcall fake_call }
	    in
	    let var_expr = 
	      { base_expr with nexpr_node = NEvar (Var_info var) } in
	    let encoded_expr = match cinit with
	      | Iexpr e -> e
	      | Ilist il -> encode_decl_list cinit in
	    let explicit_assign = NEassign (var_expr,encoded_expr) in
	    let explicit_assign = 
	      { base_expr with nexpr_node = explicit_assign } in
	    (* here structural statement node and operational statement node
	       are not the same. [stsnode] is used in the operational graph
	       before nodes for [s1]. *)
	    (* oper *) add_opedge start_node stsnode;
	    let enode = from_expr stsnode explicit_assign in
	    let sts1node,ops1node = from_stat ctxt enode s1 in
	    (* oper *) add_opedge ops1node opsnode;
	    (* struct *) add_stedge stsnode [enode;sts1node]
	| NSswitch (e,_,cases) -> 
	    let enode = from_expr start_node e in
	    let upd_ctxt =
	      { ctxt with 
		  loop_starts = ctxt.loop_starts;
		  loop_switch_ends = opsnode :: ctxt.loop_switch_ends } in
	    let cnodes = List.map
	      (fun (_,sl) -> 
		 let opcnode,stclnodes =
		   List.fold_left 
		     (fun (startopnode,s1nodes) s1 -> 
			let sts1node,ops1node = 
			  from_stat upd_ctxt startopnode s1 in
			ops1node,sts1node::s1nodes
		     ) (enode,[]) sl
		 in
		 (* [cnode] is the node representing this [case], which is 
		    the same as the last statement in the list of statements
		    for this [case], when this list is not empty.
		    [clnodes] is the list of statements in this [case]. *)
		 opcnode,List.rev stclnodes
	      ) cases
	    in
	    let last_cnode =
	      List.fold_left 
		(fun prev_cnode (cnode,clnodes) -> 
		   begin match clnodes with
		     | [] -> (* no statement in this [case] *)
			 prev_cnode
		     | first_node::_ -> 
			 (* in case of fallthrough, control passes from 
			    the last statement of a [case] to the first 
			    statement of next [case].
			    If [prev_cnode] is a [break], call to 
			    [add_opedge] will do nothing. *)
			 (* oper *) add_opedge prev_cnode first_node;
			 cnode
		   end) enode cnodes
	    in
	    (* control flows from end of last case to end of switch *)
	    (* oper *) add_opedge last_cnode opsnode;
	    (* in absence of stable way of recognizing presence of [default]
	       case in switch (emptiness of integer map is not stable),
	       we consider the control always flows from start to end
	       of switch *)
	    (* oper *) add_opedge start_node opsnode;
	    let first_nodes = 
	      List.map (fun (_,clnodes) ->
			  (* special encoding so that [switch] can be seen as
			     a correct structured tree: a node [Nintern] is
			     created for every [case] and serves as 
			     intermediate *)
			  let fnode = 
			    create_node (Nintern InternStructural) in
			  begin match clnodes with
			    | [] -> ()
			    | _ -> (* struct *) add_stedge fnode clnodes
			  end;
			  fnode) cnodes
	    in
	    (* struct *) add_stedge stsnode (enode::first_nodes)
    end;
    stsnode,opsnode

  let rec from_decl d =
    if debug_more then Coptions.lprintf 
      "[from_decl] treating function %s@." d.name;
    (* clear all heap variables to avoid name clash between 
       different fucntions *)
    Hashtbl.clear Ceffect.heap_vars;
    let dnode = create_node (Ndecl d) in 
    (* In order to be able to transform any [ensures] part of 
       a function, we must create an end node for the function body,
       so that all parts of information are known under the same name 
       at that point. It is also used to provide a single entry point
       for backward propagation.
       All return statements and the last statement should be linked
       to this end node.
       This transformation may lead to a less precise analysis. 
       Maybe it could be restricted to functions that have an [ensures]
       part. It could be restricted further to functions that need it 
       to translate their [ensures] part.
    *)
    let end_node = create_node (Nintern InternOperational) in
    begin match d.s with
      | Some s ->
	  let start_ctxt = 
	    { 
	      loop_starts = []; 
	      loop_switch_ends = [];
	      function_begin = dnode;
	      function_end = Some end_node;
	      label_aliases = ref StringMap.empty
	    } in
	  let spcnode,reqnode_opt = from_spec ~is_function:true d.spec in
	  let func_node = match reqnode_opt with
	    | None -> dnode
	    | Some reqnode ->
		(* oper *) add_opedge dnode reqnode;
		reqnode
	  in
	  let stsnode,opsnode = from_stat start_ctxt func_node s in
	  (* oper *) add_opedge opsnode end_node;
	  (* struct *) add_stedge dnode [stsnode];
	  (* logic *) add_logedge dnode [spcnode];
	  (* the logical "spec" node is linked to the start and end nodes *)
	  (* logic *) add_begedge spcnode dnode; (* begin of decl is itself *)
	  (* logic *) add_endedge spcnode end_node
      | None -> ()
    end;
    (* clear all heap variables to avoid polluting real effects *)
    Hashtbl.clear Ceffect.heap_vars;
    dnode,end_node

  let from_file file =
    internal_graph := Some (Self.create ());
    (* only return functions that should be verified *)
    let file = 
      List.filter (fun func -> Coptions.verify func.f.fun_name) file in
    List.map from_decl file

  let to_decl node =
    match get_node_kind node with
      | NKdecl -> get_decl node
      | _ -> failwith "[to_decl] should be called only on declaration"

  let to_file nodes =
    let decls = List.map to_decl nodes in
    internal_graph := None;
    decls

  let collect_vars () =
    fold_operational Forward ~roots:[]
      (fun node (used_vars,decl_vars) ->
	 match get_node_kind node with
	   | NKexpr | NKtest | NKlvalue ->
	       (* only on code, not on logical part *)
	       let used_vars =
		 if expr_is_local_var node then
		   let var = termexpr_var_get node in
		   ILVarSet.add var used_vars
		 else
		   used_vars
	       in
	       used_vars,decl_vars
	   | NKstat ->
	       let decl_vars = 
		 if stat_is_decl node then
		   let var = decl_stat_get_var node in
		   ILVarSet.add var decl_vars
		 else
		   decl_vars
	       in
	       used_vars,decl_vars
	   | _ -> used_vars,decl_vars
      ) (ILVarSet.empty,ILVarSet.empty)

end

(* Local Variables: *)
(* compile-command: "make -C .." *)
(* End: *)