diff --git a/COMPILING b/COMPILING index a7535aef3..308d1b9a3 100644 --- a/COMPILING +++ b/COMPILING @@ -1,6 +1,6 @@ -2LS 0.4 +2LS 0.5 ./install.sh -The executable is src/summarizer/2ls +The executable is src/2ls/2ls diff --git a/README.md b/README.md new file mode 100644 index 000000000..aa46ece57 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +[![Build Status][build_img]][travis] + +About +===== + +2LS is a verification tool for C programs. It is built upon the +CPROVER framework ([cprover.org](http://www.cprover.org)), which +supports C89, C99, most of C11 and most compiler extensions provided +by gcc and Visual Studio. It allows verifying array bounds (buffer +overflows), pointer safety, exceptions, user-specified assertions, and +termination properties. The analysis is performed by template-based +predicate synthesis and abstraction refinements techniques. + +For more information see [cprover.org](http://www.cprover.org/2LS). + +License +======= +4-clause BSD license, see `LICENSE` file. + +[build_img]: https://travis-ci.org/diffblue/2ls.svg?branch=master +[travis]: https://travis-ci.org/diffblue/2ls diff --git a/doc/deltacheck_logo.ai b/doc/deltacheck_logo.ai deleted file mode 100644 index 58140b302..000000000 --- a/doc/deltacheck_logo.ai +++ /dev/null @@ -1,4776 +0,0 @@ -%PDF-1.5 % -1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream - - - - - application/pdf - - - deltacheck - - - - - 2012-07-11T12:42:11+01:00 - 2012-07-11T12:42:11+01:00 - 2012-07-11T12:42:11+01:00 - Adobe Illustrator CS5 - - - - 256 - 60 - JPEG - /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAPAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq85s/wA8PLFz 5/k8nrE4kWVoI9Q5KYXmUboANxvUVPhmadDMY+P7GoZhxcLLpdVvry8mstHRP9GPC61CcM0KSdfT RFKGVx+18ShfGvw5hNqX+YtVvvL+ntf3+tW8SVokT2jO0j9o4UWdGJPuTTqTSuKonyf5kuNc01bm 4txby/tIpLD23IHb/M9cVT/FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq8E/N7/nJAaPdz6D5O9Oe/hJju9WcCSKJxsUhQ/C7KerN8I6UPaqWTucfJmrYPEdN1 rzD5o1lr/wAxarc3lpZA3N01xI0iAKCwRY2PCmxPAUqoIGSwx4pXL6RuWmMjI7lC394UvbPzDpgN t67+sgBLeldwsDInJqk7lZP9VxnTaLOM2LfnyLTPY2H1Zp/5reVNJ/Kex81zssNsIViW0ViWa7Ao 0QYjkWL1LNQ+O+aLUY/DmQXYxmDG0q8qaBqXnW7i80a7MtxBMOVmiE+kkTCqrEB+z+J775SzerWt pb2sQigQIi9AMVQ+t6xbaPp0l/cxXE0MVOaWkEt1LQmlRFCruQO5ptirz+2/5yM/LG8mFvYXF7fX Z+za21jcySmnWi8O2Ko25/PDydY0bVrXV9JgNCbi90y7ijAPSp4H9WKss8veavLfmO0N5oWpQajb g0doHDFSezr9pT7MMVTXFXYql+vax+h9MkvvqV3qJjoFtLCIzTuT/KlR95OKsM/KT82G/MJ9ckXT Tpttpc0UMEcj85mEgYsZBQBTVOg6eOKvQ8VdirsVdiqA1zX9F0LT5NR1m9hsLKP7U87hFqeiiv2m PYDc4qxKP84tEukabStE1/VbMAlbyz0u4aFwP5DIIy30DFUd5c/NfyNr+oHS7W/NrrCtxOl30clp dcqVoI5gnI07LXFW/wAw/wAxrDyVorarc6fe6jEE51s4S8SgsFUyzmkcYZmHevtiqN/L/wA1N5s8 naZ5ia2Fm2oRtIbYP6gTjIyU58Ur9nwxVkGKuxV2KuxV2KsD/NL81ovIOni6l0S+1FWChbiFVS0V 2JCrLOS3Amn8hxVkfkvXpfMPlPSNcliWCXUrWK5eFCWVDIobiCetK4qnOKvNvz988XXlbyLKmnOU 1bV3+pWbJ9tFZS00q03qqCgI6Eg5CZoNeWRA25vi91ZWKuCGHUHY5Q4BFMp1Vf0R5ettEhB/SOok S6hTrQN8MX0OOJU7qyGmzZlZBwQEestz+gNh2FdXseqfkZ9Q/JFHEZPmO2P6VuRQlqMgDwAca/DH Tb+YfLM3s3L4c6PIt2TD6K6vDWjk1PQpNPLktpzvf2sddirKq3KgePBFkHgFbxzM7WwcUeMdHGxy 2Ie5/wDOKXnmZzfeTLuQtHEhvdL5H7I5ATxD5lw4H+tmhxno5Onn0fRmWuS7FXz3+Ttnax/85C/m AY4lX0vrPp0AHHndoWp4VOKvoOSNJEaORQ6OCrowqCDsQQcVfO/5y+Rbv8u9Rg/MryCTpqxSoms6 dD8NuVkYBW9IUX03aiunSpBFOuKXtvkjzXZebPKuneYLMcIb+Lm0RIJjkUlJIyR/I6kYoTzFXYq8 J/5xi/46Hn3/ALai/wDEpsVe1S6zpcWrwaO9wo1O5hkuYLXcs0UTKrvsKAAuBv17YqjMVdiqje3l tY2c97dOIra1jeaeU9FjjUszH5AYq8W/LG0k/NLzBefmH5lQy6TY3LWvlbRZfighEYBadkPwvIeQ 3P7VfBaKvb8VYT+af5W6H590GW1uYki1eFGOmalx/eRSdQrEbtGx2Zfp64qxDUrbUrX/AJxiurHV IHtr+x0uSznglUqym1lMI6gbERgqehFCKjfFWTfkJ/5KHy3/AMYJP+T8mKs/xV2KuxV2KuxV5r/z keB/ypjzD7fU6f8ASdBikJ5+UX/kr/K3/bMtv+TYxQy7FXl3n/zvpvlX8z/Lf6YgSTTtUtpLdbuU BvqsqyjjLHy2SvOkhWhIpWvEZEy3pgZUQEV+a/5U6L5n0o6lZWUSeYbA/WbSeNFBmKfEYpVApIGp tXv3pXJgCxaZQBeGfkL5Tl85fmLJrWoRH9H6MRPJG/xD1E+C3hJPXjxqa9QuVkmcjIuPihciS9L/ AOcnvzMuPLug2vlzSZjFq+rkSyyoaNFbRt1Hu7inXsfHDKVN2SVB873My2epWuq2carb3SrdwQkV QBiVlgIPVFdXj36r886fTZBnxb9di6+XplYZr+SNqth+eGipalhYXa3UtqWNS1u9nM6KzUALIV4v T9pTnMZMJx5DE9G/F9Yp9hZJzXYq8B/KH/1oX8w/+e//AFFJir37FUg/MDS4NV8ja/p8wBS4sLlR Xs4iYo3+xYA4q8a/IPztaeVfyK1bXNWqbTTNRnS2iXZpS8UDJGldqtLIRXoPvxSXoMOh/mdregRa u3mh9E1u5jFxbaZb21tJYweoOSQzetFJPI1CA78xQ1ouKG/yd/Mu585aZf2mrwx2vmXQ5zaatbxH 4CwJVZUFTQMyMCK9R4UxVhP/ADjP6n1z8wPTAMn6THAMSF5cpqVIrtirEtKtvzYl/wCcg9as4tZs D5pgsv3l1NHI9mltIsMwhhjpyUKJVA28TuTXFL0vzN5u8+/lz5Iv9b81aha61q97dRWekxW8Jis7 YujnnKVVZGX4WZq+AUUrihS8/wBl+bPlfy1dea9O85nUZdOjE95ps9jarbSRAjn6fBea8ev2q0/a xVrzr54l8yf843ah5nhiMMuo2KxzQxHkFZrlbacCv7IPL6MVTP8A5xtEX/Km9C4U5crz1afzfXJu v+xpikvTcUOxVhP52f8AkqPM/wDzBP8ArGKvPfyls/zI8xflbo1toerx+U9Ms0khivWtVvbm7YSs XdVkdEiiDMVB3YkHoMVTLyN+YXnnRfzHP5c+f54dQurqIzaPrMKLF6qhWYB1RUWjLGwBoCGWnxVr irMvP3ni50nUtG8saIscvmjzDK0dkJgWit4YwWmuZVUgsEUEqtRyp7Yqx7z+v5leS9Fl816V5jk1 yGwCy6tpGo29qsUkNaO0D28ULxcQfs1PjU0oVU31P84NCtvy60/zjaxPdPrAjh0nTFI9Wa8kJT6v UbAo6sHPseuwxVCav5S/OS/00Xtr50j03W+PqLpsNlAbBHO/pepIsszAdObcv9XfFWLfmJqXmzUf +catYn82WwtddSSKC7QIYwxg1SKISBelHCcqjY9RtikMl8ueYLPQvyI0O+uNR/Rcn6Jt0tLgIs0h naIemkULf3rsdgnf264oRP5bW/5wXttban511G3s1b4ho0FrGJmUrt68pJ4Gu/FBX3HTFWLf85Ve V59S8m2GuW6l20S4YTqB0gugqs/jtJHGPp9sryDZozxsX3Mh/InzXdal5WTQ9WlDa3oyJHKpI5tC aqnIbHlGytE237I8ctMTGr502QNhlmleXvLnk+21vUbZBbW95PLqd+9BRfgq9KD7K8SQO1cDN8O+ evN135y85aj5huSeFxIVtIzv6cCbRoNh0XMeZsuFmnZXaS/1zSbnTus9qWvbQU3KhQLlPE/Aiyb9 AjeObXsnUcM+A8pfe0kWHqv/ADjfJpNz54tFv5GTUdNguDpPSjrOpEkbMT+xyZkA7u2Zfamnusg+ LdpiLp9V5p3NdirwH8of/WhfzD/57/8AUUmKvfsVYZ+cPma38vfl1rV070ubq3eysIx9t7m5UxRh ANyRy5bdhirwr8z/ACXqXlL/AJx28taZKjJO2pJd6undZriGZlVv9QEIfcYpD2jRvyv/ACs1bSLL U7PSg9rewR3EDLc3JHCRQw6S++KE98rfl35M8qXF1c+X9MSxnvQBcyK8jlwu4H7xnpvvtiryz/nG L/joeff+2ov/ABKbFVvl7/1rfzP/ANsyP/qFs8UvU/zD81+V/K/le41TzKiz6eCI1tDGsrTytUpE iP8ACWPEnfYUrihg/nrU/wAwdZ/KzXtQuNPsfL+ly6bPJ9RnMt3fGAxEgNwNvFC7Dt8fHuMVQf5X +XG8y/8AOMlvoSECa/tdQjgJ6CUXs7RE17cwMUlJ/wDnFTzcsVjqnkTUq2+q6dcSXFtbyfC5QkLP GAf2opFqR/le2Kl9A4oaZlVSzEBQKknYADFXmP5heaNP8zfkp5q1TTQzWHpXVvbzmnGYW83pGVKf sMyGmKph+Qn/AJKHy3/xgk/5PyYqwj8zwP8AoYz8vj39Ef8AJybFUs/Ma30q4/5yZ0C08yxeto97 YJb24Z5I1DSCdY6MpU7z7bHvil6nL+S35ZSxmOXRVkjb7SNcXJB+YMuKHlH516donkrzF+V9nYW4 0/yvpupNdPEGZkBjubZ5GdpCxY8amrHucUvo0EEAg1B3BGKHmv8Azkf/AOSY8w/9Gf8A1HQYpDxX Sda81eS7z8vvNnm6OLVfJ8lhHb6ckaFksAyijopH+9IRBJy/aHJV6bKvq6zvLW9tIby0lWe1uEWW CeMhkdHFVZSOoIOKED5nsNR1DQL6w054Y7u6heGOS5XnEvMUqyUbkPbJ4zESHFyQeTwC28s+cPym 816Xr+qStqOkylLXUr6zt1ZFhakfG4kqk1UADKWUqSBvXMnU6jFPlEiR6tEIygd92Rf85QfmBb6X 5atPLVtcUutcIknMarKRaIQd1YqKSPtWvY5iRnGJ9W4bMkqD5yt9R0iKJU9Q7DvptmfxL5ljVab/ AFNwifP7AyzyD5Z1nzdqotvLqOGTae/bTrSKCBWBB9SVWNKgn4RVj4HLIavTXYx7soRMuX3PSfLn /ONvnTQtdsdYtNcsluLKVZVosoqBsy/Z/aWozJydpQnExIO7ZHTGJu30SvLiOVOVN6dK5pnLQ2qT ahDp9xLp1ul3fIhNtbSSeikjgbK0nF+IPjxxV4DoP5V/nvo3nnUfPFlPosd9qUsr3mmSTXBhkSd+ ZjosXRDTieddu/dS9LTVvzrciE+X9Ejeg/0ptQnMQP8AxjW354oX6T+XN7da9b+ZfOmpLresWdTp tnDF6OnWRbq0MLF2eT/ix2r9wxVkHnHynpXmzy5e6Dqik2t4lOa05xup5JIhP7SsAcVeP+UPJ/8A zkD+XxfQ9EbTNf8AL3NmtGvJGj9EMa1A5JIlTuUHJa1p1rir0bQ9H/MGys77WNY1CDV/Ms8PC00q F3tdKt968E+F3Yk9ZHUtTYUxVg/5T/lv+bHkXV9Subk6NqNhrk8c1/ClzcRyxNzYtJETbcTQSGqn rQUIxVF+afIHnvSfzYf8w/J1va6ob+2W21LTLqX0G+FEirG5+EArEhr4g7GuKtfmX+XP5h/mH5Ke 21Q6dp+q29zFd6Zp1u8rxgKkiSJPcMPidxIOPGMAFeprsqmUuk/mz5x8pXPl3zNbaf5fF3CYL7Ub aZrqWYEbiO3UBIw9PiJlOx2XwVVfyW8s/mD5S0RPLGvQae2j6eZvqN7bTSNO/qytL8UZTjx5SMa8 gR0p3xVD/mN+R1p5h1uLzV5b1BvL3m63YSLexCsUzqKKZVFCGptyHUbMGxVX03Wfz606JbbVfLel a7Kg4fX7PUPqatSnxsksTdf8lR8hiqH1fyh+bHneJrDzHqdp5Z8uy1W607SGe4vJ0PVJLmRURVP+ SCD3BxVGeePIurRflovkXyRp9uLSeE2jy3c7RiCInm0myu0jyMTXpua4qr/k/onnfyz5bs/LHmCx tBb6fHILfUrS5MnPlKXCPC0aEEcz8QY9MVYt+YHkH80Nb/NHSfNukWumx2vl/hHZx3FzJzuEV2d2 cLEeHIOVAFadd8VTr80fypuPzG8u2FzOE0PzZp/J7WRZTPEhLbxPKqozKeIZWC1U9uoKqW+Xh/zk zDaJpF9Hop9MCMa9cu8snACnIxxMPUen8yCp64qnvm/8nrbzR5DXy7quq3F7q0UjXcOuXFDILtq1 PprRVhNePpLQBadxXFWNeTbH/nI3yzYp5fltNI1iytV9Kx1K6uZFKIuyhioErqo6Ax1p3xVMPP35 ffmb5h8jT+XF1Oyv7/WZkn1e+unktra3SBkkit7O3jjmIXkgq7HkaGv2vhVTDyt5A12fyFH5D89W On3elW9oLeK+sbmV3Yo37r91LBF6bxjcOHO46Yqk/wCXvlD84vIIn0O2Om+YPLEcrNp31m5ltbiJ GNdisM4Ub1K0O/Q4q9hxVTuII54JIZFDRyKVZSKggihBxV5Re/kL5e1zWDceYVlvY4EWCycSvGYr ePaKJQhC0RKLUip69cBiCxlAHmmWn/8AOO35S2cvqnR2uW2Ki4uJ3UU/yQ6qa/5QOR8MMRhj3PQN O03TtNtEs9OtYrO0i2jt4EWONa+CqAMmA2AInFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX/9k= - - - - - - uuid:73daaeb6-1136-654a-971b-5e977a1fafc2 - xmp.did:01801174072068118A6D8685B6A1D455 - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - uuid:a3439645-3ef4-5b4b-a8cd-d96103a2c5f4 - xmp.did:370310640E2068118A6DC38C6951E8DC - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - - - - saved - xmp.iid:360310640E2068118A6DC38C6951E8DC - 2012-07-11T10:44:56+01:00 - Adobe Illustrator CS5 - / - - - saved - xmp.iid:370310640E2068118A6DC38C6951E8DC - 2012-07-11T10:45:15+01:00 - Adobe Illustrator CS5 - / - - - saved - xmp.iid:01801174072068118A6D8685B6A1D455 - 2012-07-11T12:42:08+01:00 - Adobe Illustrator CS5 - / - - - - - - Print - Document - - - False - True - 1 - - 297.000132 - 210.000102 - Millimeters - - - - Cyan - Magenta - Yellow - Black - - - - - - Default Swatch Group - 0 - - - - White - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - - Black - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 100.000000 - - - CMYK Red - CMYK - PROCESS - 0.000000 - 100.000000 - 100.000000 - 0.000000 - - - CMYK Yellow - CMYK - PROCESS - 0.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Green - CMYK - PROCESS - 100.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Cyan - CMYK - PROCESS - 100.000000 - 0.000000 - 0.000000 - 0.000000 - - - CMYK Blue - CMYK - PROCESS - 100.000000 - 100.000000 - 0.000000 - 0.000000 - - - CMYK Magenta - CMYK - PROCESS - 0.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=15 M=100 Y=90 K=10 - CMYK - PROCESS - 14.999998 - 100.000000 - 90.000000 - 10.000002 - - - C=0 M=90 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 90.000000 - 85.000000 - 0.000000 - - - C=0 M=80 Y=95 K=0 - CMYK - PROCESS - 0.000000 - 80.000000 - 95.000000 - 0.000000 - - - C=0 M=50 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 50.000000 - 100.000000 - 0.000000 - - - C=0 M=35 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 35.000004 - 85.000000 - 0.000000 - - - C=5 M=0 Y=90 K=0 - CMYK - PROCESS - 5.000001 - 0.000000 - 90.000000 - 0.000000 - - - C=20 M=0 Y=100 K=0 - CMYK - PROCESS - 19.999998 - 0.000000 - 100.000000 - 0.000000 - - - C=50 M=0 Y=100 K=0 - CMYK - PROCESS - 50.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=75 M=0 Y=100 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=85 M=10 Y=100 K=10 - CMYK - PROCESS - 85.000000 - 10.000002 - 100.000000 - 10.000002 - - - C=90 M=30 Y=95 K=30 - CMYK - PROCESS - 90.000000 - 30.000002 - 95.000000 - 30.000002 - - - C=75 M=0 Y=75 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 75.000000 - 0.000000 - - - C=80 M=10 Y=45 K=0 - CMYK - PROCESS - 80.000000 - 10.000002 - 45.000000 - 0.000000 - - - C=70 M=15 Y=0 K=0 - CMYK - PROCESS - 70.000000 - 14.999998 - 0.000000 - 0.000000 - - - C=85 M=50 Y=0 K=0 - CMYK - PROCESS - 85.000000 - 50.000000 - 0.000000 - 0.000000 - - - C=100 M=95 Y=5 K=0 - CMYK - PROCESS - 100.000000 - 95.000000 - 5.000001 - 0.000000 - - - C=100 M=100 Y=25 K=25 - CMYK - PROCESS - 100.000000 - 100.000000 - 25.000000 - 25.000000 - - - C=75 M=100 Y=0 K=0 - CMYK - PROCESS - 75.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=50 M=100 Y=0 K=0 - CMYK - PROCESS - 50.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=35 M=100 Y=35 K=10 - CMYK - PROCESS - 35.000004 - 100.000000 - 35.000004 - 10.000002 - - - C=10 M=100 Y=50 K=0 - CMYK - PROCESS - 10.000002 - 100.000000 - 50.000000 - 0.000000 - - - C=0 M=95 Y=20 K=0 - CMYK - PROCESS - 0.000000 - 95.000000 - 19.999998 - 0.000000 - - - C=25 M=25 Y=40 K=0 - CMYK - PROCESS - 25.000000 - 25.000000 - 39.999996 - 0.000000 - - - C=40 M=45 Y=50 K=5 - CMYK - PROCESS - 39.999996 - 45.000000 - 50.000000 - 5.000001 - - - C=50 M=50 Y=60 K=25 - CMYK - PROCESS - 50.000000 - 50.000000 - 60.000004 - 25.000000 - - - C=55 M=60 Y=65 K=40 - CMYK - PROCESS - 55.000000 - 60.000004 - 65.000000 - 39.999996 - - - C=25 M=40 Y=65 K=0 - CMYK - PROCESS - 25.000000 - 39.999996 - 65.000000 - 0.000000 - - - C=30 M=50 Y=75 K=10 - CMYK - PROCESS - 30.000002 - 50.000000 - 75.000000 - 10.000002 - - - C=35 M=60 Y=80 K=25 - CMYK - PROCESS - 35.000004 - 60.000004 - 80.000000 - 25.000000 - - - C=40 M=65 Y=90 K=35 - CMYK - PROCESS - 39.999996 - 65.000000 - 90.000000 - 35.000004 - - - C=40 M=70 Y=100 K=50 - CMYK - PROCESS - 39.999996 - 70.000000 - 100.000000 - 50.000000 - - - C=50 M=70 Y=80 K=70 - CMYK - PROCESS - 50.000000 - 70.000000 - 80.000000 - 70.000000 - - - - - - Grays - 1 - - - - C=0 M=0 Y=0 K=100 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 100.000000 - - - C=0 M=0 Y=0 K=90 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 89.999405 - - - C=0 M=0 Y=0 K=80 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 79.998795 - - - C=0 M=0 Y=0 K=70 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 69.999702 - - - C=0 M=0 Y=0 K=60 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 59.999104 - - - C=0 M=0 Y=0 K=50 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 50.000000 - - - C=0 M=0 Y=0 K=40 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 39.999401 - - - C=0 M=0 Y=0 K=30 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 29.998802 - - - C=0 M=0 Y=0 K=20 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 19.999701 - - - C=0 M=0 Y=0 K=10 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 9.999103 - - - C=0 M=0 Y=0 K=5 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 4.998803 - - - - - - Brights - 1 - - - - C=0 M=100 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 100.000000 - 100.000000 - 0.000000 - - - C=0 M=75 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 75.000000 - 100.000000 - 0.000000 - - - C=0 M=10 Y=95 K=0 - CMYK - PROCESS - 0.000000 - 10.000002 - 95.000000 - 0.000000 - - - C=85 M=10 Y=100 K=0 - CMYK - PROCESS - 85.000000 - 10.000002 - 100.000000 - 0.000000 - - - C=100 M=90 Y=0 K=0 - CMYK - PROCESS - 100.000000 - 90.000000 - 0.000000 - 0.000000 - - - C=60 M=90 Y=0 K=0 - CMYK - PROCESS - 60.000004 - 90.000000 - 0.003099 - 0.003099 - - - - - - - - - Adobe PDF library 9.90 - - - - - - - - - - - - - - - - - - - - - - - - - endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>/XObject<>>>/Thumb 15 0 R/TrimBox[0.0 0.0 841.89 595.276]/Type/Page>> endobj 8 0 obj <>stream -HlWˮe; X?pұ㼦4#>` /QUZE-ٕcη?}{~WVXY׷UϮ?~~@n%|l]V]?se+áVi]W&M˰k91ji%(&܆#F{>r/mggѵw؆֐ gV&F+~5j/jVq [m|Ғk>fa\ cjmvpF͊C cCC/1nXP_+{`0XcF b7[yU YVa'( J;DTbx\!F'bg;s nw.DB7FUAys|c4l#fuvnp0@0*!Bណ/D3bc5HS$yo{50ydhd:鈁^KC:FUµh-L$/fg>lwXD.Phhaj>HQȘbL_b&Σ % .L,r4X̷xy]#gO -yjץ#6I <4 }!.ҷR Ɂ_^"~50cN? 42lDS(1;ʘ́.+T" j٘N@ k apu_X:u2ijsg0z?y|$ -HgHPUH(ԏZ%{nRT64rpa;Z'V::pV2Y]g-3't출gKdb&aOS8IH;@;%PrrF33DnS1d+;Yu#xOjgk &tI`n^ -斶KEc<1nJ%5W׍Fx##pGatbTH\tUVF*"&Xm &F DqX7>92 No|m+F. -E!%C_/-TY -.:}>ᬏI+'+'\^"DNn*4WaG -?:Ik~3Ap=@"Q~!؝ڣ'=3ώu$4XVY0~dszH kL!;F g˚M^M3=%rjO`Y&Y2P-P1".gbő T6i;RTʛQ%:$1,eqyew -7-H)RĕQJaw7c_贲IY1qdU)D@0K!a79;gk.Uo(QM;SzWCZx& ĭ5h|#G;tqCI.#jA;sBf?~!kmf)h]090gpQ \g8T ,;r3>e SZ6h+cf-) ,6怈S#b5oWP9҈vb>z9{ -b]Hj%BW0_L>4^jtJt=o$UxYqԪTgP!2aи2B`CIvo5ԃO?`4NdPɋo|m9/рzF: -g :hut- pY9ƚ _dv%g_, F -T)$zcy L 96u y1yZjcgY혚OdZM1d,WR"wzER%25ѺzU^a إ>,&!CrrMQAPo ن7@AHX\Z->!<{eGIGؽ}tq>*2u{޻CA#o# ΤqG`n&}j2t -=hhl5We`*>Ԇc8e[4!q6['|qltG`憜2X] UbDPS@ְ)HcKVOydn%)25 %cct> ci -ݓg:MV7ɩ|9L]Ƙ -TX<+XփG-ފMI0ӷ%-`y${TbW:oRxȟ6`\WZВ8F}S{nة9(A):mhmWCav붉5( @qV;EwqbjRfb*jATM!YngzM8uSb!mtg} x7,º8# dxJedGrQOA 3FG0ڨӟGf)X=`,NJ=:V, nGc3jAo-j1( -6`2(֛SJGYgk1`7R~oH0lmt0r1((> @upAjHg - }.:9\ -ub=ؽNFrQk1S)jn@KLq}eg;Y`mFSd&#(8vӟF l -]ׅ*vSNYU܌>Pl nKp{J$I;BM6m~WPFċg63,rXլ\w7bpp/z+#EdeV5B="/xӼq]=*c8 (jV xc9gp)p8+U!2a-V*v\:H~ZkVD^9[ BBq hE)!6:=MsԮFRb߸8M:ۋB1jJWlF$f8¯4Moe,io)Kfq_=Y凵(¯00%戛Mf /| - hfA A5ડ)xig*~z3ۖ_ / &B 3-O ןf&X Yj̔c/,ƶ0n&ڨ{Y:?@]uyT qq#¬qHj_(X׍w:)S 7i:B /;I͊Rh^).mi:Kl-LbtR=HS+ɰ=n1o &"r7XN'N%_|_S2}׉N3y.cCwwG*>~3,4,H 6:`l+QWy+ %azA6ye]f0 )$6cxiϧta endstream endobj 9 0 obj <> endobj 15 0 obj <>stream -8;Z\t5n\em$q4;r&*!#<$h)pKE1J6dN -@mp")aM@]333gC>D5)'cf"Z[;>G&o<,u0oWA8s:*3m7l>kbtq<7fE07cH@[r=TpBa -Zn,Lk[MLVG\>EriI%[mr$K_K9C\?O6>5Veg1RHCKjbI*Q=5<_Q\#N/7)t"a5'TOXJ -ZsL'KM%nQ9;=.?MMEHO%]T^sMA%)A+c74Mr[9K.FQYGiT;m]\IfX6ps8`_);Q4b(4 -X3N0kdM^#q]4uL3*HgbRFsZ1I?##6h[[eUdlC.[kSWZ(1=If(*U$k=Wm`(M__-K2V -^KiLo;Shas>Gobob:U)-Gjo@;]h8YTKFgHU+lij"!<=@C77d~> endstream endobj 17 0 obj [/Indexed/DeviceRGB 255 18 0 R] endobj 18 0 obj <>stream -8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 14 0 obj <>/ExtGState<>/ProcSet[/PDF/ImageC/ImageI]/XObject<>>>/Subtype/Form>>stream -q -/GS0 gs -129.1199951 0 0 133.1999969 134.8740234 237.2716827 cm -/Im0 Do -Q - endstream endobj 19 0 obj <> endobj 22 0 obj <>stream -H vݸE'V/$J"XZk6߷77GGQԠeYuu}E_GJ#gב8>X+}#}/} 퍾#I[.~]vn5=tm I]iCzWN{_SE}=U6З`;ߠmA|M:[}ztru*>}J+KX9](=aIz!tĊA!{KH,F9nYs(Xf!zt:e!A;Ik+FMK#Q+U+Q+U Y F]aeeeisEmSEmSqSq3ۅq3uuyuyyE}yE}E}ÅӁE}E}E}ÅӁE}E}E}uuuӅqSEmSEmSieeaF]Y Y U Q+Q+QK#Ik+Ik+I3VwNB8Zr4ݳ =`:gztBc隅)5+[[I,E9nY^s 8d1zqtj=!{CEw,G:X^t =bAz1tŊCM+қ#GA7,I:NX^u(=s`QztU]һ>}J+KW)ݯ.C:_]zهtzi ->U}DǫLoW4=]iz'ttuj>O+N{.W6:m:\uztwn ߤէIgO/|ζ==j;ߢ@o|-:Ʒh;ߢmA|n=7d{+ߠmB|N :7d3_mB|. e:.Η`C_B| e:6W^C_{C/} =U>\K_sC/}ε=E:>\S_kmDO}%:NZ;[_c gUeYte&[.B͊2ۊ%Ư(>j N!d`U@2qs%@3y",g Oc"Q#m)GW5VNfЇ4{aM۽ -.nAG#rh')}*vXW9_},zH݁>?_PW5a8:ޱjʂAt39_ɜ xgR>I|F }#ߛb[9_:h:OR>&O9~3{˱te|[ e?3W_ ߜ0rTN(Ѡ~Jd(:˲n:\ ^xt+=W.Xu(=:aMztÚ#%Q+қ+V7DgG/:X^tݱ=0:d5zqtj9G-kk[JǬDo9Yr0hgzt2UE3Ik+QK#Q+Y Y aaisEmsq3u3y˅ӁÅӁһMWBGNI6l9!=,s:zitt`fҭskMcgKDo5Ν^j:< 'gtH)B:˯NkP:80= K/ҳ@o& -KңA"" ыB2{,DO=Bkk,E=Z S,Fϱ=jCG/=Ê&k+Ho=š*+,JVwh4ο0=/MCKHզ}@+NNW6<=m:\}zt ov7EGۂmztMovwLۆ2lzt/ҹvDڊk/z t/Щv~=t_3mHO"iGzH[ңDGړ^:Ѧ/Љvw-=t}lLOggzo8[Kٛ^;iv?NJi.M_ zJ[2t#IM_w.;BiKLhKDh[Hh[@hkxOhkxGh{Kh{>>:CJ]}o⍮M7hGUC,0V/cY}X_F;чюec}D_F;ևNec}D_F;ևNec}DF;֗юa}XF;їюa}XF;їюa}XF;֗Nec}D_F;ևNec}D_F;ևюe}X_F;чюe}XF;֗Nac}DF;֗?va3{@1 fA 63bA lf`3 1 AM 1bA  /(^PxAтM - 0 endstream endobj 20 0 obj [/Indexed 11 0 R 1 24 0 R] endobj 23 0 obj <>/Filter/FlateDecode/Height 555/Intent/RelativeColorimetric/Length 42467/Name/X/Subtype/Image/Type/XObject/Width 538>>stream -H[WlFM;HoA4 AEPD,U׿m)w1r̼ywsBFFFFFFFFFFF拿kd6>kF6e[&ZH(aHs>BW|k/'k#>eBVV"ucI FVa/+BoBz̍:lYK1-kԈ:e֊ cF j徱uXˬ ,T m"ܦ)xuHx](, -B|}Ľo5&#ks1j, -BP4|>w*!DPxX!m|1ugPM )?߮0рp#' Q B"ر㟔MB |0ag"cׅBQA( 'd'iN|(0!C!&!m|J1wˆPA&k׮/Jn|̔0":j{juCbR+ aϞ={݇7ڇB@! סaXt!ꂳ@Y` -BLml8p9dOlllH #A)jqmX mάuACXPYp&@86 ڒ"!@{`݁p(!mX;FTSl(,(D}膓>tp"P<;PQ̆apUCD`*hpuussww8=pՕB>8\R(6Skڏjs ..4 - #^^>>>ooo//OO0Ap -E,9TjkŨ0ABsDBcp!D*7H7 -g<Ca66M, u T4D 3A$CBBCÏ"jp&LUPx0F8l4ya Q]A]`8pe삮 -`&B!2**::&&666N bcb"##$,@!Pt8mP&aX`.0F. G<} OHLLJ2 , II `E@΃tPullcۆ( - k?P up "` -  -f"!1ɐ|,%%5--==###S RSR@Ő!q!t:XsGq(6m"ilTT_P_![{'gW"0]A*⹉4x/Pܜ쬬̌T1A:8vTd3E NCΓ $ - ԅ`q42TQDn^~AaQqqIiiiYYY\BBhC8\8`fΟPz46*&ny\8 `,c"=3 &DIiYyʪꚚZ-555UU+*JK #1tDbrXFYm(e[y"Kcc C$/8]GE"+'/HTUԞohhllj:r455664՝Gz鈏8 -~(f 6LiXHB Ω6; # H/,.)8^UHégϞmiiE(3gN:Pρ#ǒGxhp jۆ YC$T4H '"(cQU5'AlKk[{{GgWWwwOO9%x -#|Gy@u$FG6o4hV YCL0h`hbh -B=_rewbo`A@<Tly{Phj4?ӘLŽ$ja`pb}/,);Pq+WGxnxhhpp`HHB:*+Phcpu=Ys$Q {gEbr*EqiE%c]@C#'&&''vmtdxh\HWG;t:qeg7b"‚| u2O4ݓ`I ' o?EEum=c h -B12:6>1yͩ陙[͛7>._@:ZO7GUEYqa^vFjrR| j#[طh 'ƺP"vO1ItwQ^YSp c C@S3n߹{ܽ{w޹ %3SdDžs̩ڪeY)XN(|!ɺ`' -#hTlAbuX\I iYZZZ\\Xx}(w@fn^zՁhEqFEiQ>Ն]A>OXit]czZeX1$#(Ș$읊g:{.\$ơbf(`ңǏ<]^^YYys{Ǐ--Biqm8zuƺₜLA#őRfڏ3 e`?`k #x,-3i:e`x,bnG {W_իW/_x,C| b`hA$'>,H'+ϞC߼۷޽oʇ߽{3*3s{vnF͚XbԠ&k4ѨkĈؐt:^ 0gh(1ɮ7}49}0ϿV=\/]p#ڶ }BcT:ON$ԯQ'dN(c';s"w|=":&>,rHEQqI)LTUWW -@!'FtDXHq=;_ИAu#!4d7¥Er%7 cqs#_^H*EUUu 8k4Zm^Vha>*ey!HKNERc(4֭E͞9}$64*'7ibM&Yv!000]=䴌Z[HsBDC8*ܬTЈ B=j[7+ %(:J=gXr dEVN@Vfxwt>:;;;:@|PFvFjb\tDhUESvT';nxѣ$]8J= ?(4<:6((R)UH:T0 A>񀎛-rCBiih0 5EM``blJMr*F@pXdL|rZ&wQBhPA(DOO=G==$xthV+P(4bC}=ݜ14 ROH߯1mOZ:fw" IRjsQ[ih$AA(=?|@xV*0?'35)N~z<3;DaK0& 0=܄qq4 N -(*:h-Ƣ ޏ~c?]d1ޝ6d6d ! Pb䲻7NȘ켂R@Z@X@@[b9 !2Ĥ׆_`kF>vߝ6XF0v -0\zT,lrUuM" - #tX %]u0x{Ɩ>|2FneH2=Wc\EV!VlcpqIC`M"T4xd+OvjD7l|0a#엇SWGgggwXvfh@5QHo` EP# " Pz&4!"Xfݝdg's}~1ϟp Ax zUi¤i3V٠Ո×Oy|.(~pҢkWĸȰ ?/{@SBA+ΖHd fɘqʓΜ5WmR;<EIK*(Y2\e `4i(-̿{."4㠫m&Jsh0>c$CE,:u`t wsB`ɂ, ;$b\2`PAgG{+ j59!lĩ'8meaEkU$`- ՉSf@.Xuf܎xZQB)Y>~,=/(i%vh_jj|9]Җ ̒o-f0ˆcVMT<#1 q._Ə4]P-M= C=U֭^l_B|cȀ!1 %c^]l10ݾɠ3!WяQYs6 jI%{"ň; qR pO]Ν5sڔTH`(A'%ꋖZq =s>>TI5j`ID*%bDaÀ(Uc10C"c'}B^E`oqp9t'04[F\E U%b#8|`.KbP2~ܘF#%0|9 -٧2'۫V{8}!)=+:O =šG ϟ(׆ܳzn^9ݹP{ÄS"mɠ/2d U#s=;{x`\W65eE=b"pqܷjQ_ъŧ(JFPɷO%s,^ݱnQ/._z ̒d0`jvOPB D1;;@Zo%Xђ0dp!D@2@$kL^ݽ~]L|ZqyUݭF08B=YUQRܓR o$V%K2dZ{ -Dc'"|\SGOߠKW <00z{d`ub재b:qc-hI&((rO Edsɶ6W¢b -|0dC0~^;q2pTAX1qbuXU \S 'WϜOH/D`ii(.`AEɳn%-M{b`Ċaj$Vɒ|)b(Kd U%`t^ <WXZY[5CV(,-Du:$+U#*ViXYI1x$l>qxx^-(vO0Z(0 JJi=rCcd҅DR ŝL~4 },A{? 0l Lq+U!Vɒ]KKl G2>ڧaOpҫ׋MGO@`/3Z_{㞁'8PAjK"};DSO%ljjmEx3`0Z(QV`Pyf%R :VLTV]Iu}K OM]C>},^_* &J{{{Zo75Ċc^1KM2'#S̒Ɩ·O_`@6Z%Qº+6-ZL’@ -DRO?XE$f}dn>Q,a{͏o} 0^tB 0.%]8O  c}MT*+.P=%0w1/Y8 BI^OF7 %i#C{:{ZmdUfIY Ŝ`K$CK -ۧO`XTLb*/K?0^}_0VZPb0pDp6vObHK2' VQb`٧w@h$'7K^C|`&`tGP[Ej5%G#{jad>~nɈC• S$zDD_H/,DЫjk=5{jъAŪHHN4XG-KN[GOҳ}6 bd`<0nl(bs@1FҒ(/ #: -m ud -ML(AYҌON06ݮ`\NOƵD1r{~U!-bON~1$c,d = -Q\'%?ɵwJr:VQms 9?dHwb[BIl¥+p%Tdt|rfN~Q>_{q0ʥ ёaVIjS&V%Qn aKT5@2ko%$BRFv^a)> c!#Vi0p09mDŐdH%[B+ M]#sF2^L-اh7{0[7KEV9Q:vOb0K" VvK X`ݤ#" w3KFEiA^Nfj"q*U :J!J8I)Cv- gKpdlجg•$U}>ygF, c=MV{sCbr-!%뵶[X8e$FݭwH{tCL(7|cHOvKFo)tjn1d%# -$2 6nEQ' \(J(QVSR!>- [D dF}Sv=QHFd7) 1 /.,͌jo$D<C'wK&L!H2ʪo""~`D1`joXKjG=%l %qIYy7jdǒ/=; =reȑTRҬyPI*)4hyh@%HP)`*ʐ"qsZaޝu~Y}h~} -ٓG8F! FxH-.VZ⵪!AnOfb]V`M>z<7J=edL#!i VQ=!s %jK;`9{{8G ߑA 'N0Zq0)`X#0TZfb]\kRZ3E02aPuy*sTZ% *K%f֫`>]p;02 T00>h C_[cb%V\a8Kd/PV`5_.!5BIYe-42G{`Xa`,k% t*r2XX #K `xmpt63R_`@p% u!dF7`d<yd   {CPY Ze&8OXK6oP !`!R$ %d .<28'/{q0Z\B  Ie$jObjƉ3犐K`Ƞ˧"aj!;WV( p"%!qK -.=2 jIՔ10fJM#%b<.1&\F\_D8Ǝ<7ZeDl'Kl0"\RXTZQs3X `bQu KS0`L`*ÅxNdc.I']҄\ -_ :0>׷HQ`dnX.9t$+셒kK8{d|c+7]Dc ?0$vrIRZIpՊn% -l0X`4UgO ϳ0$~lMu|^g\]։Dp_}n7#0 c ;A;NU. -VN`|>Q]^ZHOILc* Tq :)KKZ0` "0޽y*F*(`CGK~$\%~KN#T7mz%% UųnFS}u a`C=?O.&ůK4޾I%#W100iYSqHMJ!p"_I0F ?%cqDCtI$\Gq+Khl0:p0`% O:.`s -O鹂\r%n0X`~wjn\+Xp/!gЃDG&\\bDd' $pI+ld|VJ_ Ȓ/{mF퍲Ɖ̴{cC}0 G4?),10%CB\BGԫ_!0:4V]t8|p_lDH&A`ad.CuJK%d%$z{vu>rʥsgNf>/."4`&Vf`C"q\rVtz;xQuؑC"Cm0 0F.5 zKR,Lj_3ƺҢ¼cGG -ܾŝOq/wdplZ"Kj;Q_]^Z|>/'H];{8FzZByY:dy /]kdNd7% 7v~UF띦Wϟ=u4;wxovqZec`/Q0J3`̱@dpD\\bj\ .8q%_^}FMŵ gOOOI;hͮ,0f2`q&.U\bli. =N[cA]M@PsNOML n6CcH%cqL>kD\bhnz$GO,^sCme3Rb‚|<6`h1C,`s -|0?ԵWZK<|%KD_YZ[ ZpDF>ׯ0Yt"n0cq})Hur}K.^^%KxbgI UȒ[uUƹ3'3~J 238/CRvɨјK9GFD sɶ Kݨ%%l2p0AԫUEy'>/."4m -c|9c$e.J%&Vk]<|C"KK$Wr$ @w2U%) #pa,L\2O~bp  -wH֩|%.ޫW&}Zk3C]MƤ&2`HX`sx%2rK4t ̬W+*!)-dypI.Y,^{xzJ➨;{8!04*)01C=?OHDVAIu%dV)٧%57>x PG%Kؽ{*s#=jϳg0`Q]2wTYsɣkddΰK.]wB]BOVȒiI{c‚z[ `kS0F2e+*40vpr ړ\RX\.~j-A.ZX`WZk/QY(/{ OYE4-ݽ'-TZ^ .i뤸 O,9?."$m;K3O cٯﯨ4'd5b٘ -c/أ ,`AIEHSQ XhDz`]k;s̝e&'_0gsCՏ^~&[hu,l#bJ?WPR^U [rn+%[@>+QL^l -?0F *ЖP~љ[g` [b/95+\qYemӕ%[Fٖ0V'd UKs3gϘ<^g aM< =A~h?yƜށqIG2N.-i-[G[ Ϻr'%^Ыfث?Ϟ1eMUzP %df_bm۝E\JkwLbJZv\k-|)ْl "3۠ϺK%yϤ?I^;c09}D_G?~.1Zmjnet4"g#n da}2x賾4>/;+&K -_< u<~/IEVdaP'n 'B~ - Y}Bx[K^CO}}MQu(?6;£%f+-rN'gK?ANׯY2qd W{^COy\vpph橳EeWnܾ[l;-Qm7W>ϰt475gWa|) -H>?]C"b>v"Zk;n3fK>(yd>Q5}>@%,~P~!^'C1zk6l~펡kQPV%TzU< U=n -i%'ݩt,QSq\~zٻP[픟pWK N8z0!:sL-Is --;â)?MN ?+'n A+.2 22Sbgէ''KP'd?GiG )%>go%)?u8t3/y?̖|P%z ͦ3)ѧث{9YɩYKϛm/T%`}kc-"#W W{-f&ƀYe)fz`*gaŚs2 -U֮X3d)z?#|2 e ~b~[ B@Q 22 /+O1Kdp$!Ӓ3ml0?+ rC AF c/>gNdh"2 O0Td>}-[O=O'O] ? |~+Ș7kA}\XyKi-g2~|KՀ32Ȱ`1C Q}r-6AR~?)?0X~*ݒ.ɖ޸XslNVZJ\d 6goQjyJM;3N ?9["얰`M Qj|*O(V3_R󁔟ْw-a!S}OXOXO+g`XT ?pN?y[ųǏܿ1wy: {!d'W'cĨ1 ?X-l~V ?[qid2 8"Cԧbx䧵}ɩ9KuP<~dK-iu^d1^?_2P*V'iPv.!q.~vg[R}?Xm\R)2P|2Uw)isU[RV|.'W-Y0G"c ? -b]ljeOYTVY'CpK:A6@#joenf@oC!jbHm_XꆖShK[4TW9IV@tDȀ'Thb]jcgXtBrjVιb!~~lNnL"2F3niaGy;SnK^p<%!tK X,;)"+V,VGwߠ})iٹ灟̈́POޖٓGr$$*6Xi3*"COX5b~.2~ZڹzFƓb-b??O$`[i( `ыEx*~ʋu-N~X;)?_+'gK oI%$XW`Edk@~"2,Vs+{7ReꚐ'uB~r ^dI-qsbfb: >wd|>^~(V}RΞXO(V'SdKIDْ[?;; ÓnH7"piҥ_b -PAQ1Y *D,kf75h6^ -:s3%%0X1Kx,u{oU(5o&eB՛b| Q|WG0K*0KIL p`t[CX*^+qb<|+S?{^YBAֵ+O,ٵdI*/Kb(^2zMC b dkA&vvzc~v R?gOEYrhoJ̒t6KHkU}{p_nXAbt,b}ޅ2x~}e%vRD%Ƙ%`PkHtH?d *XCqR,V~˳,i;shӁ=me$*K,, Vk($nǗ.V5\ \^[~bet20K~No>dY. VTy3o6Xe,!Pe0Տ,ְؤ nzqa%ʒW/w,, {Q(3 -h|+Ū,\!QJ'(/S䟐%wYRV%6&~cg/]98EʀŊʰePKc3qVbeAɣ%7YBeIiaNF`HFAT8X'Ƃ݇p<>8: 2eHQd.NH[_Rbe,9V:K֮^'jJ%".T4 僥CG߾%XAj Cs+[G7/XK#VЋŪ~.=y %i \RY2eȜ -m fCCU -7SPkp>e!'d jk Kr3b!KYb8K <,(*h$T>s6h0eb%0X9yb]fFj;y׋ ~<6]p@^ U,sǂ 7D|p0G߱HP?e؀2&Ϝ8,&1#nc+((~b,i,i>[KW'D.7sw"K\к T(pèSUU ç'bC24Fh2ܽP!ye[p6bt^.VR?!K~,iܿb}QnfrlNvD_yD  ǂ%ҡBсp+6)뒰ؤܢwՓb_=,;_l,i:@d]^Vj<ɒI.T2fIwsAt!6NwSMMG(CR,VTO@9 CӲ˷2p^n+~>%.s[ '=1& dx'/Kd(PA19w"!G4NrlYB9R!#M\]as5,VPٶ2z\\|OX_z+WD,[0{{%P?Y,`{'  @$F9r(r'F=4988xl6+C!eN1wQhtbzNAʚb X~ރy~nX&+%.$8gb8Kc(ðp2X zȄh83r1<dc q+ 041Be2EĥdYq˶]{6;y(kwfɓG|h۷JO99u,Q]" > -037H,,YᧅDb#<8 )_)ehjb:x2$fVT2[AW2?q*GYU(7396|YS'CY2DiBa T Z*O1G<=]'dvHywf1U Eƥf+۲ kS.\X_/eG,3=8wtue!0p0\. Dh,1pw H88" 08tQ4T}]m1xȰkd4ya1I+wn=qXPsςĘ0Ȓ)e%%%T T,х1BbTc]\] qEh-Xpҹ$q _q 8ё ` @F"+ეeJ*ZF' eVӕZ3?6#ObKйs㐩Io?R0sNN,r DbDV*/+S(S(RYTR$*da8؉-N֖G6EA54PƮ=+eceU7u97p qo22~лX!?AټV~7U%A49Zg7O NOXbښ -EYX* 82Rj6}}ߐ?oFퟝw>;/QBhkIiC[(;mU -AexDes+2)ex;IS-VOXOI~z:Z]=91$2=i3+뮞 EF+_X\Rry|~yy@<.SZRT$/';3-%ah`# 3=r`&@cHyTdFela22^z%=L~.R'CI.^B)cO7bi`E%"9yP **+jxBB >8Ņl0وxab{1̓4`y1v=2(c +EeP1`,Vϐ/+F`Nϩ2>&Tee@d]N(pU5"X,H$bH$D> l sNu{kFzNׂ46h̗|OfO(eEepF+\Ue(/V Y,X|ݮۚc~\|)Yꘟ̠ɲkش*(nO(%#;\ZXRW_/6PO*Jb#1.&*,$7ԆE󧴵D٬^)ј2N)QZ6VF2+krbLx,Vk8?gMWs vsP]#ӫpKoߍOJ`rUP4777566HGY)a#=%)Ath+:Сh yr!l9**#!93ʐ2zTSbb qfcfy`OXꚟ6(Ɍ._'2=B#b'eJKb:&Pc@QL SRa| SF#Qư*{|Q,ւ܌ aX^6źgud%ucc 19QƯֈֶή߮ΎvBGQl2SACnzݰ2dxFpeMص_(BNrH/bm_.W.B~bz:Z4KVEO݋N %iYZ1~Gޞ 870.&mow'ksd~6U2>Fᨨ:U1b>.bO5Sƴd|5HfY=c3kGϛF=xbqx| X o``<V!Iae&E<._YْTh̤Z?S-V/g+S b]?I~2.?g ɗY!3mzG%fpU5` rP ? !@٨,m=NbqoX(tN20AwPƛ7_1Fe(-Vs=\pb~.T -GO78ï!]=012y###p]بI).ˆ :!*cychmn -wpl@vw4CBleQhz89X]1;v@ -J~seCb&ru%XQjyK;\w?rΞ1ӡ@&ϞR`,Ps6tEV uw4{lW5pO([SӕAP[9eP_C-_&3/V5%r~@q /_s!x/.)-]TAyNA@(chkA-рvх30P 56SC{ 1(2Am !(#/+%16"P!X?TJg+'BN)Yy~Um]C p 5@,Kt86z D!6p Iqnm-.>)#jA(x5ƺJ^I> u'획oi2q] >PR*BmXhD%@"Q*%DPR!sdjS[%jG٦lY\u>9gz^? ʆ~pi<)nM/sMP&3M ~(<%׮^ʃ?y7@`Shj ©ajl/h`4 O n%`gXwDXJjӰ`(v_0fj:gI! bi-,Pz,f;/ְC)5@^+T&ǗѠZ@v#Q(d -xpvy)#vF_Ȱyc?H:avJv<բwN`h~;w|?\HA؀F;EcSvEo ] :M(ۼMb0lyd\K6W/d惱f%Xb =\L3`u¼c > H l 6dѠqS9Y驉((B.5с@'Dm%d<}\ȸ8ƚ3rK:,ދ&+;4I}!$|l@4h>W(LEuM F6_ǑI*n\ddHu5EQ`5XQh;V*liJM>GPD!$+M45L'lVF -zdln2>0d|`M~+=cbڱ+;jD4VDƧe:|rh|hb4h=q8VhVp fMsIμO0t c~1i2XV}EϝcKo_q4 Aڢ Neаnғ\ ߾+)`3 orx/qߥ<H )OV*+- -k;>Dz>A ['M@X2n2&n :+f8rLCV B_ì9MocHDdϰO!Ky4hDyF/~%\i{qj_ >C'n=!/1ȘkBG 2 H% kj% XFČ⍕e@='i5pp?Θ[iY>a}BR ٨ QM -pWZ85b"B6JĈ F$ c !c2n6 xge(xf+;* =7٫_i:OD),h  xTCJ+FRORjQ4  d2fwm62IS Z -;{gWTXU^,#u"2Z o -t>h@604Jc_#ddD]E CoȡVMn3F'\QsOFku{W܂q{=3 dTV',1k2822\$d@N[`TG"%=ښ`(h+}~315=E'Ojqj4'2E$zrɑ D'.PF  6؀Vo-v _AZB? yspl ްvRhZ=`Fj2T!OGZ7 xktܞ}O oVT}VdH!Y05 -Q]y,Y8g9OX4BM@Fh3/1S~q0^%r˦u+=/jŚpDh(F<O8}%^m -ݶ#>@QW~>/k}6Bij>A?XLEQd}¢վAW2xo2$w""!ѓ @|Y2:DDVJ#Ths^}4dĨ1'ϜǦFk:WW;!Pvaj8rHK])F]m8::z%f=z1*CF"e: #!62tF=i ޣ` u}~6é0k;ǩ kꇠO!LJRWO"*.AUķ -}Sc=]]]>ƃJW#߾~S])c"B|}Χ~%>164ܩ!QA " Į dz&Ģ%x'tO!C#';> W׾zԹu>y'B6TOp,&9P F# 18 g@ނyK /K@s !* I`4:qyL]@]*^1}L'stJFC3 OG2G'dR{TsQ8C!ࢵ#㛯i -G}u7d覩4UIQ]KJ+E/k++YA+}SȰL*Gj̜y -d6 Ti'ܜgc8Ѭh% h0uA:`0LKFC2u}y}"Y{'7K-HFBà--ͺw5А 3!Å q!'g!rQ+ f`0V#:4?jG 32u"kޥE\]ruUƛ׬O2/; ܃P'} &Fx Ȱ#2Vȸ\X,"U@j=IS'* }F#تOO3cDpZ5fj8N|>Q89c4 vA*HQ\`iҋKg.i(P娱DJPQ$QktrN&羟laK>,?3~ץLPln@%PaK!cGIjT Pt=rKOFUO>{JARBʷ43h,7oy /30Mɒ cFAGDfM۪"~T_F)24~Roʫj}H+s'|p+ gK(Phւ .Zl*öNn> 2*)d2^j!9"㧑{ 2ofG@˱45]Ș)EF-VW5q):zSMزJb<jyV: %vDcEڋc0VCCAd%ew!2piN~^%͍V,њȘFՠ*5]OCRBMAESB]mH4V,[ 55'5Z(<ȸ? _pEua# D9IJTXUޙ}mogOa_.VQ%4 |biflrr8+t kyP%}m]H.D=@C*LG'ҪmMO'2%k.rEOn<+Ϙ,241*gzP),">|Ɛb(k0%ܩ xlajlh```hdrFMh )2 2~3Hх)dEoBdd,e11/8 -IF2]Oj}BACOz=6rJ|Ѱ4_cjbllbjj=*jQ8d22M;JVQH!prMG$n4(U+Xקj)O%J(\ƣO<]!VfpZXC0ݼ#DI,2zwŽwQAƼfb*] EU㟓?NW]fL OA/VMGA|U IOr}F }IP%4=1&"8cvu !Bz!2z2n"x(d՗ 2K%b gukW.]81G6J+ORбB\oݸ<]Pj<~AƏ42~S Rz 2z:4n)IOdx2,&!wV vp}F\<'k[n(蓸0wptrq`GǧK*eYdW!2d?GEMi896"aXȘȘ"E5U T URd>Ϗ'-#z^XB'IPFWw/_A$#bɳ#RY5̀œ -2  N:L90]W)ɕC ow%yVz}sc]e$+-Q*pwss  -EWp - uMdQi''Ol|oP} y~ƣ߼wԱCvSDaA~>^^>B#E b`qȸ+Cm32"2B7m,M t!2fN C( '}3. ~-W|"rh=M̔0,X  8L(JHT7A"1 2!2U 2~NFz+h͕" dH"KOw=x(C p}2>41˵PWYZш l,c02r -J+[)d\3WA !:݀[ 2 sS)dL,VQ2]'e}SJ[w>KqJ(Y4F 4r=rh[+J$haDX(XFb]lYdМ?F1NӶʒ$@/Κ1m 2&\BtUxCI }䩪B5 zq[u9DC+_T^U|Z@~=42b7d2Sp ->#>IL)">i' ֿ]%Tx d:FnVFZJRbBBbRrj8GRTVU\r.Q"+@ DFj)2&? G'?p }O,KׇG6:%Ty&4OݵzKIA^8#=-55--C+).ݾs~R?d~!bS\RWF/*(#%.*q@3gL@PSϓqÌeYPS͓:y4̓PBm<161 Jh/>̓@ 7+=5%)!N,D$IZFv DCmxQA?2f1y2x*y2 Hc:P,~{&8(yP [eFyIQ~nvfFZjD"IMK. - P2tT -ruG)c =O9O:J4( OV߲mGOx Jha Y'P5ڪܜLlࢨj2LI`_w'Kg5*CX:7OFQy2ɓ'k6jjae-5L ռ\'=FS}]ue9Q\TT`.Ve +ÌV%PS&O@)-AZ/O&b` ]xuW+(.#,ח% F().*+*.)+660]Bz(/H(xeiʓ'KV-b(\04ʊR -䢡Ivc~jVnTF(# Fq(m1cA4Fy,-]-J2rO }rB6hVZh#g YBM|ʐ+(ǭ֮\h?)K͓A` ]TB}"Di\r,UEIuWhjJj (  ,yU 1a\QXbSe}TP3 t MP,~Ah4؀,zSUaP8RUKieUP<7~Z 561?c}YZ4<3?r+*˵+4~U! }]w;v50V2+(CSN;8z%[GwH,²jF?* .0^AFA_ggW7p₡Z?uS 2 Keyy6;} w:yBip^t "s=r@z(c20Ke{#ЅZgpz˵,f**4A x \RuYPB<k\ePB{IC `7<|1ڕ{JO?'u .W#-/\kjVA)Y(!"g4^d 8@.?.P<`*(܈%]p:GArA mPp,{4r5`.+\c%Y\A}( \ϟk {2qK(ƻ4fΞ\^ut %re1x' ^40RCqႚ|`2O%]f/ۻxFK2J*@dJ `2\`訌32^)$O&]&g9%4\{AO4HC p 0U(ct<%tɜ -{ugO̼%4# U4(64Ņ*#*#_Q5{׽Z,ZQL e(SfBU^54,]C#Sҳs !%Ag68F%g|h0}1AC -Y.W3'w𘤴̜ť54!zhuP" iɐEt4u1ʓ54-Zz&WOȸG ^ x@Eai`00GG\ -.W SGwD7xx* m !UkotОoh;0,:qy'Μ"!|*dGƿ1p71Qm\KW[9o3yOC4hh񃾌 "6D 1ji&ۣG\4މCCpTŘ EL+r -.W.Dm]K!6$/WFA -FUhh,_cbykTFDhg$m _$3.Xro]{4*I4^p& &Ā\곴46Zo -O>ZPthH&sEG{. Op8@#бSw#4ޓh- ILQ/?{B1r$u Mh$D#q 4> LFKr .WǩNXbOPxlBtQ@#L@h,Zz&WOF -> c" oLbFC@c|#D#&) QZY{UhP02\0r ~ 444 ȸtF!D:DK!GLdXc2rEh"Phhs -IL;Y\ZQh".Ldi~N!#!/WB4&8p$,FBwLFIFLݘx㧖VА8Lc@7BC 1WoU, q. LFEcب*74Z N 8Ѹ!+4a`" -1rr塡8Bid܂d 2 ֦ vD'BcI4,!Q4 -CC4DqAbp2ht1|8 ->OUJdd#Pd(=Cc(&+FтsuR!2V/]`%hLXb D#<6iO֡c7!/^}h0qRYㇲ@2|+מ`%hL@h,Z и(42 2^qPOWHƲE D@C@c+4}8hhCh& 0 #a`74 䢡`Փ>Eb4GSg2B0' 6DCNG Iƽ[I2!cLK@Cm&D#D%Fw!2>Eq΍+5  KD& c҈ ".WhxSи4 ,IƎ`'3.Fddɐgh јC( Ld%ɸZSAȰ%cHDFcɐc4nj5D#,&1-Q!4d|mmd4C2nd޿ X1L|Cyh̕b(=s2/g^k->Kd5qhQ8Y\ZQhGFՅG;fW.Y#c&1B@cU }DCyd<&"2`2؏ 2ylm}C 7 Bƛ爌:{wL1gUL18Bid܂d2 A"`+f4 Qd\.GddEyY 2?uTqsqFQc `twwwwwR -H -؁RsZ{ay<;=\_XAFAC^hˍƘȈEdؘi 2v1dЩѐB#93;ШnB㟡h !@F9AFT*&cF 5иF 7AF?XddĆd(1dЯhF+/48""K}- E=sg3dТa?g#Ok*JsϤĆx:ۘk(?׎WB@C物''ڞxEJ 7&GDFGǭ!nF:G0dа1|hh$Xpwh"SLF)&#.<ƌEƖ C`7h %$nLF"Ý"C -Eql46rLQhGh FdQӈx hE ?~JFfRt.C  4,|_zB9ad2Z2DFZ|D5#,c\ؔ3E%5)4> C$;IFAFC5IFL:"C$CpY tk hY:Ph7>~7_hd `2="(@dDy"2T0 m46laaf(Eh<|~+Z/bl2zO5T..L p2Nd1dж4(4}C30wxvDF]eٍ"|\, TN*Fd^ȐAϸјFc%W`x\*F-| o #+9&DOCo^/bby l3.P1rp N8[F |Op' -joa dމs "c&"c2Ct>~B" @CY+0">+K+kZ cd"#6TO70d,N4BcݦmR{=kl}Z; < & -ddD!2TI2"20dж!h IYZ.Ai2Ƴo8 EƚذO' C Ő1= Иd{mO^@ -~pȨ*yĨ`_7{K#mUE=;n$ɘ͐AFFc -a(4Z=kGh 4} &ӇҢ3)q6JGd&cȠmј5{ 4qZ]? +,Ah<4vRhAwbenLFFbTl22dйQ-+RE LH?wFYU]s+_ia ~‹Ը@/g3}MLƎ-B 4oD4m.бz&NaYFk_h2na2C|,uR@(C7 EVa4NhZػGh@D$ @FD- d 2VrAx!0Opb7hh8yǦdT4g b$H2]Lsw2N9Wju˗.Bd$YBc2Bc&Bcɟ֊KDhjYػG%tfShi@_`2!2Zj+gS#]l 4Od >m ǁصq% }Sg3EOI44ppK8h&Ɉ p2U;u M>&aޟ' 0soHtbBF|K8(CdGyؙha2$lXɘd1?ih<4V߼cǕ4l#R^)*m O n { շ 3cB=Mt$ "h̞4j:Ɩ~Iٗs0o 2*n\<jgnd얔gȘH C@c [vʚf.ށig/\^hh=i|}O0%y9Y1aN&zꧏ܇#ȘŐ1 ƢEm&SjV~1ə {`.> -d0s^9z<(JPP R*HZ $@HL(^JAt+zSܵw -/~y_ONF - av(A󆫸@CUC;0".9#A? w|2Z*(YDL:D$J"!# a)Uu]Ó<4>~ރǿa"c&â )qA>n6j(!h\,%{m.Aq)"O_}ɨbg1adHd )8!gliIg2[GhU@1>}%e4>dъɄ` C]M i@J04qt O%鬇0sKk޼ (۝>Ռ҂l|bLXD CEQ*JrL?xhH0w IgP46=/7ɠQ6J -rBd@G4j ਄Tbn1X?bdeA@ ;=)&<垕ʍk0gQ2C*CECKꞋW@xlR:ʬ~ jg _>hzȦSB|ۚȸrؙӧNd ("hhxEZNA adn{7$:!-3]ET@vA16זg&Lj)=)6<19?Q4NYk m}Sw`DlrY1<137ov^mo,N(%%DdܽsK@8&  W10\!4nݹkdaṂWrј]\^띝P1-2BRz2 a -PdHIG@^xm4Aq9exhL=_Ydkscu |?Ga dg '2@?e|1t|OFFrlDm՛edE@bU - W:fn>Aq)"V766V-x,  -a (qh3?(ƹ $eյ-=0|J@gpdbzv -(f>qAf(/P14qt dtTcqiyyyPw[G@:D$?P2 -+@Ca -Ih/dqixJ)pѰqp -$ddSS]bbegb1aޮd@'+#hHAhhh4}#bRZŮd4P !-16"ޒG>'P2>*hT\A &QNR@1P/sC;Q2Lh}7d<[PLh2* D''`}qɸy%EАqܓ<1_X\BPJ D| -6&"&L$JƑ!h\,@?$2#dr -@rIY\ -662XO e<\Ϝc`jeMNK'frdr) - vs25ф%q#*@Cu|5bRqbff&KMJ - ZZ*<2ĸd@@tDC$m}K[GOߠȘTǧ&c1aLȢ8Zm&5 ƸduCfQ1(QGh*{릛Ś{(|SInI&&xDè.J"-Vjpb*,X1sj2)b 4"뛹b[qquOχ\\+$â>L>MSh /';{yպҼL'S3ruzhQޯTkG'z xP2ģ_4=Y@5%pZjae\*2(F%l~N)75R\?8KVz#bxXL3 %C,z#Hl|a JB>ԉ'%C(DF(Ftu-JgBmeө" ~K(/pO`jr('t&dқd">m.[Bh>}bG5r07֓)d#^DA?1gn %CD`H4_K kUBy_@ Jh25̠ƂɁP8]!+H8{K\ !рL ҲP 8Pn %C0h -Ƭ_t^_,>Dž+bhJ`tFM Tf n.n5+b'%C,hO P7eh00fPo !J4=aS՘Ƌb;9n`L11F1(B"uE (&j;sya2I!$]PԘn̂VjpI\ >I]445F& ȡZ1&A{P2DN5ƙ `2h1ndP2DDUCscr -P@-`h#!& jlp7&`LjZp/%QĠ[",^ pclǧ"!*j(n|;8B$hfp5x6L# c@UP;^P 3DK 1̜0  cT5``nP` 1j6 C|zAf UE -$H]hˡ}$@ I^/H p-}w^^q c Ph3D^ *=nhz?K$@"cŠK/jA^ ""JKːːAAAAAAA> 0x endstream endobj 11 0 obj [/ICCBased 25 0 R] endobj 24 0 obj <>stream - endstream endobj 25 0 obj <>stream -HuTKtKKJI,t(݋4K%ҹH4J#Ғ(H -wqyy~3̙g<3Y9El -@ ]!O-@\+BVKK :OX~WCaiHKL0qY `5ck -X]x= 8 XĿ׽>.f#aPn D^{y8  dp H st:Y׬cxc IV?S!:_9[YbQP~+rA -ShHht^ '0߅™kYXY9Yqqpl'WzEE$%D>,^|t*K)%/`\ҫ:&D [7dplDa5|mb4,yy{e5 3⚅,t+whlA   m k -xYUH&%Ȥ -qO'Mz3KT@v[NUnn^\o]abTrtlmE]e~U+jאZ:zaqi5};CS[\_ۆwCaQ1;>L$Lz}4:%8M7l̎Χ/}XT^]X>\Ym[n!ycskkƶʷ;v{pIs0Xݯ3s󝋒&$WWW*)!$$%!e$cHNOAKIMEq ƕ;KLw@YX;ؚ8^+DspfKOTCPpJ%D=++O%$*8IZ\Z^UK_wL"dx]}>9=;s_G8/̹N!Gz[<=2|B}PQzlH0Wc(Een|Pds::5&89yFT"od䳔i/ZK^&gd:fgQl kJХeJ*+篍kj5U[ZUh0|em6]B@`PpH?QM1Msψ*iϛ.Z [JYZ)X-]R޸Ѻپw?@?5 ǖ'vNg -W3gLC#u!MMMEvAms˔FVNA̝GLwA̬,llؿsݛnͽ+!B²" 'R&k?3?4+:6oT\ұڿ6VʝoF?LT;:>::>:;eqvx^sawݥʕ'_EFO\DKLtAnFF)F|ԭ6\`@z?m+F;LwiAhy͖)Mgw~_ @ZH_XA,"F)%/*9aZ:Q,\B^_AU񡒀2 -*'[j o5[uR1uh`fm$1xJgBdrltlyyEe$feg-g#`dGbwj0TOC9; ܨݿxz6zx8IP=A!.aAxۑϊ}bG-ޒēx`G/Ԝq_O?0"۬խЮ˯ǰı²µŶȷ͸ӹۺ 0@RfzƏǦȾ *GcЀџҿ'LsٛFsM6+1MZ:{T?~ò~i~L}~cbA~Dad~ty~W~O>~\/~|~`Cx}%H}1X}%z}K} {N}׋<_~7A~-ψ||Dz|+E|[s|z} ^}wO@}-~ċ {Gu{Dz{]Ĭ{f{Zx|[]|ϕM?}R<}Ǝz]YzHħz|z={LNw{\|=>|v|ېI8z/r z;bz'sMzd6zɬqv{D[{0> |;|yyaIy?yazYvzݮ[{^=c{ФI{R*y߄yfUy`VyyuKzZi{ <{z%zȎ~+~}͇}W0}3}HtЄ}Zk}=~zɇ}!~Єd*s}Y<9wpSwuuVrUW؈|;,뇔{RsѲ;:8q)PCV:4.8Ȅ2񡂡?Up Vu9S c bփR.ՁNn U388A/ͬδz6߆өn1T\e7݀tXT)$̯̕6;eCʷˆ imw3SƀV7M -\lGNػځNāa5tNzlߴS<H6*-N}o2ن N%է>w֣A}⇤\fXMݘ2, KԐ3g°[} -0e6M _1 ? 1ӣǾI^I|B̯dܪwLe1$: rW] 1S{z|diL g0\ U{[G{!{ ޔ`{&yE{xbie{Jr|/c5}~ -~:f#MKx+Ca|uI~.yW ώәߎ%¡唘[w!^T`^H*- 5GȨ瘎=Π4rv_ҍRGf,ދ̋|,ƕ{ Ҙtٕ^1Fő,;',#h%T,Qۥ{[s:9󅼓&^!Փa@!" y -.Jl6mHju,bU6+s hܸd-ʥ}wi-sun=0Ľi-_*)U_ˈb$na+;ϧT;ppA7C4.*Iߥa8Mm.ACi7\j|fiԫ)]ޭjʄU]3(í whJch-4x7h׿*P0H됎L랇ڡuÂ,{Bz}8vggҲd[!XTZZ.vlAg -{;Sm`vؿ`~?ga. -3Ì{L^WYe4]L7ok!wI~Ira^=C#Zh`Wu}p)"z7ff&3$FJ8Ҷ5m -uR_,^VS&aR~PfLL_Dw*`\-9]q  TI6)>u6 D`e͢/xqY%9ʜ;åOd\˾P&eRz;].R<oΡ]P{?: r̨\ʻb Ҥ3|m s؟W9oZt]RnÅ\cW#+nI&gyAjsN06HiD'@J+a5V~cRI̫vwtUc[3+?F|l(iU^+O?Rs1Hqil$Wþh=(RE -1BvџnF/ BsGMY9>ܖ3ȗqI ڣ5V_1ȣβiJiX0WVH[8g_/ -n3 ` 38A.|f|ј0I6bv%& ;Y㿜҄#dD.).p'3J12K[Duɥ$s8IƊ.z^48e!R6}vcMiozo0'=~i,3:?-?oS,9w#ROa; ?pB -֞IO ݟe#}ԯN$\l?], y,>&Рq]yh0AqK)ĝBFҍcH:-h-ǟcf)K9T127]qEjL<>h;|U -dpG -ƫ`&!8al`83>.qɂnA9 -; `HByg KB*k㰗2fF=#OM eT? -mTm_OBۊV<ɆF('n3uG~Ȯ#7Њ9[١`Ns.P..콤 'KnpF\? B>-`NWOOWBlfxW^b-_x&*/(j_=߆󑊢zF`LdE:SNʔ@S 03|TOKokto}bFz$4-,.m'j*J|)J6BP ^3ewܫpX.*,07xPڳ:2XOT21|"7=0ߴy}ĸB)H[Fs V+̯+Y(I(x&9JAI'tXmyG=X[8TK)2<TSRvxlȓGO|g/{>4/gRFȶ&A52 uЯ*B幃AuFǞѧuD)B,*?n` 'qQIzK֗4{B_g68#ʉ2.A$69!̒ub1&D3Qx" >ɏnνxVG&TۨÓ)sxd-5KxߣD&1±jdGjJ|J{Z ޲f6/vTp̄ub PmBU#gBg˷)-*E -ar>>Ƶrn[ɭF-IByѸP=ĶKUC wG D}"vN.p]]Q8uY{#qCv}sax_oyiNr( d8aw2CQ}V8UWO\g \yk@dcZt9$u -p-1z(=f) -vě92 w u煼ת#{P6+Dq3HIi%BCb!kc5&U ):X$܎[b2*@PkcӘdoTB_L1Uwi")=2#pI9,RO>T@>;bnDPuCfk^^\G~ oLRcHqܮ=-8^5Ońy*9:-\g8:T<?*C;[yX+I;lRL߭$DvYTQ6DyVmfy%/sIsmXP1Lռȭvow)QBb_LVwupeėO*|+](uHװ4WU.{ 4\m.QwR~MAiRz+%BKz?'{ k҉aa{H]sX}da~3_auQz VM\ĵv5I0LM)DŽp1:5,&4 %!$}ocޤA]R^xT◬M&/B:DwA24?cd&g]5b4a?iǐ Ĉ.OA 6vfvsd(5yTH/P=(a;zUs bWxDa)Eʼ $sgPJreY3w`cFo0|U[j5k.5J&eTor È´}I lpjC8c5J=g%Uo|L58E" -ِ[Ak]J͆VBM"{NrQihЦ@Y?6^߫ZWٯ]ذc؋hKSLj:>O ɲ.ݰQ{5mm<ٷ?^v"}ъw9O&vX7km[ ,70nΒ7|eP\I;-wgFN cIP#qWI ;NٶA)H~7i thl~~dzY Cx2>*c&mb{9f1X*L #> - V@g蒼]7n249=MK% ;,F\j 1klZi؊ΐ.|Q9а$_.!;̿lE,ɥDi}D3^a`Y5g{J=mɳy3CM'jM-iЦm n5? SJE+U~ ;q.tXd~~p*QeS%.Ћ"ưBsZ6-6[\d;^z4`;64藸ͱw;|+&AfLU3XTm)lF'l VɺgcGObbɜ9;v \CL, >B?KGCe"z -@EHILp<5'҉$>8#gL2m c1 c Fw)P+rkC qp/u8#!*g°Pa`vu@oH`"Ž:z_Q<,D>'ӅWP .`xW3|!6 -5 El[",0 e[Oz0~lUO+&xkPc|u$k.?{Qp""kr6isVa=~@W_ -.<7 -2#h?c~m'rE_xs6aG+K 14L^kUp^^_mS^dШ'>}5$:τ!E[bJx&n t(m;ZsF5uqX.ՂBqKP *l%{ٓ{'f';,TT,bhUq2Z3;}T9vwRR;GD - K*/@hUv$j!@ vyבm,W|-͢ ^ ~D_􆭍"ĉ#c禘*X/Ϝe>|XH;:)d9gƖ4aBQ4Ew,C -ۯBU#>SV$L-5gV ϯ*B#} npþtdU$Db&$^\^&Z"/˺+-}%Z:}9AYu rTlP0"~! ͚*@5K?߫Z-P=j>܈[O?)a5 -?WUsy5^(ge${Cm> "Gգ+$踿ϫ& Xw8?g,'ō="/xNM)'EFqrf CįQ9ZY$r!6m)4 V9kJ$# FьX٥Cp[ģ)CS;rFP#ImKGɺzj>>X9,ZL-jIbkȉ8˚?vtxPIO}_ay@:|Ve6ubd/e3<֭ztea'cLaM -lz&,f^_!?l2x2Xyń3D)\?ye ~4O+9$  -EVDTSؓ7X?MM!ԼuOtP Cbt;iްa@gW#@4c9.Do z2>M5i~u0 qswQ9ǸLt삟Mz)>kɝI;io"U)]$YL >$$T:gUo$UK,C`sCMAJMÄKC(g]ٮ9sUG0?L5QM%0Ol5&`Ƒ1,x'{k+mY}-Js#\d:i/NK\8HstQ#-ND).s*Zymnf\1l{(E=VGW9s:?wǟQZsC6A1ƃ6K@8OUY^`7j6@9?,yt4&}"T- -\Y&kVx녣391ٵqQ=beMq\`/nņ|2͌JkzDmͫIR4\~5NlօKɁZ]TC3l̅D3jSS)tWw$IX[wV -WTUw^PeUhWE^ؓ~Wchs sIg`wgs (5mr] B`7JfAaA3ƓG?{O[ ?xj/Z*7exXz Ά})C?`KcMՌ&)Y5J]q':]$؞]Yv x(ıH1eU>_0b?*񸨎b¤،D;Wxm]|N7U13*;.=>SÜj)CM>.eI1/QvН6Tkk+Ɯn\\FFV#Xde&~WE7"bju^I@j@bQ Wk8w_D ^z xZKA _`T}] -x}ЁM0S,rV+ KO&ƈ`;E{irf0F] w86f fm_8c3V<)r1p +hs|p!QP'Ղʛ2rӤej4Y r, r?4! Uq]f(*&umM+;1 --c8CjL=L1TDJ7>)BH*cHY}~xI,{7WjWާʇhg_YovMKiN> QRǧ}AQj^G syJG"?txt,L>֍p_>Po$^<%}KDS4 -*S<ܖyd;éIJ~JMn>ȸcI6uɖژ䩊i77_5W2' 9t^}/8%wd0k)ͦF9kih3ShPBULzs'0$Y/L3ol|f ɪ\AW#siS-O^I+36xas @M -A hm45V-' ѵ1S+ ~*%~k˝ʉl * -lك=3_2~OgPs -Ccd[aے{<ХjA {! ߲ۓ;O'9+wEHE&JV?fiӺ j05瀶bhWZxo=ƺ 0zhK5mov (YOut;e=R*yMVn,$v:QڳE.yVl;svn,Wi.[@34SD_!MF>J柣ND @$Y~-CMu (+lBpБ^#$~2è /@̣6 3nh -;۪.3Fq3\َvZnZ"/vNFNJ2V{#ΚVse_쑮Ta8C¢!Η>FL\M{5eH~7;F AB?VY=۩Q i9J.sӿc%FVbdեiL`a)kD=W \ne>NX7Ƒ†2IYf-to7/~Uas[`W*v3_`~:kjR("E -* -e)DDIss,f_n6":hmh+]AqñQqSa9{~8|~bh6GZĠםN\h+(E30~kTMGβ1:zka'LG2>,gt X&@?e% -=@Ihs)HUOeX^m7R7~,, \jJԌfͬ8!*]JR:WR]Mɚ PZ;JN.8ɦ,[r*Α]MM"waX)Lbjd`>:?|:?u>^G$fa. -ʥ_S%ED8 J=ĕK{6r zGG Ui<Kg"^ q -I6vPWy^,uc/5@:ǹ+[N+li{P#^yv,ñ-NѳH⺣<֡gxV</nb6󴳜Ρ +nhB˾PoT(W##ĉTwZU} w-vT-9O᭺HIz) z9R'dI5aZGS˟agW=.P1ٜ y?2X)r4VaGXBe`9Q1͚@85$W?D}z2* -pt +;Br\ܕ'> -vCNeʔL-ʌqKHr 7I d<BgNelB^փRγF2AqCR&t7߄{" D9u)Cw1t}?"'[7o̩~1{>Ru* ʖdClutqf2[l~{S4>J$.nQnlP#x])By`r+wLH?VD:|iUG~ժ+&+Rb gP>}WԹkQǖ]WSkqwZ -DQdVd24KGMvU35KJ~4&jwJ*y;X߉˔O@5hw)񘴕o-9E:_̂o&6#V(ѽS-te$ פp}4%4mrnzhe4KX*KÃ29ʩ~'Ǥl|O5ÍB -;^j㛑Q`exH;J\*`l˴Khk -&tF|(8VǡܷR:ϳoG*UjSKknRgl ޅ-6&Nŗ7O4rGmO[du_TvY{ ̏Iy\aRKy&P7ݪJ)l"W5{K S_j0WSW;wixF1^lО伴^'1b%OAXhq)L7j}=9PX=n`ɗKX#CùA *7{ jWܴTByufכd=Af]F=_u*`q+_i݋\^`BaE|S&%Z a8+QgQ[IK-jIKr2Tcju=A ʧQ"7{ٮם*X|,Yzѽ}ƈf:jCo[>]x^hlhNrϳEDkcCǪ ת9c Ht<)}z!hE~DBӳ2S͆i{;ouIp??砃46ٺ^"1R<-65sjpCSjqi6dzھİ紈 41.$5EG9:=ob쾄 v#[xﯦAF+T(C@RQF772I$^a$Eq>.AEbiO0]ТK5ΫPÛG ZdJ*$d ^}E*֤>?Ƅ$dO _tl%$^7[KSECqz"$]*B]}W zT[Rk"n]EUYvFUW\B6-RB^Me2B4/wͺh4Ek5˖<1U[tD>Q!.kR涧7uJc>c -l/i^3;iڐ0sĀZnS -qW7Np:([568ViAFޜ~h9Pldüj2dO -+61--1Ewv =JCHW34܏&x8,&#Rc3Dvz6RSyu_N/nmكvT֥Y˼?RFװKzn9Q4gC^5l`P\ܲG&ޫ` 9PҞٲXr6 -V4,{a؄\tcY`]lǿԾar鴯؏=b!&Yb ^[\aYt$w -[R)i[{$7f"o Xp -zBz'hO|Ō4ǐ|-j -:}̴a%Tv5Y9QK d0 ?$ćH|#uD3 phrd@,@XmVKY@ou([8#!OM~.7SoJn%OG" -Ü3N|/'O-R_1Vh&׺ NPz8de 勊ZTH;XQ6}+'h_|ȋCcuHjBA,NOS{3 L`]1> A rxӴ*E^.ؐ`Q5 v{`=W6뼟\9avGOXc& v1w~0W:ʎ~f: 0/˵%m KRKAcR% P#CSߥfmD5oEx17B0<&Yd8"1wܡ5 TaaJ3p57A>+yIMcu Zd?Bk1x-rsV9sH6p]DGgO| y5S$aE`$Ls -[Ym ~u8p`6*I ߕ`S88sn9O3nXOE /7f^lbN[PBFO.9Z_.5>F S̉R'}ΪѬ`_dX|{dHXԾ3QlZe7PRqشO5OkZrx5u`aǂ:*`T), -DPQʮdߓJRk=H+ -*#u)h) )B6s9߹瞏HZGzGT"93hDͺ sr|b4y $TK "$I~$v(B#].qi?CN ~ޱ|ܷLcOnT~vxj̦5<.f\K<2p:CpSy,66>|zC -E -T)f/:X1}J+>_~Q;^ㆪvs&۸>.k7yZS:˩㜍rݖۜaKa!l.g57Kv0!;ڗfe %]"XT J3aժlwVj=v姠αe=bI/gH& :g,(y 27>aba88fVVqɌT0NɉB`( _"fo! t}Wg_0}HX 9,Qx=~Jٹx>ӱe9M2mFS)Vk-eZFF٥btg0O?Dǐ%7eyښ6WSCyeUS}l`a8i g"1лJ"|PKڝc,$+&PvꖴGBoj_t4I vqf熚(eC!b׼^SbYi1¨;2W`/7uh?4 -!z@#(T 6 ^!R S#>E/Sq9z_ /G%ӈ0C9[ۼ@(٩P ,}XTOkpQȫUG6 x2e,> -?ϭQެYz/T5FL^`tީ3\#̬D:,vw[mDW)TBZ`0Ֆ`3tBQ˟kks41y `\޸cV#z`XHhwA0چFTyqӵܫ*F˪%*/>9 -gS'"b'zL=N)cs*bR)W<#S 癛)K -&L\9WtW!Y17i*%wJ_ 閥nWJ!p-0T`:K6B+SzlL,~J#ZLHBEe߈Eq1 -ڸTD}bB;*OTCnՍl$OYQ0mz7o9NŻ|hDV[Ve֩b7YZÖHl~I)ܻJ5oOݑ%(,hZGҼmRd!/NEWutV57z;jjs^^lDǾ0-a_aL؁w44簍b^ppi&nX uƻ-݂ -cY4_g ?jGIfH %J҂[%ϩC6OzvWzoZtA$?z;ؼFT2/+0@@S<@>0bSuqw;j4S'/4sEթ(P[V^5ƊHkg/ۄw 0*֭ ajyB5TC J(_F4!m, RN ?S9 :״OfOV"յڇ1,V)S@._ -#Q`K|ͨ%cj/&\: [Ft^Z"q٤Jm뙊jMarח`VCg -w"~>< 8i}XT8dzQVY<p%HG/Û`rq;Nm~Ms\/Zh:(MXа^F.꜋.Ys}5`a((X0T+JS 4&~|iB!! !)$)ʰ WFY]E븎3x,˽}|dc -|i-0Ws -Q_GpRjy0׿tjT̎ԍD1څڍ›N:ka? 7ek_%]a;זF=9-b= &Mm0-vD'^j+/5(er^+EL F1$1KWE|fOFMKm::1`ڥfXЩM*i9 -l?+Lw?-Nx͈wɳ\C0瑃f sM;iđ`$O0z*RٹB9@"k5v~.lB?ug]ed8JAj͹um.DO^^v:y;ske+,L¶vŝҼخd_5Z;q#k> MU\J{l*͟ґ3Doy"UDcu#H)BPit/ v`_Sʝ{e5mpPpy=-2[m+v6*.WۿSǔ] -^DMk,2.#ɲ\!{^I4Ԉ.~çlDcBU\b"c jvJG|H`_2rHѥ tHHBaG :Bf{'9 -[jaЧe -&hz6Fdy?>gۑx&l$^:^nx-'-]O 5@S Uڏy]Tu _,zWPT|BJ,ɕ}`8ߴy?p7gˢu\JO(_vOUue4+Qbi?A.jCxyRJ駥Pt㸲rTfdd$ֺFR>PaL'v2M*׵T]`W*cD*hAe#"ɆKO9JKL2J( KgK3jԉfZnL5oM(_>FOӹGi}<@w#Ndhoo4Y ̾Fٸ2YAz$W֜5Copli\ 32l;a<;S?B>zprjsm1tZc̥{s/J{c*#3ހfϡneh->Bc9SJ"չO8'8ހ `yHϤu-*` x[c')Oy\x!QS9q*;$;d'=NY ,|ܶ34qT=ka%hs䬺UX7Fl[ o1apuxf9QGk4;e -˸7荇5xB:yZdͫ,`2?_a[0~9iY Fs3g Ë9u<,yx87 1Ja,O@/gO㔛94 |.]16'^@1'p:XtwL,jVQv@wl{έ̱\?R^UV\GI+9D03oyd[R<""" -.2}"!<4tH~(-r25DH@l"K濣,/S}"+~wF}V dRz,:w&?C~FqJ}JݢJirjzEgU#p]ZF%+[PjewVjlW7wR/*C%%jGx @EFH)&0_Օ|Xu -DRNXA\0JSH307͛73 CWc+U#r# aQOL4Eљ?s~{sIy?y>ҒLָKd-ޣJ1v*fH 6hz+~BO:IQqZUՍP[UD#BM >$ z|?^!J0W8N WzXfщ@'h< -%sdR۔e[$z,Z2H5[&Ht L UO 췯+52j&P6uRɮ! -a+rk!o4 `ܗP)f%VQTF(Z]s,TR|O)O?ho# ]6yл)OU,F٠E})gsٴGyҘp/kw~˖I'Y;TdgYU'I8@F* 8 $I+A2((+y8OϋWȗE {բbW"@}@C׌teYgvֈHofE`eagbN_4!/e%O;mhtWv6[iyFy4ʔat V] au #QYm3rM/q{~tjD 7fiɷ  . =[n`4qShBrx_5wԐ %nQ~x'G[ `+qb]Q2Ըi=UGn~ڋJ(Aݪd E7Kz +M]!} jnh-Cզ_魺a٭Dfrj6$-4nUZF)Zpux'@]U/ٳۿ3Ug`iU}ڰULWu+SU[;uXJPvOŀ{$KF,qQruH.}imfZh~atMBb0*iWC䶧jZmn[nKfi c+.&oV.&ʭ{5_s9dmIA. *s5: 1Ů m!|fl'6#N -Z>\oMkCZ8)*bEE@(27{I" $!0a=+vUZŁ`-xEJUǺ -~~7TSsV6i1=2J眆Jh@ Uu;7!0 -߽\醮%-;=.e/T7D$v{.ʫ|ZѮmcDֲ+-Cu_{>1H1]"D^nR ٺ:E3[h9 7TJOW+3 vœLimc @6'[c`Ǧ8v!bR{1_ӵuoPE2\@;4"mO m{ ߺE1dA}C=WB}[3']\PJG5VmnYG Xyahd'J[U~ vWۅWo]WnGnR9H7ѨAu 1vZm]lUrTVA -sj6lhm,My4A*0vJR? Ĵ>2C!*#q0MJ!:ŏCR|dFa?2݂ch3dBzSIt?%LmF[AxYGҏ0m;GY1űh%[sጒ@9 q_8G>r Wn)jodEzC.qJviN&If8bg - v|sd%:uTf&L0~p.(RU -; _)w%$/ t# -~#u`u[w.qsY_-*'̳ɩk/)2* i9$7fUzflc9}],툏WYCIkS-ty7>T! 26Kݲ m&cӣh' ..+upC6&@j5tdP0=I˂Ė -C{޶$tR:(ϭuOR4$=jluq1?פ9Si|cqF!_z^SK}`d%DT wV>;<'V=(5H%jWMV#9YD2֓p~~J }D]gNSsjJmn->,vg&SLl#>^i8ʞ%4'RJDhRN0hBA0(r0K+aMY|"EGE_R^v4/?m[˨yN`K/5[71[Gؒ' '铯RGhqꭁ]>iIX -5'\GB ćd^ux+[^%e ֪pxE - 6%!Itި@Ҿ#% :*h$r7שׁ55׈Ց'I+6*ЮwȰ%U#zD+Jt BaUؕ 6}uOr7dP Cu}FEua7RV"KST20 EN{^lkƕ$vW(,F7b ˢÞOy<"_).kh[n 9W?gڈ7yș*ӼuA@ OpIRrP($e[iVYR -n#(aFq&mq3%\g?%ӆM5XD3b$ʁW ƿ5&͔D4®KcᏊ . -1Zo -^`~¿`6z q aXǰ)Ӽ܄'84 n"Db.yC<K d},{*h -ڸh>wMv^ c8Iƻ(~j? -eoyl/Dl5Żרpy1ܣܵ^004{ .%CA22dWuQ>okL<5.ſȠiffh7S-|^TjX[wCY*sG^1Ve֗+˃L3 /2y{+.;CtJ } ->٫y6q< WxA_PZ? Q y1>yK\.!OqM -0Cl];Sk)=RZ@[ɷ5JBeǐ$Ni"0 -úR4H~9.☫|Dϸah-)r~"eoMK%4 _7"‘e -QD~0T.>"x*O>酧.Ey+HVy55RWsEk*PxEGB;(J X(8hiqmh^ 0`}_APWDLZ‹]<4zG֦`oyZR|u^gCF#nr)Va5ƪw9njyIt -xI1bIy>}-AگOShKFx6xqqQ -3SU\ka椚̩Di~ ?{>J3mtߐZt]YNju]ɒQYlZZsNѴѷW>Sݥ0Bj+7q҄fU7m :8^;#eտ+*,_CY3MSU*LX.jQȖg_IWJ5a"9R'C\y׳qH)VU-Z.\+Ѥ/aen/|F[?SPkr" -^Y>VH9 &yaIxQfd}+] -U.o.=q-y][viRgk*`/pLBu+A@[)&PYQ?im/K,Y*gu(i2`؀V"fJSs=RU@7+>dْsmY)w=U?ο3D qjv83׽} 1r@vy:{Eͩԡ.޸,珈~CH{ksv_l毁@"lOR."0Fl]]C˧Mfi nq˶Q{56ef e l[IuY_(i&;to 5kZ/ jjp~Ch⨿䦿iRs!G-֠5 -&wa7WAƫXUr8+}E)oVӃIÌ}qZlh<gw -A?=$6-ޡ|,)!<*ǘ*z!8߀ϸuPpD|Ŝe=sm4'ҢؽYaPOZ(vj?VGgxI=V-̹uMCJH_-C]B~2A\8*E8PTΔTo 9/whaߣby\'F,Ռo%wU/ժnM*T Ƌ{5NJԢT9L;y _fXD\uַA:x")V%V/*]1# )ԋ@X"SVӅ4u.f?Uչk%Nj;c~?]Pۺ˄WҌ=V듍1 -E ֻqd{q׉; -NYHdfttc #&vPtQjd1o ­R)ʽ@}<7 &8wyybH04͂@>o` ~M`Oi#T2"-!NSn\ z$SC%Q%;OzcT)!M.wf.Po1U=Bl1F#F0HD\u̞rڜ*ujQO5u8E$7:"І(UuANgulWYE*Z"cT\kTxlx)$8(YBIY`[}.Bb T$=U8Oŧ yP-x$]0_ -j(sOH|/=wKR` ptl>f*ӡuU<=Ts(&zpKA?sLo`N0Mq+~*m-~F7^5惬H]${|-Ҷ9Y&=X'Vu+^ϖEm -Y/0X cAdPc_X VRx6b|C6^FeC]o-F?f7Q3V>͝yFsy]ݯMF͊k^NնI#FZ.7ƆQfeϫCJn;AjB JFw -mԗ6t(I5beElXQ͌ i,)6QS 1zJezVBf ۹ʹ/ HQ89SnE%o-4NJ``,)~utyQN]vحp+e"xN6y*,7$'x\CQL[8.d@}CɏE)1D?@晹b$?7 - YM N| _Td'wa}0Z<9|3閗3~o=Y>l0Wb=P1jmE XR[louv:.C=;.a.BřS[nWJ3ǟN1='\Xr8۲:KXj6e g΀ap%z"K1.c1ɇzɭGTRiVBe-)K@iͬ!u@_`&2q up%P -SЧ|NWP !o-t_ nyV|ؤ賐e`HʏE=>\Tǀ|cҎkIST!%Gu,%[IR'+#T}m3\/df)`n2#\M(CQd6flqGv첵).Z&wITe{JQܕQE\m`p`Ҵ\z[v7OVo9ݜQ}$SSFMWdnyuя: *o[3 O FRJ0ոl+L+&oE+d- -@?^fEkoo\fyJ8zΰXmi  -Nw}OYpz&@>gݪHc. ]7Mz#fe"g\a@\qyºJc\3ܔ r'WQVE D|PLs\h_h#9Z-TdL>˼!WS/bniA3.1Fx@Ǡ3UNN^nPOZdtvWO&-8ךshveSȉ`wPU_cař=շ}m`<<$+UV66do88{ηzkG}ڻ<<7\jvg!5M!w&GmpfSgO3x? -wZsLRq/~lK]QV:om<Q' R]AMXyu ^ȩ $}! 9LHaH8hʡrTtD-*fY]]wuu[bgg޼ߛ"ȹ I7HR7HBHudt *Ჲ=eJtj| #TI/W?{ΝO^'`v'$^E=7ITF2˵7-^'Z"[x ;[U7,QyWrr9E6cy'I gIRm2ZQ -{0K,^H/>>G@l`T=FZnZH ѳ$m¯鵩KA3D;w7ŏw^J<`i$M_x8wU-,/h!pbP1|*k _U;N45jX_:]$ %ͫX+é Miwzz{7`fOE5FohX}fL}k%Jq_b_A54WK'h?:lTHmm. m&"X7rV7l̨b]r+ OpK[{0EuwrfӵFajCCPktMݻVw[FR(Y-VE8 P?)p>͛5 #TtF%3 qhk ;`LVOpZۓ. j&\Cʡ <*g!r)J;ȁ&xK0N\B&Գ$bԍ7fpt(0H23ӲG1d?ź -bVֆ|\[w+tjj?b7hwJCmm#b.^VBDRb8E]4J 7LGc.Xd/a&ڎ @顢zQuֈ4Tqi˽èb˕ 43~,ymoθ[0 -l} TCuLBt 2ZW>Eh@+[Řy0= -sU"r];û](̏{e E=ma^2'FKv~.Оm0Oj(esߺ Pk*!3IBЦs4{^|{6k\* }XYǠD=A %$hǹWǂORV UBꯪr+Ca6 Kԣe :Zڿu6&?W&k).]%],lb7MX][H"}WL)RIrfr?AƁY&I~_IB${XlZXE&|w#؆`_vߢfu3fm89?9 -̟NՎ`jz1*.@爎܋`oْJ_+-4α6@/DWEjE}HRDl;Y+ z/1Dѓ(z)oι&;.4aZ#gsbZ+XWi;<~n"( M'b6!G lP<^\nM8--aG+dyXP^s:0q \p3bWu.,R&rm#қs)lej(^ ,=/FV6fj;ex%Dk%!FW@ao2QTvs 5h0B{UHiGCOzL'pbIq+'_1Lv -QA%$[H~}{1fKٲ:HmWS -ëd}2w7 j< O7i2G;SWݒ!@YsZ~*PƐ6xQܡ/9i7cGHVf3R>K2jZxH"Z")vHD} @} YJ64T(P_(*C]miSJqOZgA(ny8}wν37;?߇*x"D6HaeZ -5K e -tE=H\ƒW8 72ym]Ly 1N<8͍@:> >6pӹ$.7$C$pA)hJewT*FmKg-lm*{{v\ܲsJa>3_*ݑہ>V5|WG_>RR_YL!RFjz S5fځO2< `}I\:XiZkRH*4[(xX$u|I9̺TkVzl_׼gC%*wXR nY)N.9+wZ[E9ľWJ%wp`Nj[.b|JOsdW,R~#* ĽyFdwCp*L(8OelL˞)A vfFʹ.Knd~A򥾺]Di(i]YʯJߟ?>w[侾7KK6w"!eDp5V* 3VEa{:KoEDcɾJ#oOU44lTjFk,>{S?ýSk>Su=|j}T -SU.nk.mcŮ)RxbT<TV*yÙ<+`RC;S^0-itp<ȗ2IZ_0ȡVVKHWol9=fd jb%}DCy{sI*{ZL1r`n}+D_*Uz3}i779_kjxL+u ;FxL.mmQ`sKzK#>&ޗxiBV^\s3_XX_رC+ҭj|S kϽ|j|[X -ΆBL.?\DCqߢ7nO(M&JOiݖw0IJLM,NCOYPoQRSTUVX Y#Z:[Q\f]x^_`abcdfgh#i3jBkRl^mgnqozpqrstuvwxyz{|}~ˀɁǂф{pdXL@3& ֜ȝ|jWE3 תū}kYG6$ڷȸ~kYG5"ŵƣǑ~lYD.оѧҐyaI1ڲۘ}bG,{W3qHvU3sIa)\ Z, -     !"#$%&'()*+,-./0123456789:;~<|=|>|?}@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`acdeefghijklmnopqrstuvwxyz{|z}o~dXMA5)ۈʉq`N=, -ٖɗmZH6%ؤʥwog`ZTOLIFEDEFHJNRW]cjr{ĄŊƐǖȝɥʭ˶̿*7DQ^kyކߔ ,8CNYcjnoldVD/h 2 -R e r xzzzyuph^RE7)4=@?:4 ,!#"#$$%&'()*+,-./|0p1d2Y3M4A566+7!89 ::;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{||}v~oiaZQH>5+! ؎͏Ðxpjc^YURPOOPRUY_fnx̰߱ 8Ql»!Ceª9^ɂʦ2TtҔӲ6Lat݇ޘߧoX\[VL=*b/fMq T - p_L7! }tfUA, !"#$%z&d'N(9)%**+,-./01y2g3U4D526"7889:;<=>?@}AoBbCUDIE~% ہ‚rW; ϊ}bG-ޒēx`G/Ԝq_O?0"۬խЮ˯ǰı²µŶȷ͸ӹۺ 0@RfzƏǦȾ *GcЀџҿ'LsٛFsM6+1MZ:{OX͙~ʹ~y~eL~j~Qc=9~|4~cl@~]̳~nf~C~لOiZ/gP8v}6q}0}>ϲ:}i^},~ ׉_LpK-~~,*~&E()D9vyowy=TS3wI!D)J%OBvwN64;>FVWm -S^Di*bPkpة?%"1#!ϼK`L<n-e2*+) X䥂C@v2l Q?(=0q MzǃIz7MEY; Y@K (-\U&>rI^2IMe;Ya"VN,S;o_%sD;fƎ.R?l ;0Dq>8zDKG)3o+&<4@n͗0EO94#ҐnW9 b_7}B2yːv/ąJH삻Ȧp$ȫވy;Æǘfo虔F¨LsI,KhW2!AjHE^τ _wdlXggΩr!jU)[%B\DCfp <_\?k,.wȲirJRݐ=>0+cvZ{HllLVAc۠ ^{6oCҏSمbȏ:sz 7jP@Q;[wg|z30Uq`!P-~|X3+z2lIђ:_p-FOJ*Yr(".O'qäfrCRJ'dc~h!€?`}WzBd;hѲGϲmT SAij9< -ߨ%@`8xLTqė=,Mk $hJdx_r̰gʱhtG,KytomVK0X?R=Џ ]ٛa`sʠ7g&Grŀ?>r&z`b>&z%sxbw&{~څ]"WR%c"zD zA rs!֝=jcf]rmANJl$ے#ؑ >wTfGFF699<׵.'SZ*˺#-Jl.ZZx%m*| o 2ӝ_TWK4eRsu33'jRFBWl| -Fgml0L1, y+Hu2f;[T0BE{:qntoT]okI, - LgV_R:Kϋ0dP?= vE̷փ(M4m\Tk׉o,H=Zw/EI-LQ[ 8F/g֖'$?[u~fghXjݚ- VImKՀ,%ibQ*e97WKMYiHtXTBUDw-49#iԗ/r]hGވ/ - -lD2 h‘%TTT*Fdw">GY?"[f r5ʊ4`TAo4H5rWS8Xy;$Yr'q vUPV&4m/5LJE:S7Hvy.. kPXAl` -,e: E$@BKr.!{A$A,CY[EA;| TJkU>41aƜdcT.Us R&BchR) - Pd;ʟHbl?1;_:i^mMh9Ӝ+,x+(‡j3=P6u>a}&b (0=.À<2&m%u9_~zL!S`(6͟>թVlW䨸m5ypg!2< PR%wC>ubvbF.0UK$K;؂P,!rA5%\v" -[2gwdxJ:_'Eښ_+^Cژ I! v,V72UJLNITUKɎIy/R+=+(֨v6!M @PB%R--3|4-)#ͯ w.ܘ<;b#;*>$eG ->3"و~AZ$xOUx f𜓜x;٥Q h X(Zx=`dš 8b†id, ϐ!enZ -b /޲І2P0~ +1baktT ?g)˧9 С`.ޓ`>'4\DRdPaxԗ?i|9,t Ĵq]"m-9OD'Ex>#Bz6Nk%tm6BDzVQGq,2O: y{iHcy[]vaZT5 ȨR 345N@qG!fYXr{3^M7HX1ey87ҙ;NP9tn/D=}*I:2s̋%G{7abTBm6ۺ4JZmI׶Fהz\FD*rEyք ̣V-8ˉi#7XmZLW:2 -$Iⷱd`U+z3 8"}Y\E^\Qܵ)<&uZ!FM)V"ڟ}&à/ ď 5 O546PW눤0 fGlEbdc 'ƪrӬ[{K("M/y%0=zFBx}{w6{Y50%,40R}ԓvTp>K@fR$7HU( /10f<,1BS>٨RI3#&&pa5j19#yTH9cI[էjU̟~? +7NzM`k|-kqJ}(Ҙ2SaӼGi ; b:`uǤayU}T 2Ftm̔%OpuDU0m~L-_:qWg0~huw-] NVrP =<]x;Y1iw@8,n\(zqb !$zB&5dn61Q& & CuЎy#c%$7]w'z\0Lk{8 ;fGS Fx¬P~Km%t3MccM(bCB$ _ J,@՜ %ӸZ;.6B)PT~~:_tHNITScΤ5_3bO6-[o 7$cn:zNqnE2~7\NT' "[fTT^2F&+c5r~ԕ(jl 48mWDC]X#<n_ T 45 C0 V~ m&AGA7w@w;Q8Q ?d9#1yʕq_eS]y|d*&6Q30J(WG>HN vAg+[o:y1ډGmUV'pJ{"M@3X|*oƙޞ%sfJ<ߔ[-0R'G i++qNPF\&XT~ykPx>–~u2LX'P MOW rة Z?qU\+w>-q}y/sRQQJ@737Ka[t̷E8X,Tp!PVK$`Κ׵bu~*LlBz-f{i8DbMp/ŲF_<`w[Uq. Y!'i7L' Rz$v]c-ީ%HY~ٕ 鞀ws{)Wa˹ԑ`{[z ϡZ& z -- U@uBP.8jz B{GtϤ1ޕq# ^o2N*`DZm錞c@QY@Oy`ŕ^ )H??s %J@f-H%{#}řPKn@u5w:=YX9(5#p 9#Av(~-"]Qb'䠡ya -'£ +vO@%7_*Z-r*~z Ց4!wBpG-q.a+c"wmqk=WfB +k^0>npu5㞃= m]0o-1:ǒ~%ui;pVO/a3;0oKܼL6Ed@ZU%{ ^ ͰyOVNHLmu?uMBEQ1\IُOui@L7Nk\dd[i|lRܰ3"rW^  -19~(VZQjsfb5~Nl, $LAE \Yv3k"*Ie.gj4uDk"*T~~g^ ~<|1cPx7kF84K(/AI\%HG;'6`kK -ZJAFqKq$5GT#.a;1 p't.t-SSUn;QY(sў*M8= -BHZ# GcDS{d',Utl=,}*vcr+](_1rØ@?A[KDlv'”o>=ԏ[?Q ôn!ܘeoiB]u3PzP'ߧ%44Qw L7@?;gSVjgohop7syR\7V%xL| 3n|2Q|-GotuV֘Gk}fd'̐yQ/;^+b#&~ي2(ɚpTֆ)$Dru:5zj,|~0T\~>*,6Y -]7E9!7;au*8Y?Ң#WfiA~\mB\$OwDhE16:_JqBR%*X3 !O:`Iok2+}Y'1%Y GPMJ{rK w_ L&N NyA'ճmﺾo4gz"v;L je %Ɯ{NS6U'*@djNcvo^=Bi 795l€Aⶫ627ICkyV_}B.I=YR2U^c~o\Ƙa3Ƹ2@eU*Tlmcӱ~ xnNU)o`Iχa]PFŚVTC&ϣ࿋Y=d]/..FBXs+$=}buM>RWm6Ŗ6ᢐFX 5x{v*j;zv<_~AVUJϐ^IjQxシuQo=lK_ՑEkZ\4sqU7vOa J?Q)4C^\k[{3y~M|J'g4Ay,$0( jHl:Q"V҉1X&e s)MZ(W |Ϲ\88&tcpҔa͔ CC GU$^fb|8u̸&A֍9ke7;㥦koAvՏ0o5y'M3q"y$[Y@SgÓ=ݎP1)L \!B;U!)/C$N$A³ueuU},3Y'/Jc .8_[ON-<"NawGm_+yj~P]ſ^\y X,r-|㒒ܳ<L^T},^eDR,nkqց%|r,!gJx=~p{"\eeEN;Þ=${q@Q_\?/иLe>u#Mp'Yn_e<q㼅Ra8pLB=(YK[l`BKB#4;c;HS^OA>Ʉx\+0lkOԼ`Fcfup.wlCnKJIi]&fXPAn1کFTKBoI!ӮZ f)~Xhy9 ݨOC5&|T2ӲnSLB5eD0:yP;(w9mΪnWhKu{`wk -kH>*ڲ1 wp5Q݌$;LvvJ1f3n*Tg@oO#9|}?V0M5.ۀz{" NK?C_$ P&B̆e>(qIu`|ob|_0l2WꂝsCܴLTIa?f(/+PIwB WhgšH EiŮ(G6 -"  -"(H2̙dfr $xZEP>ţC~EF:}< \{ -% rH6N$(߫Nᷘ_%1]2:$o-8ȥ I-qt;'kTjJW^}kfQUr\ulNkHn᫂H*Wd6M2 *{`V%VRoJJ`+"yO|s86Vy8 :+;9ɨ=.qqѝ=ɥ^ӏwldG;fH^2`zBȳ ŞO*{M2MoR0i:T~%$9ED~cj<}${.-+P]c=Vzpwz\S;!?C:GFIױqYŞ ݇>;]mS)yrEz_n˕aI"l|sGvmߵ_7e]֭>ГU)i:D΂G}V W5*{f? -($p\)9D$ZYr|(4D܁OHʳ ;ܫv۱jxLr_r ;Wi nV|Rudܦ;@YNl-QnJȲc/14C:'K&̕BOJ{ߴzfsW|F-q2 ?}Y[pXdY<\v+M{ir8~LJޯ vlL: ?@o[g`}>?UrǛI2Lk.}GpI8QRV%܂L0/PUE ?ɹTcۼfHs^QMC!)$ ; ej uIy W6#LMi9ĦͱP*HʘFg]mߝn+|X$Z6K'OQJq m(B~ljSuZ ťbhWP"z@UVJ΂\,<\HA 5Oaf΍C75O Uݮx7F>QL~:ʥ#][eTS2%c Æ~EWg9i%3W4ފ:}޼0_X|-ƣµVu8H{YF"qĔ-F95E!L/3zLw@"FRmOQ&[#ZO/xˤr~9T00bܬ 4Pߋb>_nMFY%MOaN$ʡ˖~ &($~>tBM%^i3ϐEf8UB '`-icIaͨ+ دR=ZȾŁ=5U#5HR>njky/s6H؃E oLyCG/?QE%FvMMz)=ZB.ϡƋ/•3O85&YKլ(ST eҝZVx'xaV4Ë*H]z~h~ i0d,K8CZy{jCF')b|xNJ>V{0e#|SE1b狛*_R"37Boξ(p3_<ݥ%-tɫBetƓpx HuRuɵ)H?mf@Iz͂qrgM_D|Ce -ӯ_wCՄYK/Ԩ 佨/Y0y̸7.]*ѳa !d[m9#{-;W[ U$mb?ci3ؘsq6ĂT t֠} dlv{Fyt/ټt̰KQ8 N"4ʻc'׸Ns6I ][#?wsb,4U_ f)Eď* uä6Go76ɵ{'CGa+RUA=@5_rgs1OUG*ʚO&Q͡4%nlc=%Z vY Zeਝ4? eC` _wvĦ10KB/*Brv4όwM 0r `$CܝGa6;g-N_&ɰ.` `0M/s\PMf`p3 $A7 i c(y jӍ 5!UiMSD-rBFL&^:OF-T4w T3c q]2Rd/3U\;?Up=@b -TYRJ3O)*+sWu.[L6ǼA. 귒hoN_=C|HW Gz}w\2h{?Ur_ס,[<4DmD〷C/Fl Mr_򑹾g"P\TMIiDw$=` IӐ }6.jYx^h}]"]l -8"ӽ΃ǐL"Hڝk:^֖Tm.^@1~qxTlU#U75:LE|4&W25exz*̖̆;M0do^lpmaIS7kD#'͊$"lL?bADINmEh 8Ԍ*"vұE݌5Z5 `z~x[MN&a|b(ǁ$ch |cq)M_Ɔw>bSО$  Dpz!G@o3a]PnN2);K4 U"p+q 7bLay$04iCc9(6>E3a{ R䏡0`?s07y9'`Lq`ScLr&MP.ڽ,_ru/F=܏=1ltŜ 9>1lם -KX_t+ =#ثL -uuWK̹ u)F@jR_$YuBśGbQl+$,o8qlg!) n2QήU>Ytw(^'Y! %GU9, &>YcwU Mj"Zo6VWF9=al mynqA/2AI̐i -qAN?!9NxlbO{eiYQ̶>SZ .&sbj?1_ǡPkٟx`дY!n6fVJ?ffon06l)7BuyMAѢ&m>>Nj#4J%&|E]ۊ:i2g0io*6zXh +҂3;1"2ҍ+O?KjaY|nMHpA/LsI5cu*ΐDx!W {|mpq%qehrYbBt M7uA- -w%5,x+ z!Ί}|%wpȩxeXx|Yy$M}yAz5{+=}5"6~{άq~p^Q~Md~*XŸ~,LU~S@~5 ~+f2T"P{pUIpf P[AE;Z1ٓ0U)Fj"0΂op~7f ![BPY_EE;T\1撠C)k"djpmfr=[M,1P\ǑES;`Ћ1')}"Ρmfni=pkqr^mtolVurX wtDyw'0|Yz>̾jqźjlr`ntpu0rnvgkbtgwWIv~yCtxz0b{x|bh|~j|l|^n|~pp|j\s}AVtu[}Bw}0z~l;fׇ i -9kDmh5})oviNqꂿUtXBEv=/yVǧeP{qgi卞|l{nohLp(TsuSAv@Z/ryX_dִ2f}}hƖMk/zmtLgdojT3rxAKuI/8xσ[c&5e[}gܞrQj.xylfoDSr d@u/x\ębp vdܫg%iwy3kyenbSq@to.wUad`RfWh-xkkemn)Rq\@?t@.wZtf4uhvjxxm0xyosekz.qR|{itP?|w-~zK'rp{sqԜu#svFtgwwtudxw*Qz%x?E{zb-}|Xpzr'zssj{@(t{vxv|cwy|Qy -}>z}-R|~H(oYpq݃^s=uPt;bvSPPx <>Ay-|0m{opzrt?s^auQOw+T=y>,{¹luSmoou{psGrlatqOvk?=txj,{ @k mܖnlprxqؔM`WsNuȌ=&x,zj׫4lgmomqq0_s*9N uI_|2so|u]}@vLO}xT;"~z-*|Ly(x*yyr z$y gWTaˢĮkTd@D\dPPp-HG&]30;sCg( 1DE*n6ܵaz*&>P3ĸg| ,X񦁓`S$>BG DǕu#i#܌-`xJ!wم:(`[HWeQ2UFD`|:Cd2~TvkdEeUb2̽p ʠ~[@QdF!7H$ #dLt!BOK*G-iCrB.UlmO> ,B2W<+367ߛ@ )۠&KO 0ޏO igm82=D 4FB[!AIb4~Z *fz\OtF&ӝN&3xF[Hjz&3n14bM zB! |+ -/hw{V\lsTjg?қ۟u 깮D}û.5ʺ(wM ұ=Ljeo(u\ yPXƢ8p2232"uh0 ;(3-ybݷ3WdsF@w ,8#!H*9)iF^ -P7Dg3I33D_)JQNdOm2ta':=J.۱ -s`d+uu- ǵiȵ\L -kw/i&G1|91:H^gW@-Eif?QF?/KvřMkz݈uN0:ӎ3BJ]PU@׊VVzDPC9>RTl{=EY^ScyjN96b~mwj[ Zl'd}[YގM:tU9WI-#d=sѣS IKuƷ6i/JO{s{c@6oPU,'9cV~M6IQ1WwoT+mlF0\Od?oi4M4MC%HfM[r0p[p|R’/Ld/_c8]׍ YpFKM(Ewo@jjI0/kad[H>|/ѓL |00SVRׂV2Cæav4x,'L82'7&n&CĿf]9-f]i{Ta4EeNٟή"V_ǔ3tf65ҷ, jP6Ex)ͻUSu@6M6dFVSˬGŦwƠuy@>.TȆVOdj?#驺sycA)w,zl<ـB*7ij,\P#;}}~r4fxO"ZhNMBe@(78,iA#FaN}qǖ*lf Zۋ M2HB-7߅,yY#p9|qeےNYƐ*M}"A튘6؈U,ۅ#||(qW,esY!MANJje6Ç,}#5tPcjOf=_`rhTkHm=op2s(Hv "zbtu5k#jl_-$nnSjpDHrB=tytn2ݑOv)yL |triIs ^ٟtSuSHt#v=_.x02y)B{! -}~st(o.w]^`cCcHlVf+;t)i0aldOȯ>tsw[-wnw\-_AMb0ke#SsShA!a7kO|o>#r -v0[Dn^aaShdL%rg{`j哟On-=rfv-vm3Zp]­ `܄cr f؝C` jRNnb=q-vBw~o`^q&ccrfBti quk_wInN1yq<{u8,-}pymjynlpptnRrp/qsr_;utMwv2>@?nC)HKс#Eu$%`^>[ -(?`~^x0_+OËv&"YD>s5x']~-if~>NF" P^OG# ǖ0<7ӆ7 :sXL!kݱrx{6Rt"+@q*7k1U誘Y}(~\H`J䞂\ -52[{F;Onݦ *C{2Hpuw0D(MHOB$vKѻX{'V' 5c - -sh]T4I DGãTD(2BNlz9eB_ ݫ.#JUbGɰ Pc36߅!3?o/˼ 4Ta1l-vKWZApɾ<>\Щހka8Z5$GdW#{{ߢ! e8l&Vlu4ʚ@ԸQWJ"쎛)9(6gf y'1?JL)b쭢l]4LkۘPpuﲹ)nCA Ŷ+2dEH'Hm&Y3uѷkѽӭ1n]_Z<ڮRvӛpjm9G݂#j}dA-uڠ -0\C"dhK>مٸ:IFq\BVhF'$[I&3BtK\ D'`;I ["%#N\I -|?a8+ş3"-Aש_ZZKO%u6`X{cͯw1 $+OM{'E],jz6+~ Qk a=_/E qbVk&S7fg\"&]KOÑ: %ijeB>%j:l=T1e~/ߪg I0^YV)<^ϑ% -զՏQS-WGpaθD8ߠ9D֑ՃXM' -UJ]I"mteuuE)-3`Ҍ SoO6Ju@$ZZǚ;oam>݄92)@m{>-V|WU>r$Ӳ]qّ¸zEYuɔ>GT@蚩\'}њG9mp.d.@L4c&,r;b ӂdlt3ݦ]Q<b-w Nk k bK%H@ j"W4sf|Aa{8c%J@bW\E':Ehsř=}9fǹTW !3ߔ% פԘ]YzĀ&XIkWdPيb]9gbIi $ O1wu_)xS$P)m/UI .mpsf5Uwl}oyh 4;=DUIKSDSjj:?2*w0P4o+G4O6jeu HW)ϛ=ݮȆs51 okaIӽ֒Wo0%>#}?V5N_r}%7 -Լ{!`D}K_4 -!Q\HҽzȔHN>uA-^Ჰbg%+k58W #wi+q0khcuTT[`5Z[`J &-v**cs0:-7o3G(Z!d  z Q}vx'E}aQ#*'viƷ|'in˵Y;eR{E1vikYT24o/;K |O c -Rr_T'UtKyγzaL= zs#k)|OĀ܇:axim&&^cŽoIѓ` -W82K/ױϬ˽^ipuO:JD:WtG<8YJ] - ՄyiZP-|xm4rQe`dZH ;4SX1̚`wpu>7 H2%Cd>zES?+&e{\Q>+) ^T9ZPFV+@l@ A B -r3L2$$x *,^-ڷ[]<**RInpdk ŻΫ :C>KXi<_TTՖqcs.JmZEŒ:^΄hsVIbm8tSX&^ a*Ɋn^m=A2s^mICca|k`K{"Y١:nf,ڱW x_n~ -!f睥# Aɧo(u -gįVg攷E)?n/ؠbdSu3QQIB`\C!d -P,2QC[Pһn`RXYU^',|Y5G4-},V{:T5zGFdx|4Zٲ u'ʦ"Ww[f^'0Xcx2rKJJDJmB|CÁ=55oc/hNL9'0jI. =$!_3s^>pX0]ScԹ`gi9Q?+,O|ekkC)6bf!),MjQZF_Y[-ۈfiv&mH!`5oIxudP#F -P&h_2nnmMsC?wOt[Pk+jnA ǐHځY*zל`L﵋TL01|w:44o(%j̨5YJ_|fyl00DO+/.5T"$8[g)T`MH?Ɠ\fިÕyL/\Zj@Ν(Wڢud>P"Yd'$$ʗVJ+W>pG[^Gڻ2|M 5kci{ZJbILFPCR7<]'wKÍQXb* -$f»~ ^̈́:)]}pA(+RXzE;b1t!9ݠBj` d> !L7gh%7nׅ _Qg1R2Ǽĸ:@n\KX)'WIC0hݤ!XL}4l5 Vh2,?bLb#(sÀytk]:ibP_"2S&F ߆*:/~5l6fݻ Ӡv(l1u;8qi7mL[@Wxlg Y<#nMDyYZOEX;/C<_IfGuROM++c7S -4ƊaZԃu Mߊ]>]o/m^&=Nh̕.g*>d_$ -]koj-]wz`g`@XRSZ^6uV^og~XQ 濮a%{s Tp4{HLydW)YU&R?FD/'gH7yOG -S0᪄g :po)-.XF:e*diG{.㯙nwn.tY<"`7dsSC!x$g:SX9Y%r_']4K . q cYv.㏢Mrm*ADbW냊M1Dqby9mT'buq7Or }yXK8`微.;~1K}wҭrB;ҏޒ &6 Rr*?j䆑lugICkM|vhZYHn8VzQ3N??֫zGP5|No(RGJ[5&Hs)qq}^&2n:zǰkFmP03;7Nsi+ZiӍ ^zs7Tm , zb@p22{96ʄ/= 4)c x -t&83B-(;^SedSy7yG^H@Es7<AQ|h[\jeZҎy1|i-M']|k!3h{&m5&[KiK%}UEk̀u hT[*FkkOZ e ev]G ؼ;GLW[d;oo3xY{OEk[@|l2섐^򒼗F6a 9uUQ[Em'*uWAw:^WfAw:Rc$DZ9-N7~c - -?;A34VfO 5*DvUe_Rqr_pMv]{қ[;f4( c5ڑGdxEjO-n -| g8 KٶŲ]{r3J(?ұqlu;S7qWA}ǰ=o -nxg|GCTpTaH͗O0U`llڤClt0jh~pڱY_,x',IUjn\[M zDBb<Ô]T7S0Co}2%sF͘MQ ś!7fSѕ&.!mFk(+O Oȏ@ W1fG 0JZ-#=qb>@@gIxFz|޴\E=Yg6atҺ*SY5T9vh  %2{}n}I90v zRf8kOʼjVo:*xH3_ 6WWx4\;5juK::i7rʶYAd~X:J1<;e -(;MsrlڪU[y5vw(k --OlHWeG㐣݆L9sŠFp6i&xИp0C2}TxmCH#ѽZyڇm{+EAaWdVSy%ې8bש"SLL14$Bs&Bj&d@Y?O+82}-D^ݒD(PR{Ѭ.s!$4Pڣo\i(#u"D8 -:]C>6ڒ׶*m@1GQm lìOrusg# tk-ۤ^G) yۂ2b+PgDWB;T+4Qv{9輵;!f6~/ė|@r~EM$,<`2+oMҿ$ȵk뤆)<$\nnu|LX+z-]:r"Xꗺ.KW;–YFC :Aǔ+IU u+U>.+͋;SN@] LUXKx6 ͑8=*U4^qݗۥ>S韒+Ż eLsf v?m!'粈Yv0zْ2GwT1e{BHM, &fr(y)% P Ehl% -$EVDĶt o \~6-s//E 2<뤪t :mbpVn(Q7:ziZNl*3miИ` snX -U\Пbi0^Kc=!!{pwpyKH&Ș/UDg#M@1&yf_sIrŔ\ Bc7HexXltbu!hI -&) ֩ršbps;Cu GFq~~c6RbO'l"<͖z [T0}5y V|EWrф\2aAA0 /ɷW&aA -AK]מ q\kPU"Jѻ?W{j#'rG^$U)~VHDTup7eÊ⚊R"I^w0^+mOXiMi-T5ȝ'N]~{e r5Ճ-wA-VYF~UgBOJt8y0.{KO(vlJ uS0փyk^?6Wc+ -Cl]Eko%ݼ脦g}h0[[tVۃw,U^|}X?4:a<X s%هU)<@ZQ/[6 . 0A=fxIҗQl3\PBoJ]Դ\>[3?,ЛMOyIOi> '|2kxo6oy*Zo9XYifNP?1k𾠣 *_BupֲB[ 4Xφ}P73d"dٮ&<ăT>x4Y"GXF%Ngt2S 8.hpq܏#~2HleҢ(j =~n$ Y9PKC‰/q䢘&lrS1|8+ۺp5q Z(QӸAX!\$$$CsrL2$L%,*OQuOłBuUX뵊]xV~n,[|nC --bY@X?(e92"կ)fm6@>_|Xȼ L N+VJ2v&ǂga:y*=>C,꽅zqwΣaVbP$Ԇ3H* -|tc^7CvfCUʆN\A X)MȊQrK{Fۏe"j%hCi24.$ҲɹDӮ?2]HMtaPZ+C9J*_r%QNH4r{W) |em}^e ٻ -.v_.e'T)V4(FoUgzf0=rƣ[(hGjKҢy}%]ʟ%(y쭬0L1sR1w^NJO7 نyoxõO`i0)¿6T@JJL#״C[!)9!w+@,&TQ0GU5a -5\1(-9]s41y3yʍ/ G䇫~IĴ41_35g%@.1N§ N̡Pi'74@rz8Z? i;f -cENOri@Du{A6.ѱ>1_:, Jf?/LCNN*E]٭!mq=p)ݍ -cFMH?b;t% 7r~L&3>ﰞ~6slD'9?6T­ϙ^ 5; -k[}gX0^hq$WKJm3qV/f̔&|}31sO[9"6ε6 9K+|dj8a&kɐ=9wUͩ?|0,lugzeU,}* e-^uGSoy77bC#Qşn[,( l^ 6!ʌ>":jbiq2$V1\$ǕwkGԣQ%[`ѐJ Ή `]+Y)u!*5(HIdaoElw17hYxЈrMyA39ScLYgBل*dlQ P/Džml)IR`i?ĞAY訌:et/ ysn琸M>dSG&HPe*p:vFӫ}9|%*CdڌTm ؍θSVkq~VQ< f -CB'LH? 6ǍZWzjxA|+cshi#a43 KZr?'H:m2AĽ eЭdcM^k^Cj#,@DL2I~tHGǫJ̀e W`_qZb -"pp߄CH I&d2L)xʪ*jXEtJJ]EZ_=@XY#>(UT#tgE UO4E]cDix`Ffw0b(U -Y]sAvjfhw@A,bx#iu+E_Xx˼U-EW'_@ce2b1( h^EN -`V[@-kbn_Pe:60lu-'\j|Dme;tHGD˪&աD!ߪ@M?B=rΕtSwo2Y!;DLž]򮆁˶Rf;˷-r0ۏ첸R}"?5#mk+3((.RxP{K$ ~?uX m(U$C[KIl9vL"F]C2q.OI61Qx 1iQZxle_)O&uZCj7$6} A~8zXmb|n^i>]fQBchJDj^ k]rou#Ih -8ЂTc1)üW+-*kxueI~PE:LR] &t-¬^*$M4-bB c鎳A9ZuKDۄT}pp;dzx0w - 7 ? rlJU/3BK3hf@jm1RזD*p֓2O(Vv -ndmMAO;1S`M-a6)N˛,_ -l[c.Hі%Ŗش+#]lcٶ$ s~&b~In^Y6-쪸ʟ/FRa` Ei|o$Գh:)=kZv6g|V'E;R^t\"ZW -YnN'⢒LiK[!6bjnf$=+ *.ӃKvIchP*%zډ,1-pGsD8DC7x&X8e!j5kL4Y &XqYLA)$]s_g^.[fx́{sHq  o݌ KFaa)1$PoגיDO̐Ńwq?0$װޮxYZN8$8 _ُ$`lcZ6ݐ?ȇY+0H5zቔkQ}Ö!~QQ2&P{BcH|7gz9^sylu^A ;RckU>)vQ 8:oVcsK68#7>^nNk_<w*>mڹ3"ΨŢl` D#ޣ7W-#hD:G"DxA4 >X( 6b-X>*'qkxOOX+{5| fP|~NEzEy?|S-2<3}=`[~#ltGPj_ _߷,cn$kaM=UlMQ"gɆ 5iЉ5M%7R%qvLSG[]]M vKsw>q| -7pL=#.[CjϨ^wUOlTvCe]j20uuFfձʪ:AƆ"E*S'_ -!Z:Qpt47rv윽Ys9{<Fr׃d+G1 F~ /bm1&&x, ^ LtZnDz4g?x7o߽06m3fB|=ksΛ 4|K5~Xp%&(*,.0<664^?|X@`PsB#b$ PX<1A͹O3l.O IrOS#?UBP' -BPT;} *~>22 -EOL_~[ g ,v,cy]zFl(}FVύPq㫪J6A$*H$Ρ`v0;f×9zL2ٞQC|QM5xzAR+Ԕ k*xGjsH%Ť^Vaݼr~Lȡ3h5$؋#2'$ -,FP].V!foDc&2`* _'ǹ{# ݰw%{2>aQ*X SV*5r1V/\2dL9x~dE ]0 -^z[AKmILŤSK``;m\ojc{.]w{]}A][UT5䄚T9"#֑$-QJ֙ -(R;7n^윆a:VVTST@e& -PkLlvw6ԷU8{`>5#8-Eʦhc5Ij ɱUx(EUu=XU=ux}{tjG -4a(=Gr(nËqZTivU肝 F7 :&|ؾĮȬ8CLNlG\nt{Bvx~T2?]ъ?:B': nAS+w."nG%PBRBz^MLpz&*T@ mHh؇Dc΢&ZT_Wj 5yI5LOї5m - һE/`v0;fˡp;ϙ־A}UlK8SQC#kדtYFUVErAF̾!b7E|{e wY쓌E8T@V4U4<7IIiA(R@: j:8vug*tE@EQ*r 럄B; !rIC@V@]_ӇQ5UW/)aY/-Ry%F2"  InK/i"tY{p8d|Q\Đxi'6ĩ/UUi5gԧyebLY(ke&\1q(h-Ev;wΛ6 !5kC(xH@m՝N&וy UFeaf5n\+#$,۾.wAڐ&T%_}ؗY6"s 9G&j ơR9aWLt~-m ANv$&! 2p0t{z$?5Z uTj]Ġ`9t& f,h؈!%gS$&T<6ncK /'z&bp`F*8b(@H3x!}': yo8IP&\P{C@Rt(ɓʌ*rH1𵐗&dx'McČ`$f>m|S~䃱ؕ$x0mq]Pe& i#eF6AWB~8QChiTɞ <|]z[u*nz!bg9Ԓr3lq Xr3" >4SPh=m@A8 {Ͼ+\Ǖ--F3a@4M6;ҩ'Z8JԐpjj6 DzQ0'չ=;Qv(X N#0-z#}2Ң>ƾ#Ahw8Vw5C/[r:mU5fYH7H)N6S PX'>}<5ӽe~y'NNdtOݗdjM Z̓x3YAdECM&-ڀjG ož>ْm\-u ZTS#%xG;Ѣ8]0^`#Hƺb~ںnA-9*ViTR8 -`'yM>aATm#GђZVZ˪ݐETD_l }mϒdo8zPc)VdjGT *:YϪ z*MSqKP}W7K۫Ov*om;Czzqt}JeVl|eryItV2j)kb腳h ?|lIlN^mzQr}\E+ݫl([Xp1ٔZ[m@_Xi䮠pvfy?q)?GZ3=@W =T2lvsdrڰP챢ށzE     q5YTp -yOCŻReb &l[Ghmb9M%>]8!p~{gkl’B42?ȩVnI6 -e%2G-8o QP6ncN/J/FQ&= }-9>#, +>nƙ,Π z,>3'ЏԍI6Mo$GWdosfܐT:jGyhKڻ)k[Leٓ#ceA>Vl oiEǪ2p˪lMe.{J~IT"Cvnc53}-"ÐhI'ِ,kHM"D[YjsUZCM:fD˂+)U -Naa␽Zfk@ 0,"IBLtrAlĐ  N9Vr:#Q1ha x!coDjԀE_dLqi&]8NLSNIS/)WKlƜ5==\[jTv]٨@(WKsm!fwO)iiLڤ?鑓#tɕOL=?ٯ9,o9̳t2UAP@C6-!d!@ BB6BĂQDkop94Mre9*ӍRMd0W:rB5*G1GRBd; ib"P'dh8^`B5yϕJ\ L΄*nW2b߭L)3t*E&' sdr* i@s?/=:Vh,~ߗ;{u15k}6EnA;xobhS$u,N%ɕ8j 'q/qO=`S)г ,Tרs=@o5-z$^˚Fk3(lUA?5(!4v(_uw1ff:w-}hXKvzqAOQ NϜ@:&z$B/ $Gc*8?z0;ߗ]/ZZV#sY]X&qzlKNCd P¶GFޜ=;èj!,z5ϥ+D`C^n"NJf90 2?}ɉ=yΝi*mJnL6M$_e A ($eEU Ȁӏ^9,>IoGs}YEHBWh֯յYTwL3rS1MOeS-)*d`[hh%؝jӣ͓\$|[XRK@-_JoЌ+כŋ8V"]?/&{d_$]B?,kʯ2xF5xun#s -[oyDs?{how1,8 fL?CVAyE% -K.?)-amU [5[ڜȺMtM0o?s}*Ϝ|-.̩ {JZVu (lIneC6%FQnj̍;\M{w 564q@p${{bKXQVx &\^fA{O򒻭m.B0b @ħ/d?4m/o -y0wA6kloz=vVtbd.RC{,DŽ4]@Г zӁ4#L#y,xK|}]XÿC>A𵲇i6pD1|܎,HψP(@c ii@Rq2[eaU^FR6Jz!` {v' fQm)0}^(6Rc$5 (r~P,y9wM:(^։gDHDϡyl"0A4t!5F5bl ”#@ -)ۚ+Ou`;\ mqׂZ4++'8bqu2ǬN Gt$ F7 G,)O '6bgSo/+WuQ.mlc`rj($oQM -0rIF?i#@I_S>8Z7gW-[ܫ J?&[1Ck\B"mф;[ - 7qD -$fØt;Sj͖%qzfg,;-^Q`-}"ҘGHv- 35Sl.J7oÉ@ 5pNgmwٱٙmu*ꊸ/#7H NH  @HB\$77!PxE.ov[O8bD>Π)Q6AY-aWjLGU-oF7k1Fj@3\=ۉ <'#Gޙ?uߎo qxeP IÉh1nzY=Wu Mզgԥ'(e]-gCGi.];^ɹ>~o[?) oOP^M!=aǠtRl69m^rU4\ O%%-,O]TB*s;?Mw+Pmv{ւC)#HܥO)ih\LC.!K'b1 HQs.w{ϟ/2Tp c6#s6"bI)i+˰exVz:;9 sYAnSKG?vOW{$a R*ը1o7l ˯WC^kh+qf7 :B|J+*u}B2#PCѦˋS%e*:g cCh܁li) -`Fm5{kï 5!>s^sUXt9UJ厓7YΆ-P7 $*gz0W]yl`\:XA>s97<5'&cE=ffӕDdyix M8ZH6."4Fm Iz9)d1 ź F+)mju@a7gDfFiUcԝRڊXxi>6|XG/@@+$kaQbќ0/nMҋ]%:c!רZTxY jq4Fּ]Xyw?=5a'v:u]㌵u=,"@n9 $$!+E@AHGBBpEA."(hA P뷙ӗ}Їw -oPEiԑ9qͩ[ q)Q<\Uh.gY}WS(35QEJYj)zS h/Pk<^~'?aS| A :8}F/R+|cha - 4Y^HjZU7 -[C1 ?w<}Aw{_Kyē]Pmp\+ؐ- TźˠRVYĐ[tX;-i(i7[9GPq4zg6@0=4kֈ\c-MANTij *A+7V |ZQ4fmld/ 5@ -ݽ#]w̋Usri07mN wˌ|!WQRQIc fWlerU:Gg&{ q? -n. |f0rg$u͚B869A$Vˊ:bVoi L,EUJ@!Og)Л@v4>4=A[+g $fy4"nv,9r1gJc:5J-AYL -:J匞Y*ϗȭy5Zg!W6@@6,GDOMBӆF`+٘^-+*uj/iuUcnC9K)7hsz 5]Nٰ;Td~>TJ4& *ow} u?zXcΑggS+~P2u.3MV&*1Z,_e%I#\iPpYRg/PphmsY}~'kGs4Tj`ޅX~>3en؈24"y 'ʸq~tZh/5kofصOa8s߸F_$@3q˰>'n9;7^^^=1.5?jD'_X,D,Qn?t/J\p &w!ב0؋gTStZ*j| D„=bCB3WYx{ot}5[,w$ 4LBA#oaQQ\xąʈ}IHNK ȇߠ Ke's}*_};v$p;$p\,1~ ?$ - - ! -9~|?}SRwp^@YH{VDrqQ"Ş'VpoTU$VdDױJtzt -*BM"{i1a=~oضR[ Q!q/eUV.yVH[(`IʪYL 1KWiE2c9rg0]DgQ])ܚd]ѯWiMU}:o@:vN?ćѱ@Fq?.[cT(y1oM70œh~8Jh.#lQDҭWF[3j;E#@O<~.;YKhk&qtd=rT}J+zPUX}Ψ9gTz<#8:<1)y/%O$yevUm:>Cn^!R$,@P18Qr .eFҺs&o|<#AD1@q47剜_NJ5yvAT8a@Â*2 -hc^3~13JEi颸r!:Aj$U^NMrs!&xt~8ۀ>4@sWѴm)9PV-kQŸiP8SYFR4c4Kl] IC4<Q zás!{2 ЅfNxfKH~JμΟuF^4܊prfJ@г:6BRBd -Am-[[ꍏm@Ch[kd+>~r`vS!CkBD+Y]d=a&JD;Dlw؛7c_so` - y툈z6tk4 6֗7Z *-Kآ&%ת#qfB׆cʡ2 GMTC?.X [ZH5:Wt6譥dUEFIҬŋ(ZǗkxZ,z0= >=P~?Y9=1y~4tV$aix%A!jLsLdEԶrV!tZQ<s`i ,{߸?xQ#/Ne`%zyx+UnGz)xVY'iNCV`k"|FyT&`y'_z>#n/F\Lz2Cs/)Tb%Ӌ\8yU B+|Ȫ/: {7Ӟ޸ho;A[,8N(V'O7* xUzjޝ;Wd(aCV%l`PPyp<}捑^gՕBkQG5wa…g7pkŭYlhd˿L^b/IİK(9w} ۿy7S[Zh=(L0~l.}-ZYn@."@P -gSDFd{W5d˸:n8 \o3K>^=ݻ_%%4$&8 j%| A -oմĶ^Ƿî:fԌ& 6-LzH| b?ӑu[}U -^^_b6QYU82Tݘi-434o'iͩZRn -ZoH͟sӹ?}W>ߪm7 -b#1en ?#s"*aQ{u5k ixtJK} -LjH -0}0:[gAM vtv3tљvZuծ]uC;rCDD @ !`BBHHBr;\BZPXnŋu ؇}f~/76ذQ @Bbh\Yuun^R! lQwLs6H-M{#RpRʒKʓ7k׌MrM'?gİkS!" q8@& xw3KsޖG!禼:􊑟 %X~H<齾vmWkaİu~AD -(Dh>F,AC~I)o|J"&xŭԤǮ03bgF}PM}3-z[6|ǓoK@C' 룐A PtD`#c{xʢHjl80bÀ!s'<jc/q/Ӄ@ | -8- QMxFeU>iHR|/1{.K<['-<+AIgPW7 K g - N H]iD/X"IYEMo( -g]Ytd_6]8|pR~ =)L}Uz{@ yf4HsRA:VPRX[CYqDu*ܹr. Y%3XlsZ~=*UN^i\U^,t{gP5y - AEr(ӣAeQq>IY`<<)`?5Y^2]b+0gnϪn]T_\Vc/=˚%>x[@A#I=,-B- g Vm<Ǿ_%߭PfZewJ-۸?{5# %SryUC ݠ>Ф'XʂRlFyCrsTI0%ŭҐǞ݌!Wi KFMvWZfC?]>jqF-VTyl?d^6b#Sl0bYKO̹4KftDuE5spx!DGSvWLv|j'mmcUZգ_E&Ѕmc~0 ֑ܙyWk:nv}þv sv$4y4A֏K磻2nuJUaDG222qwQ؃RpaWPgM/ uLnmXivu:3_0%yN䍡I/ɴQ:8nj %bP,|Tv@^@q;$8ΐBOGhOtP___r:!͆i`=li_(x1ra q#Ь$ $v@mdx8$ F{8 -;("a)^STS 7 -Ә>ɟAdL bc!3쨠bUom`kRS2i@1ȏlr>>^@=͚#K+ڴW+lc4`}_81CQ~u6hxF 0l? y;H !?)|$Y"3?iV徊H!fLSI̝Itx#{vMH!!M@0cr?H+e.%fNMcH͐/dLk V-I9wȫ_G 7^P6P%Ȩea-\`XL)jYFX| ך3"紒jro/&ꀣmjv;!NzA1 -1+d)VasYV.o*X0N?'Tg<'TZs{ZI=yw)=?S4О\ p|*N{?(ы -Q#eMeXqiJѳRSFz9XFRwOMnUzwOqKqOVgKx}E5qcu(:ʢ2 R^P)R @JHC"BE0 A\ gnև}99? ^!HyYz@-F*#1KcH9}b_Rh2/s/gf 97y7 HPa 0WRX3aA *v=A)%(j*5ybf?7 +@\MH@2 P7]APeB<*#q -r|h%x\N/bz|VViè- -5(n@ -^$k -$ub -wkd߁zf0]1>F)\d7KheRUr:[Dx%2Q5I%euaYI+tJ^%(G-il \~NSyU0.FyaM𔋵dCPq d&؜L,QdJ)BJ)dB֋$SC wNyߧ6Ʈ6/> -qJhMIlm"Y+q &WQ%+ŕm -Tbs@@ӞEoܭ-~b0䤶2'rą >UepKyBBc^3XVVIqUz1 >7O;AtzB;~ICțF-LZ,8GK(^4#J]cz9@YA}O_\;nzGPLh%%lƲ.I*\Y(ؼX%mK$ik ^-!Bs@i -?lu?ov9цwD%HS2{31| -n)c!5*!/Q)Hj&I A |sPsp3F>M/Gl|tĺκ>mw3ȭUNӑ98żbt,Bw2IjVs:L&9Z&9&^ MaݕɤvOeq'Ey+_hbh'GDzCȺB(kAzE*f5Ό0"4ӌ)ftPnjXo]+o?سB쨅手e36M$Po(u -v02`Ry=0^G/z*TN k㷩a#3 -sr%ۿ -Ve ˴?si1ߓAԇaqIw3SY*v5(Y51讆to40xQ9rl|Wӆus^Y~mKw|NQ^#Bqsғi1s̈9Zn0/GϷ`{|{cn[:6-2vk-oVZm-FC q4Fcqƴ(c j&Rߕ}L{#}9,Wϼ3 , S!VCfi}ؼþMGNK?z8O.{—`bc?[BD/b>bSPo93){J<#}Yw:W@F4 WAZY۾[hΪ8,v -]#xA7̀}@a zZ`C? O-"ܖ#>65ڷ;2"{+vM%\ -ypI^vq2_gQMg9=ǥ=Gg>(*(Ȏ;Hd%| ,심@EERVOU0l*wo{_;Ci zCg н|_H)Om;ݠ0ʃ]ʬ_Y4("65p`63q' ܭc~3!>G P~؎wr+ ..:rN@uᎅEc *lظ -zHMQ xzAԾDkW pN8t8@`s$@fka;PYln "b HQƺoc.᮳cً9 ܹ11?` v뀍5}wG!Bj/YD}鈿S -+5wqY.棇xcy/q14o(v7kHx AAn8x|A -e=1ı.${5pנq -&+0ȋ9 55l eԄJtJ{UK?Mj>"k>G>EOsE7ڙ+2k1`0)쉑KxP{ -]D#؄t -J2:xՙ&V"_8Cj71RuӲ -6YPsMҹ>jY,BOz;[Rd:MRhg75V]={__Зsbc kAENBv?k|?0j78H89PE --aoPoꤜYB#k 5*a\pP&k, -E|>O<3KbXC㟡m+y~oߛ`b<&Uȥ\59颦lY€VɋTg*uũ 6cdJ3Ft@6cv`^GKq;}^] -h;c;H N]/eS  VUfRe $7eMZYWF0W-3|@oΗ l1a ؜um%]V;B=vB\pW-%\gKERSy*ʐU(E_0}&79 @͟ -S߮\tncuO:>hp{+!Z#9RM2Ǫ* KH)T*mN6M2յ4\DgB9_2?B p%MumwuL@#pBA^ ST::8iQimlY"YY9}^Pd9(R6 D)LI3 %8)|'r2$E9)yW ro?(}Sӑ) ֩ COǥ]%c7M5Y,iY!iFy-_RM-ϻR?{9,Rl|RRF$5tYqE7 )ɏ<ޑ)  Y4PSF5;/xWg-^f72.ԊU!AyW2*R/}8Bfzc%9gʥAgjĥ:NwJCrgECzu6Wzsmsw~a5eJmN qȈԪkRbWH:&*_V/+w_rDgfIkU[4Pe1vGO}MO@ٛK_omϕY' YwFHNM?x=G_sb:Uݔɬyɮ|ɭRAb/+զtU|J -WmR}mNW)6'|cDŽ6%ňw3\Heܩ%w_J{1 GV(d2*uTnVyxիE5.vmyN5ҏ.b< >oDrZc}[-U$rD$j {.TB2/^#.SjПS3gi{ݒ>'Oqb_B]\~gݑ&ft{w t\ ꨎltz9)z68D WoZ?u#ꇗT ,iCzҏNF<,iQL?ЛO`S,W}ueyUL+vS;3$~S' j#*eߩ]o^T,7Y+O;'=#e4@ӑ/rdbO,B&xȏYhuX#wvݗ -C3깢L!rL:{NFN&&%ST˴}P<4Mt -/fVwWkS%*4ҩǡ; Ra:6p`F~ 0cFnuF##G! -E$Ks@9]0D Te8v,`X` N70I>~ r>ę["fȱ2E>ރwf6uw r3W)˕ 0b -WS $x9[LkpXBA{c7$;C#@!MO/ X/AbAh)c52 -E0"Z+l xj=ir$5w« /Urc3\嬃hD1w!av%8?)b|Jؠs~S6$ o=OQ3MAdpm:f2ɷ@Hq$KˡS YeLT~Sz7I}t _(Âh#t! NuM5exuH،x1bCp = Ȣ{v)Ki5)Zޤw=@0A}N7PF,`Ȅݾr<`&OlX+m$9CiFg#Zd= ̠W5o*oQ+~(F{.0F0Lw$sD% lggEw:v/@2ڿ.bϰ=l.R-:{RUp#V$BB Y$9Y$0Baod(PW+^!,E^y>9/yw}qzP!qO( CT=gd W o#oŸ_F M"#Q/IѯȷP(7b5. 0w~B~`9PXT?9; @X\V?, !tǻ4̡Y%ԴjH#uz:~CCoX}:No\{5MU?ͯO+r3nwfB` 9HY}LpuD(09ZMF5M.t+y&A ?,'L2򤨈2% `uM%;Ěsy~QC| %'bzjb72zjRXMI\I-)'Kb mB\@ḨOH8Ww~rCsk 3s63Q64r6[!¶K&~˙F"D]?L 49.5%Y =7pH`1],Y1W|rTMOweC/0m|L"H -Qo\JhKٍU}_6HϵIӹ{n OO?|{e/ʏU{Pu''L٠KT2^fq OhgK ^\RQ?& -lLjwxѬw݂{"YMв֞\;Tw}˄ nʦD֤ctB5YN7)S92 C'NEEC,PGI1YR PJ[rY¹}'}K5Uv Y/Ηg1c|I'SCR(NYd*R!Z2_ɞ*!hTAc2px3H]}=@]_Y0^}gwt# cOU EttAVJNSrY&U+UJJE1HaU@5ikwxN|ҹk5zC'KԘ<^-j3$/K5u&-Qp5 J暒Qr4rn,Am@7dK[>Tluٰ}së otxՕ`ߦ*P'B2p5 (\R' G&w5\gZ׻^<|}WwVPr9꘩{.+a%R!(Pq9g83mRa. $rt >SWV:rk>WX}rKEGK 2؀9ZG@$Ub\TDc+شB-h.YK}6(E[%XӸ$.wBly; -OU+ڼGr꽳ݳҚ7y(n)(A=Ǯ52:ZVf$+̂J]#EOP)=@/q֯/qxpoӡrΟ}=K+3FNȺ :VMi ӒLC5vDS7<]~QmP.rF/Pm`C߽yݏ:6Žў%GVg  uDЏ fB)7^^Lu)6Z2>u䝆c Ъh](VED$ -*d/FI - Œb#ngT-.uGܷ0n -B39+r?%RC]9˻RzU.y;w;l`Wqy-g?cS_iy=*| BKZJO6>b)MSXT*4VUj^cu:ZvctWn`>ӳ~˴[9N;W/9'%j:f8#mϲLviTv:^֚ۖǔ.[Wd1uV#eߴj%?Pbv$k4mv!&2yҶ]7tG۝8 /t)]8IWN0׵^bvWrRsLyc?=*˷ /m $KQ TL eP`F80+c_ĴŦXJU$& U% J>=r25j"#C##KnD]=q=ɑgDGw>ѝW!p|!ݲ7=^Jp|Rq^>(9!Q( HaY1!;BG.;QȞX?2n )~c3:Q/H&à r"d(|!/1B?T`GMG b ֶj+}<Aw#` 'p3nI`ǃѴ(ȦG@=# :d Ry=[9}Ʀ߷ V|aStD}Hp GP''C>i>ԓ}<9S|P6%_z=P5uv1 ġP/r. ܙIH@Z^(%Q| DJ/&8X`a:$I!a xa;{K!Ȉra93aӡ@ -eqqu1Syn-\Hnlf裆XT?go"aHi9C -crY3aaH @FVҖECm<$ 1n&x k&i}V3 #~{Pi کaa5, >.A C+Ĺ!<20DC:oe@Xu QS|pS\(nD{;rPo,'!6@f A c8Lש( _6 hLj] -䛙āh'#NwY3a)X<,a&Fc42Q)mkD,Bg_ ܒZTO.P&6+%_e- / _'E}4pR4Bo`,L\jV[x~IvX%=!+9x-7+__)[T-=YsSn\V/*G5f. 9sPl8PY^X#*EP.r`i^|onI)k-筮EҗvElSecM셦Y֓~G>A^W֯;8"߇UcPwGs-}5bc)pڳS2$kw[4UՇ5wtO7T]Kzuᔦp?VM63rz\?Y Brs9z!p2;ik#|r[a[!g=,Ʈlׂw1XWef ƫVD)tL^Nn?Γ8rFJF7qxg3Pr|UO3& S5`їƽ}/0~_5t<᳷9h[C䙆xO$_TN r0󖻍\g'9YߎAZ-՘MOd%LM59U}v!5J@XĖ1fGyPdвp.O80v9f< smOvcb8fZp(%-$T,,5K34HDuQP"KٗdZN<9\vupdi}{>Q `>7ZNHM$RCÆGda+2ZB'pĂp2SHr] -j yhC_K^hyb5b=lО# pQ,[8XG*cE_ODNCVNš)i8GU;ۈ&_HfPHZ!I!q"EmB"~>"pg#!(Ohg3aNQ4NB8kC{-!v,t5J d @T~|p7c1?#HKAo*V"t@' @ P{}dY7` -b u: Z34b(@,i!֡%`D(0~N} G69?CL  [(B[= q.Ш=4Bqq%xg`]y=;{5x5,k 2)Blp'0\Wx@c2;U ._ QM;#tp[\6scc~pG{ÜʘT e0} 5alZ(~'gYb.cny8=לOO11v -{*D̿D[!އ-L쑾h hśH 1%:K谺8|H!rP6 ca=,(^%~wBx/[bE܋=!9a grܑN6C=ڵQTUnE/?%'bW/wsᓸےRȬCAIɃL;8bXɜ!|n>sZzs~Ē7 ѯ4[؝>sQSYr_?ߓߑǷKWҋayu!CNF -;ڢ0xǡؐ|Ѹ#i{KcmJqkjobMZ:Oo tgw%;y}w,p>zݭB/M6小\!8D߲^7ZՐUPq̸%5:=iszGRUgcefobEf -b,g":z_Jמ 獡#NvF:unrsԱLvSQpxWZy}&6K&w*簩2yCgu9Irr{A"rYLtފ#oserɁ`{&^ɛu6LfJSdSy:qMP\Tee`KBE~Cb2isjrqؤϷ&,%!T ; (]@{:!PRB( R'DD H*" qwPagȇ99ߒs9I$(BVK S%> ~"^=7y^as`&ETSYAʨcGq'y3좂s‚nÔ/.w-XOlDde1%PD _*s:bhqИvN~Vqt`~xv>ǵ6Ç_TSq4Һ"މDnW49z)p}8EGדrlD@`VFExߡ³JdT=bH2`#7>"ak{?~л>;0y&6)!3)l09l:`9e̒ (FPyyX햅#`\/X˜pQ<cr9Ut(PZ=/2*PmC|zu;+lrJ'&I̩ZgTn$VlDt_$ X' ڤEmۓJper7ujRzdYgg穾P3Qֵ]SNA&&t.C#I.^hz-;XO#v>c>N6nkRlrk}xg.+98=7Q; pa``4ݣARP.F}CycJO$ ]ㅾjQPpav:MaC/ao,lfʹ%?wHo, ןDY\$o4(^U5"kUfJglYsVXV^ R x_md-;]:fֳ{l`^`h>jd~rgc" t^hXx@@!`CӘJ*䣃t'w9O~[=>*~fnsK;jZ|[=8t#42B/kd@su:pPQD-JSь6t7t䌞[_Ce!S -"gf(`*`Tݍ=.ne4.OH"Q(D'P\ЈhCFG t}JaFK!k.:7ict5A=Ș0EƬ_lWXi?M12qJ$ވ:&$*eQyPEY+:긺 (# ~| G E 3N:8ͺ;8Oz@5!8&cǴ -|5;Gk :{nq#x9g 8fӸ/<.ou[@1?s!p@3 if o^9-j y;Rf5@nrv' tR/2}e_^S\?zqfLxÞ7$>hp ANAF\2r6hjіI,[t;RZq3~.Ӿg\^3E&$ߑN_%| -, @`iRkCٽV@8y5l 9H:ff -(wĬMқ\?'?z u:Lw~v{ S?xJ;oe;5CB"/oSlKlYk3)Nd;9ut3{ܟ1N|ʸI/WIs >@e@>AngkJXO]%i2Bӟ֯eǤ鎣2Մ!n 1!ktkk:K7J?(}\[0G}Eb=l AdHQ@[!Mڮ{W{zn4yX)(6~;aj<ⵠ*+6EI>9?nj3qf K10$H 0<_^ ꝉh4 ]\ܒ\w,_!5{omwrqqQ{/3=.iH}!徽jϾ&)id`Oˬc6'vMUE]sz=H٤[ ע/Kj{FܕXRgkܴ?ZWLdUE7pQ=’_DőEQoQ3C:~AW= 1%ޙhFIiV V\-[SOxgWVS{zTg*|$1ZpqXqU_-khbOc/scs^r⦅sx!!n꽫QZM}y6Tvnj -Ҁ' ;#=T>)2U>(I*ي.Q$]qWVS4)u߀`_vP@cMjM給`:IkOk[ -lZ -ϗΉ#j3I%iCibVvr/]$8)NIC5Cǝ/: ;/1n&K `ŏX4jFtM@- - aPBzVYaLYㅘk|kObX3ٱ~&6r6ȻOOG6ɠDW9i"ӽQEhƜ ,0b*e9,'aՖS3c3{DQ4H0)ځPqE! -<Q=0i` 4LOt=.a.ʰ"aDCE4TQDU8 cPf([ .Rn(ASxX9xG r09ACڗZ1Jj ֨IGբ8hJ*\'8(>M\'ot b`8dLT;YR6*q~uF.J=QrNި?(KGyR$%zQQţGC1 0Vg်Qf@e;b/CxbQި$D*,,  ]彂w9zЧ[0OE-z c LZ` -c16\0j -#ڭaMzo0|?@uDЧj*[>*/x}P~|ݣ|ݥBY0< }c% \*fS1wM\H tdrtqƽ7jCd n]7{G}^kNtiD/5D/4Dj=|f~Rc5uԙqIDQ⊈ȾCHrsH }; - -#xZʴiZԱuZ>sx9||񐊵n.5YMAJ"KA 5 *#pL6#-pͶz7ӦJWn]Rc&S٥";H+,%p jHVJbe)Qa^b(,D y)|Z)qn3כ X)a zmVoRG,K)kȫvٕɎ|3LV&V%XU?@Uw(1ſ!1Ő(ZeW0Wi x6}=A{a.'M6eKȞ&!>6!$.ݙ[+tOfUUFW#ȑWy{R"wypьÝs8>Zﵡ7"fi-hgMoKKiIuHl7Iz7QCi -n -\+k{'B>p6?7{qevCd]@?ߓv> eЛbw8Gv廝xw{S;|)W[E?r/~V迒g9jfjk`s@=aSN3w1_3"ܑН]QM^i@AH ,!!   aȢ ѶNjkkGfܵ"hE .qj3/s{{sfW/=4rl4:&eUԉU'br(PV_}P#>NW8,9u >K~i]ԅ܋/a坟ÝyDUD^Rj NOD{Z\oO#"V7ЊwXN)iQOͿjr˹jʺZ\25/$7'6}&o 7}״Gm:i=ic l:;wP^Ս Ϳ㌊|QMD[}fpNۊ<zǷ1tmk|cm_blԶݜǸv ?6OvwP;;ye*pALdRԩ3vΰOJuvuO*vt/v^^ٳK޳[s.=͐^cHzak=U>GhùwK[w@9(+JcԾ"_L+)qZ;@U=h̦E;ȇ#J$ëpKi -נZV7n7ˁp;8]~QBi8 c>H7'""zBJ*'T"}kC]dR!EBXd/48pܑ~p֑ ͎,xx5quoC('u"4c )d $L.9t?$\0Q ‚̷C|n Pݠ}f>g#Ѕf!8w - -W(|!g5q ̤+$a.9N )Br=H$$(H-@TPiwgpZwl!_t1 b v{ cbh01dU!$Ą Va8*Ĥ@= >re(>/}K _2AR]`O!tZ -WR`HR~E$bP -ev0CKq'@7' - r\>&@~ aأ+{X>߀8rɀ7(qVH pIj*$&9f̙!vh7z+bMDbGd*FU'9oTת+-Πӧ<S@?IH䓐0)IO0M_=_3[|5略3h5gx/4x57xk}10=c ֟.)~ HEnZ{4:ML5y$҇V'c0l{nj]^An}SwQDMЮ$M|[:A8n@,ҘHB>#/~|qĒ2U<}̷;u 3+ޣ&Op/Bh3Pxtp_t=ᙨ*рK_걺I& (NBQ(e(:\ Ź77ǽ#g={ U[Zm7SH!zʿE-!ƚ+ƛ9ji&"N}} {o7sY Rʳj)s\ΞMoBVkNŲZД!cR֐ȧ̻$VqSmDcYi@~<4VJ' s<0,bK%!dW"fŹbR~]ʀs> *SINf패';Q̨<Ѡs,AeԽ"xBBZuh)MְBXRȶ[ȯ)\.<9q]QMi$((H*"@V,f5@ !LK@(h5x92NGǶsȇ߹~z}c)̓*u96Ϝ e^*3WuZM?YP2r}mob ZfkVPa~RM|%Qz|Ǹ$~(ŵO%n -%ZnUSOPj8=G`ߡ_ҥhܟ)<fA%z)U#%ܫefeE䶉ò3. ҼMBZ P+ڰ¦9$P%+2-%&DqlZ`ߗ+ ks9l3k2"Z*?﯊"you@+a{6 }jKKKbA*huE!j iTo5&#YP>e~L`C&ZSXQr5\k޸qM>ʮSkMmeCJ)׻_V& *W"5QXN< @>Bsoh\!B-"y3$0T`½z5:<̶ɖøPFm[ÉZUGJ>EMʪ|oHY8T*Wy-$W6Ec-sFF*"odRJ,48X`f:` -ؼbt̡Bpק{+y~š@~,6<_ɮdUL2d tt[Z?tBɐ!䭐\oJީr1p {@0uָ]r]Ky뀨1dzX]ksTu -BV&*)LU*CqP|Ce¬Aȿ!mpLp~Wy z{ô1){˻O9w&)HWԖu㕧4K3!i03"Y3JJTϑ+ r|ȭ^:OuW) {hshF}p|f+iZ@՜#H1\%wc홗.:3.f$p㦥NNzj5y˟>?}?Oq$6nfDgpG p =I=@+B;D7xxK>ؼ04+6g|`rŁfڵCk3eO=IW_zFοC#fwv~Qir -os+ k cLV-&۞˲?f`;Dx; ejgA'зhv 7|fkg/] z ٿկ{x`),@ [ߙ@C 8`64f1ƳGm4c5ȵ4W+jv8N Z] _;{ z LpA8"4```"pP, RFC` -l¥zb'&jA'^R -4TxPQ HG</chm6F&Vjr -l&e -#n#D -eSNCCC@:*"=S,kP%;LQRBlt$js_%nsFΐـ޻9sG^xWSo-Tj}'润 MuyVMg/hF5DӠDdEa0$L!g*Si=j0DG3t9G.ߌzFZd-tm%mӅZ!?9rNGؠq;EQ=QGNZ (M4LfΙIJz{zX[3ح ټkqyVcW\YgCSǟ"8(s9~P~Tx>좸6xx!IM8JEo`iǒ7g`Yûl;x -ʩg[at5#}!UgєPp6i 6-)>$VG7yTE_UF?UcP=LxI ds0<Z@{-ΑR.¸j8]ECF.-D -ǣ_:N N&!Ƚ2~"RVws܏^ZqO%(ߓok"!dc@13E4wкXD]c[lظ ]lq|,úՙ3 -\+ֹM.}7מEIRN+g^3?*I1ބS8Ä́!9&1<&_b7r2Wi1_ì͍dIUTfgT6k^QIɷ<^3{{j϶:-畅w_u+7nJG騘=C<R}ZVry^).jpdI*/Wy`vs-q-[ 5gdBV.YMY2O(g6yK.omZ>a"^.#NzK\ g8@U+beV%y:Ewn_Bu.Ϩ<PD H)#LQA,"tІFpF RD *1XQp]f%'nf=G}s=WR*x-^nAIܐ84wQSQQ;aQP_B61xCTT0^,p̕_-]Qךnܔm^`UfWH+v)OmRIޒ)ܤ޹oEDBLH$ oA26.98]pfnt.*[;hQ]&8+e6lDzBY[Q+HouSEg|2R>H{-H#BK&E20\ߖpQ )qXt)*+4W֕V"ҭ &ۖg:J $\IN^vNWFv -h[i Q^R"K0T꺭bQ#U+,-}).$)" -&{d1pq5k7٨&+46r5 j:^q:(X̝),dEK9wkE5/snAph}OQQQF_,Õ2ڃJwfm4Յlț5{V5d7DbRd+>6)uSu墈&ކ.uCq~hН) -Sxgz7.^܃ZZi>5Pt:2e^iRuI*Knm7rKs=M2 JnHC{p OpCpC:=zW? --4 ]@e*{磤ϖ)sg.VY97[pp֮(f):v!;ikw۪n{B.^R=lRMPzA]H-u̕IrbVύ>u4BcuGLBd.XPWvﰢqy7N}7{;s& 9:t}C@HĶQc$:2%@`u#BF6_s*ppqp5~'[-LjL.7h2h1=D[!b܍Y?.b/Qߪr#icؤ#7&s,17]Կ+_6dǁ\DU#c$&3+Y+&lU}'|2爦4SJM&-m):S]{ýqwx+}P2 -d.W)6ncmm,m -ib191 qBG|KV@E1aɂ:3jQ!9N,vP>'Sߨ־XԤN]O}&gI}D]\wa% R)i~=>BO͂82ٙ!.g.CX~خe6JlQ*\iTS@H.!y`b FdA@A 4 aJ"cD'( -(
Pϱ+߱}`:{uI ,c`#ց]{I|OdE?Xc{< -8b¯37*535.ClU4-B 8۰::paQpڱX'v1e| 2F9#a[ lA{̷Sɥ,s0\; -"_ h@ |9 fрW2:pb5 a|'&Gq b{̽D|^'Fa7BMhXt'=o) <_YؗڝW(5ܞdZnQcU!-[j!.z5{%-dp_jI:Pw1 d_hwWеL*D:臕fJ>Y)hץ(Sc +e&Ir2j}S_l_W- TC|)3I]':&ͺ(f^zLd/.XBVJ/)y+nd)˼hYh=w2٬ )vQ,yvi%)YaYYWCoadbτd`Π6AfҊ`u ؋M,hSbE nU/*H,X%%.ls>abAǐSN7=p w -%!9kųx)-vbE8{`u,= -*̃/ŖŔ fL7=[+"|WhW+BwK' ,:}mDss^R(shRX\)wPCTffU*'EL;mV1$bل-mWC_^!S~\[~ uI}q-v P߻`G)@N9@ΡKհSk -km N3<:fjm0ormڹ55K֤Uk|YxWWENѨQ?Jw0%wrf@Rɼe%ǘג3Ly)P@w_wOqץu*N:ڕyw[6~_U%1/;{xb ؅>K= c.Ul&׮VsǭX[-uMu^uY~%U4uyqx"*ʡvlC5ިxxŃJs`Vm\clTf3iwNl׶ݴMnc3w<>]لmqN -`TRiyFxs.q|r۵yyo띭}w8>9|nrolʖҵ-ˤe=UΧ䋜o)`"<#“QZ2\b$D+ mk ݾKvxr~Kqf(/]p6Q43` (; e /r*x> ].K< ^9e>gx:,fٌ M`tWDL+p`_+ǐ5|U"wxP w`EĄ+͸EQ"\!dAל8#P ܆Vk=!㼽ay4gTh֩ȑtG] ;z6& -,}sQD%IV%~pYJFii~Nu?V,'ZBsS` 9}yt{\T_b޼1zDw5Q]_Z|#x~sKn)$U9 48U*婄\C"⁒RX?"ZB =zOᨮFgyfG*˒V{3f{OBlMz 4eεFO >pZ`JUD/y:Ľr y̿_ # C{-4k-CF(^ԽfjppQ0f|7\^a3d{wUҕiM դ 0ь]}QNbWT.ŪUV^+1\"h:еg=Փp>j --b oЫ*CH׵Gh(MAcj1:QLtwxBOg tZf݈kVߙ^b]jP!SXIsGN/l7O3y|-0?a LYB6b>@p-3(.7RFvsնD7ó2?YWyĪw6vXhŽ]ٖ eɳјJgg]ȳfPQ%L^`}a`aQ PD0÷Q )Fal7Ls:q&3sޜ0e \[%%I8ù s>pٴi -]ʞQK @ ?IoUWp㠻6DC{=7ff:47BsP u~ڪ`v? lo>mnVGA '&:n1ߒBӡC U(| YO"$=3!Q2 @ׄBz=HfX0IF)_u@wPlP ( PC2hM? tB"A -kSRsӚEs@a=2`8Ȩl3q}JCHb >$L$)^>8qZt^wK-uD'3Ÿ2q'vABpaRNH^ɛB~ CXHPCnnDOZu T 52^HF"$W셺=W3uЯGnj6{ΆF.f#W'~#{;֫e=֥?:CןdNvå_…VW.D.rah+i 8Jc=a} Xa2bak7lcwݿfwܳmЗH=_2p5YIr4'jqbjQ3o7>xDxG#G퍼y13K~{ԷofÆ_$TT.nTwR7\v43g7p$I42w7y Y<=Aߎ~m1[b~(h-hHh\`%li<?"ﻝAtWΰ)83Aq^aQYpZӀ4ۥWsN)LjK$4%Ygu 煵Dğ |1SPk_yd`ZT[0VFr2zeN -K<׭EhJ3y5YxS}k]|tDP%VHEfuIcQؘo1}c%殺9Us0clƐfӧveٷ͙.J[}FG]z%WPt!A|BT*˗$S^X`EаtP7)r>0Oc m@o6Sm9`ߨIvV8ת\+Urg̬-l/VʣBYNxBKdń3_ ܣگ逋"`~ǸCBGGAӢñG%8XrӜ+wcNg3de7 }|aQ$G}%@~HJQbB'Ry"es8Ba+z|tٰ֠iEU9H.%:!_YW˫mks{H=%Qi/dj )Բb-in({HWFG'5ԗ25e;8a\sI}iqp)2t|b -~VA `T;!V.J亻r'?$ -K߱!u="!{KsH_[p"$bP[*( b ݜB~xmuSv%2MYY^aS̃$0(8qKQ[Q&']%3ZZ:WtCY?֠ȺYwrpnvC}V}^8vw֕z&Vk}j15,(-aW¨/U V]uTz>+C4-(lA~*h7#};jdEqmim2Gi9%5\y볿_x,?:_/aa ճ`>GSʹ -]=m]]@^7^/dٿA0Xnb>/!W[cv 幷%ޮB:B:㦉fz~t.tV.=Q7![@$oGx(3͉OF"Ʋ9u5ctmim##?r>o<Y, Q}hPv ec@¤b=%F:ފ] gBgb=3) ΙmU?nqxkzq 7/ޜS'Xc@ -v>ʵ sH:D&u9_[sc>oχ`|mq2oTh3q6٬܍~Ivl?ᮝn9~Wc2 Ng9ឋ@e.,x p iq6.a]xj_Ǻ%,e%V/YUKʥ#LyFr6#Y,/<爯1E#T{'trcIICm׀759`w -ﮃ^ł_9}PV )x=χ_u1>FH}oM+@ lzx> չle D((`W% 1`A,H@Dņ(< C-O1D!ODQDĂg0;=;{9F 9PY0s C Yҿ#DHNb:D X -ACFr<(g3J,Z=X=OZ8 `] h%+"6!j&;@:5ͣ1n@hm -}k7jGK(]48Zw }0`W.e@~5Gn+jM :kRsG?:=@ON}DoG=b{}`]$7bu)bѽ5t?+f 5(F?C?b>`hUŗ`RwŮ`0o4H%$"H>)k xCxjth(m0k0QLɼLDcOWI%KrW߀|ExNyn@߅BV5ջ ]x)[,<:t6ᑬt7J+&BZ7pC]h2ehTsE9塡|(T7Tj -:U3PŸ@7QMVen)wr{q]yMWՌCkp^øZsƝ{=fKm`f9/c)QDy P+Kz?'\z?#qnU듸c>;sC V}ҮBܾUXfLXD%L3lw`φ1H6G[g\qǜqy,wy"y_sW8-q;-v\#,s:Jvv:;9:wlqfLm|N:h{u A!8bnqm* -[u_epKؐ**2,m7֛l1l5)0.7TJ6 W\:dk\^V2Yg`(vF#9. % }#cwJFscS[ŋ6-X f%YZ=_ڽXU9 ֥t'+mZ#PM88>(cEV~O8qT oDѺk6+Y"ʐ-ʑg{fzmS,maeye//L:}?>4sЬD}>͟Po ;`k@xry`A1Zѓ٣L2eit,ET-RBg}[~=h(1:ӑ (X2 d 3lj2|/N&I I?Wbиlqr1_5׸S3Xejf<.iSGCp" -80(ٯ[u^ȉ̘AȈҢ}QXqR9Ӥ'S E ]|j)ǻMk"-&1sT?pjPEq췍Ҽ3NZ,ҿqBj;(v<.@0wlpvL8!f)xy\ԨLȵ" uyGEuqwgfd`.誈i*e60 URUZb2XYK(nQ@M\)GO-hknQ999s{}b<31=uO\u]D1D[~:s[<ס='ˍykP0e P0I(HҜy2s&3.N#56CiuXShvNޠGGp>36o_kE QY|7jdYc?4bIQ4I\tl-4 6)1D")!ΐc/T+b۵ \z/NFŋ~>\3T`'ٔuy%&G,5E^rR!+ea򗤚a6IѶE -$}LR¤r'Vaܦ 7w 3wY`%Rf5Q|'&`_ԥ;I 2ۭ^8cGbt8Nşi kܭz a5_b[7 W`=.Z -׆]4T[]Mo:`+@. -L p? f' iA̓0 8 ׃S -{t{Ȁ>-fn)Eϖ:4@ro9tXr0y TO&`R3`Q19*hZ]nusp2Nm U{0C{2OAy -vP7A%PJ^uqW}@w&cN7sG80u -p>-*ka{l(H/xArA$upup}DwPA;6yDt3=S-iw8O.ձ]#Zr_`HD)PY^K:_KFn )kp9}5O= G; pKŦ@ ؋+p By:xyDM?} :[KWO21 F.~EG+#ɗK q_po-~#nT]:˪^nb -8K!N>C<O}'iǠd[[k;ϯEf\ wNrgũ!p/394L`""}*/@%Spk6\KÍ8†NQp: -jp2`9Nű yy9t>`:G}vm(/cH?5'Ip?P;2z4.c: 'i8ڍVW0.bfzWt[=h/ - n{h˸_E zyɓTb5 O7?OEOHhq`t Dg)`Cʘ!]Zv{*vkphsѦŭ!CΉׇ7OZ4gI{Y*w}? A/zPg&2S:Qh MP3}:5<@SnT6hZ4uuqUҠ%YVkNq+5WSHOQZ*HyYITꩤމ&8biޡ'H}1 -"'b{d86Gji`6D3-vv]m / %^^%^DR[- & +[v\^'_H {BWG7&3ҿ| )-F{lM16ͱhIbƤ2l}C<@,L\5G$jW3NpZzfyմyQG}*.*D=P̎NQ-xyO |JOi:D'mxZѬSQ7uOg x $>[TujZ*W+F^kSRΌUighUjP yj9n/L]ns!I!X2)!K CmJ,S,HNe'e%9ĕىeܬJiybLR^[(TĻ|~$A& \9 4{IF ǪXnZ㻨1b12h![\npI%\BZ_ +/7+)ەN}?&zAI(^s?dN~7_mSJ<ñ TMAeZ$Sa2s -[jfKS\q7K]*Ӵ'Ԥ1n},)F??2 J/W袙h!kS.s(N9]Q;yIq#IlĦ3Ein8U(1} -$pGn?cUk(b,% J,v-I.. Eu݊#ʘOʘA'GHotE,9g0@X3}9ݓT84ɬOOZqIP/y_,*ʷ8o{PzN-gߑn1>c ӧ#% iJ-,KRĦwIp^4;D!:gk{Re܋$$ӻ0 -Lg6)C8cl7FgaTV?x B\,Il|ଥҨղYeY&rM"<'"*WB[+XIYIoR٢M^s=\wD\C5`0D"83ƹBqfL7JHCvKCviH#iȆe!Ԧ.e.I -^ ̦{~F`8[֘B99c@"u(AxI$ %_2JF_Tf!شzS۴Ne*Kv -PrQ? -_H -\ la d5i!݉tOR r+ZJWޕeE9X 0e,sòb 3КB[m(xuQ!b#IY}XLa[8 l5N /xF6#n7LŖ-lڶ* joAe}u͏Dt##s*g16Jҿ< pnPBUNP6t>2 kgBCfQttR@Z| 01O'06 z? 'Q@86!=Õx-~ 0h兠V>xڸ\[ 9/G0+"<5`#Ha 8iAu#y㼖➼ ŜG;/"WX_B_-'{9ȍN2I{F(;޾^S@y\|N u'^5Mw6'݁t$jV; . ={\\ ry =f -0^-z~I8m|E&w͜>ɤDtح;DM"P2$ydIOK exJVғ;؀DW!-tUU񭸆2Gq?"G@ο\!/"o™89iͦ=zГQ3pkMrpqUFjFgъSm$3‘O"%Cpb.đh8!x ܒNuY"o$[ TY:Sf*/G|6Eр&E :=؝~@JDd j|<\5x]7\uK18 Ψ)ؐ<޷=||E>86pcQgvǡJ? -`>e TNXI(ћ`Pl67HCNI6ܒCHrCEIίx̲\fimc?p}a2lEa$&4lLY(6COBao/}\)A55J .{]8..]n r[ۊ(%*XԱmSӦi3δv!mӴt2M3Mil/2f9ߞ>f&mJ`OfO-'_Ʌɍˍ "ܱj}6p/{Vp\qz܊5)hF+" ӚjLjIMs"fÙ!v43gNsCܠ"\4wYSe}~@DPCܦ+;t/m"hUc*7'sMέdFNfXa젶 i{~0ק=hgnK.UEg_  yyBrpeCmnNSgcDǐނA}ۘ~#ۥ悺\noӝQ ~+BY_٤+Ш>BIN1@QD,60aL@ش æ4g"dGٌt*tUVG5U~B$Zʜ5!M^Z{&Mpݵ6W&dw*&]g] ] -+"\F5uWep2CiJi -zE{RpqbS#uEuSnCw}jwςb_c٘B5Y3xwZ. -וywy_sjJ`&FOy]7Gif-PO՟ -f"1j=d\?_T䴼n"[n~i~-J#0GLQ;;ZPŽ0wn)j2@eE~W9tYV2s܁wyu65WGyu7HJxZ)st~P@1лoA^nhRqp@P>CfdJ U!#:¤zq65qMMKr)=kJu.ӞF D5-ʞ[ -d!st#2ƶc8ia=R|+,a_؉pH0ç] M&)|II74eָLZqhcq=dLO ej=N'$$O`fbI"qH+FB3sH\3oFH28O1p#Mύ!Z-v87 dRLL=e\,'`f< )H\8شhlX|s/#~qxqo n=<9) Ch_$uh -ПfIH^6]p) D"\ށX"vŌu+XEJʕA|-p~I|gėPG@pS%gi9i%ҿL/וP1M}SQQB_CRJSNhHER !|dB29>eȲZfǚ550żmw\]繟њ@ҚCeSeSE؈xxu`E D994|Cݬ`@c\ 0^_o !(`$' NRp>ٜ69mV<Z[9ɭ u;yr)ɘ+ƫf0jRӢ P676 -}@R;Nl_lL:X;:8 u'F7yۀ܋ouQ`= PSy -*_6XAEl<sDw' L7;0x0ZSלV/R"ȭN'w97?=G3sǼBOٌ<݋*%_꺑aуBFqd$$o+%9V)6 *5 Gp-'.o Y]> C+"/NyYG(2ꬢ:lΧq&9[<`_Gz)s 1'#`uQ/Z)ʤN`uSJY1ks4r.f~/Ȣ!ӝ7:WGP3ٌV"uC.b,lN%3_gpqoZ躙Y<8I + ᧾dJQϟ0ԊhI>K\P\͜E140M\ts :k42JC斒e.F` J.$A"gѦV84%^:e^.R/NZ*4؉zzu uawc3vE= 2,wwȍ>6^X㴱;MI(M"pX2 ʏqPze6>WNbOr۱t?63/QmvU揱-xN,+? bIaJ.l?=*q|]?o쵘ݖѨ)nyfQ%*W`U56YQ1 ^GXmnzxj3%Gyg{GFhh:!m3;m}PcjQevlMv`7v9Xgka}9VoE{X^nA+'C %bY(zԿ=}bi}z5 56t38zc?*ð1ӱ#9JYXR<,u*BSPۓ C%(Cg( -<'bg$LCRimE/R. aǜ|+W㬰@=ծL?2ԡHWO,TcNiB:[H+~vHZENһ\b͡\l{Jza|7[ +Θ_!90IB|B\`/.> \/E(TL : <&JAigV29Rz)d>rIv!cv RC⎙!c1# !a ؐHĄ -ѡIBTh0=4W -mSL -'-kzaRYA#[]dK3f H0$h\FFhM04ᘮiBdx0%|09_#M273(Bj+7& -"0#2L*8y2T2E$\Ct2_ۮfFmo^R=|yߔ-}ԋRR-)>Ϝ"3*{$efEim4%wW^zQM ʶ&fPndXVL#1Z[W,2Z2cI>&6j!<@ۖh!Y>q4M`,e,x 9*#fT{RclO8MIE,%eF+amڭku!u -j[5Vm8պʭVU_Z-mߺC[[7䷦A-Vsy\۾HJ1eRl4^kЯBnfs nԢ:D~aj^)K`eổf+]M"ˁ샓4(-wZ^;ir) 㞓nwF[Zi&sMk.:⽏B8jijpkxju-HN~spRb]05g9#э܆AV -xE{M\0pvƎ4Gh 1.::6zIù:bQG, r/ֱ>[#>AVG%h8ٜh[mӝihml҉GccPϡ_ONIt=.9_9%tzuR glf13] &;Jw>%}iBPWf2PWIU̫8rf`Db405nt;xZj~yl ҧp>HKo[ȝkrf>7vߐ@a5쇃L  B,$B&'fѿHi5\Buz}M=żtC:~5V)@C(M44sћ 4棱XׯuSDg-:XsE]>c}X+]`F>/jh   M@'_\h(Ac{)ezO=lK59cnE鄩zj>^TPnBHȅ@B.@!B- !"BAQDTRuκεgzvvnݥ]9o|=y2|k1;nmmW׆p%f.Ōb^pe^wqbpGX}qQ,MB!R}[;q+67Dĕ8.n0np̆vr|'p"~b!MX‘8p1Slӛ+,ejwѽW6\ڔsxiXJ$+܂d'wc.#2-޺[b_77 I}O0vG1QݟP{WH{1jm }=]8c,Pxub'k&j~GIF(}Ls1.è( Qv 0 .E!qbz]BgNtW2)ZXKM於C~ʚ%X$|@5敏)pS=e勔Ǡd#$Jr#K*C@ZԂvenx:) -f$sSk48?&"fE9OO5_{Hcq2Kc^2F9)_<Ay)(ѡ]QVE9*(Mp+Фtl ;|Us^lcQQfU=|ƌsdL3NY)GQF@:,xphRRW€ - WzPW [jƙEsjN1Ǩ}|H@1jO'Pݛz49D&N7@9z_ЦG t|4j JQU*;,:7:?L>fԏ /1*Go6Gg!=GrO4\Q|̒մoѽ =?eiѬ_> Tx -`5\@mj#5uiBuc:NVefZq1^Tr*L#NKT֬`o+&&uh<͔zSy(CC xIe_3LVe(%dtSV@uʹ[hUIѶ -X6# D(ЯvB / ?02xmY/sd?q5iݧg&#?E^`!! rB*d lnFf2SH/!-|H }z+NRi2Bz'6@m W7Dd;灼i06@0{]1K%5edX+aXo/m` ƣP;612@:Xvp {KO"ޣbrϯ.˥*4q~d%dԳճ|$$;G؍=g~Iރs{Ecpnk*>'͓|]%5!qw4V BB%}lN:PBp$aIvg9s~DD$<Ɂ' =Q%_BfjDd{=YpN')|FLN3,19%]`aB.(}INPWʤ8xd8Du:1>J;돟Ч[o pp p&t;1({@}>np/p_b?'v5Q5M+[4[Rjcr}Ǯ{GﱷEsS{^ =9כ `.AFhC+qrCH_i!eWk2[EB ;ɳtUvZ+~~vuGMD]쟀F3A#bÎZ̙m e|??[#(FXI 5hHKS?(4HИ9hb4qR<_Zق5b.@dP+^?jFؤsbguC |h4)ڏ$/{;vk.rrkmOqR-Yۤk#ވ ?;@_e.hza}D Bc>رlÎر;ꔏ<-zUv5ZVJ*T)W x+>hv@Iy _bh4ICgV)B^fUllF-n~TTj{OĎ\봷hh{NC-U'5vk}?UV0īܐe-5LbCfaJh*w\*v:"*p~9.ϔbUnܜoGEچ{hsVDh_wTb-pMD(9IQ&1S9DqpŚjdt/ a44ztc-Mh`yн\g̣:0+*"EPEaeXT7( -.ǚb&Zq_c5֥1xXҨZMD? \{0t^>|߂3s1TG9y%41W1~PŌV1V$ٍ6es[2͔-WJ3-WAMJ`?fr1 6 k`T78bEqgO9+h`U9Kq&(%a,pFIJHc0'+ ?:cx#%S3=|K!1'tTN쉽/[P%5)J)Iq$[d-`.s\ŧ<+SJM2ZbSI Qg[)#Si)ZdQ5DJH5ʜ4LiK+Rm9-QtzƦoԘТKj0;1Ue -v~ۘ7m]Č~2g V|F2-cY1YVEg56@cehKoPxve -G -r+^eti)̇ ߶LUvyWMP*սj4ʳrUS99~nբf@ pWKXN/`^ @8)a3/ffl^˹-~**uv4{Wnuٕ-thFt2K` Py;Nn{7M.v77\ĵ7TN(WRjgpG˽_&h'?mM^!A918P~!0qGBAlp.|7ݾ\Gx`K:9:A$'1 -G f:GMhI ކ* ]{.CvyH8ZZg8 U|J}'|/Fk~Eo#v{n;tk`3?M—Nñ=]|m--M< W8/t?úB9sIm|y=C魇 -ЏF{ok:KkOB<u:=K[Dp\џDlAOЕp@F=+1ɤI *!q|@#q8մNjB)odJOXWGta(V2:h䳣:FGqK]k!*WmWxvjgvBm1<{/H.ΐ}"1++YO䱜LYvNATúuLM&آMG2ӤO<JpW0`6``6` -$&!IsM4I&kf]zd=Uuӎv6դQҺN:mkUv޷dz{>I}R/xW%^սŋ7Zʥc:\G&dQqXtS gb"㙏5;e2|+ -O =.V%?{ewV,Y ,de#l33a*pN79nek4y g((FsP;."7)R.JŎ].%Yˏg m K(dXѢV 2X4Lq턶GIPݦ2=Ke6ҿ7Q׾H_Ny5K/Ib$SCrM6MNJ)&X:@w8]eos[<7C_kҝ6GYyҾLh_Fͱ 3k6Tmqeioi⧣"D{(Uh:D,xlO}fۯ_\DVyFWf/k\2,'XL5v IM[aS4,d +48/QxKEDd'{VwQi> fѩ6n5zqmIޚNuk>VֶJTzx#f(-Q[仗G~C(7_eJ"(YRZ X;TvPљN3eՔ1[(80EQ`#.x O~S -U..HgI*1'k*j;ʃ(`KO>=&z(쭥z MIv Y =DFۤ~&~OF'dDwK렴ĔDPKINA? L!w("d U9@pA҆GI#ydGΈ$ ?KŻ$ }*wJkYEHM%ZcUVQ[cȘ06HD:)y$OyZ'$bcxMćOb_O7xG?#~<Ši1"ѡ5UIJQ٘U!}z I$m8Ms`/68e|/Hu^dD~@cL<0""2 -* 5"(Ȧ(( (8* -+˩₩1n&DQc%i[5ǦMjԨI44>99=Ǚg}T:++Hϖs''- ŏ;q?>Əq)S&ժt"_u~uyzYWz+TXGO~>/~طb-v7R(=zB>C,N)V|^)P+[]G9DFx!Ngu%yab Qh@#`52yi>ZUƏq@Vf*%cDuX;;M,$ǩW5Ġ1 -㱟LVUG$oV*V[rcգ_Ks4g [{/^g A' -hEc)hdc)E -ZV,""[.v._iswr# kG>>wpelwUSVw JhYG%Vu.ZꚢZL-q"|Y܊TVjZ֤y-*s?RwTcxJ1lD%G(1,V aAي /иrF,؈lCuR#~=;iAo m -1 -ǽl09C"J (EӸъQ)5UkTtFF[4b0dǼa1|`!vS\7ya&po -K#.M ̣>0dQvMvD}}GEepcĠ`Ԉ\Ƹ5qiFkUظ/MjzbNs5MSTk7IOsf`f{{K9YeIPfRIIHJWzrҒR%SJMJM]j%7)MG`A,W}́z@y>9JTqd2јTI)& -`ҧ)1ݮEXgޭX`>x7e8نV7m\30*ǔ.SR3(;[9%ʩVdLE(}t jK4l)w)We 7v2l {Emg6k|m~sn0(z8E BװH~Rr_&,K8p.+*.]tqíAaa= Gw1]_5 ͩPFs([\!k\>ZiJɩm*si-䱎jb`;6{[ Vf6SDEVFr{ 6xh$2.c}cc}ǹ}7TGKH1Ia1y5빑oz v^x2 - 3#jrK y36 Y+0;g6~K8N[ u?E\vih2@o!ނ18I59͌# </W/RK ե e_&*F;Djǒ7pjY`\ U -\eN>aFї2gl MVżuؠu <=w'-]U'mu}r uvxa}k}Ӹ_C<ω <74}tWE/JD3|t*Ш-6KANw}eE|y\Y"qyW(29?9<{=;BDzQDJ^Gt<ΐ))y|X5<\i0w|G'X4HG# |4J=ͫ O[;i$Nb''sqbױsqiRM6Z:umU]K+T(L\Mh B6&B Ć m0ډ3??~:w}~{cc/V0]b -|Q_75O op}$1s4WG - :kѡ\i5ϫ~j%?L FX0i*\ъvif/hGɋ*ɒ5Q&>d -eEi׸?-Ye,-5jԪJ-ЬyC =ij׌!ƔiM5a<NjĮ1]ר鞒ה0F,Yڬ^FzЧ}c~,lZsLf1;5mnДEami21˘F-Jn\U c>nzRqU Zju~?>./8l>Xz{f,3qږ)Q)&iU֦-,xwnm~LъksxUa -WާyEit-<3M2s36{f 5dw*nנݧG=9bVr\Ym(TO5wU:koÇgZI"r=I8ce^FːH_mUPY^g8[R&Au׎*X;EuTo͉[=?kh=Rioޡyd,}TckDָTߖۿG.Ljj7T2|[/iW?ճ@su~NB/ ]m|5j RC%w{jc@霑sE՝GT*^eٻހ_p7ά濏YjN~#?yJ -ZUVnTTeOLCS-isCO,| $[[&[=>Vy54ИVA:R#Շ}…rn1*bQe\HnUCe٢CFS]C;'Ḵ{Mb?9WY73hzϣ3N Aۄ%n໣RU_*KT>`RـUA6 j`#e )>s2/]_SIǴ0:tf|0^ B-'F)ՃRuPAED6$dLeHKɘJ 'w([*H^T^r*7C%~(cFgJ D H~hObl3ɘ*QIʤ&*Lժ ըT@aتuL.EL%*Pl܎3% Ce{ˇvLJmϳ?ݿ}" JH%:bv̠RѰ$H@phĈ`ּ>5&ym xyX{g(b5 s/w)1WΣ0JWAJc6ԔG1 #uơK?C"<˚eße.o-q3<{>Mzmx_ShB?ʹ |5[By=g®r'oϳ.0gK2{9 2{2r{ 8|oaׄnZr1xvfK04&{CYi>>椏 ~q>J%?A۹B>zƸ%9j]cF2ur9ACa?/~곟B;i8'U9@mcAg|FW(ćW$ ^~Ea{3ظ!'}q=/XRl $Ip.G&& ҝjKt>oKOlH1ӝS{7$ۘ~S M̫2ґZv>Ϫ@VOS;tF=ğI |ݞpOѩye \0]׹ i"'kL>RXf)'Z:%t,ev+-H|';!.'v5LqTa'&3iB/mt9.hXIdn9L?Ev( ,r5^qOCr1/$v9u&q'-[|c!.yds.3: -On1.̓ي -U2E|$E/"|,||\q7˺LOgTT2CeO8[S6[.R^/i8:4D# <4(GJ31yJ}P\M曓Tp$:`v [6 jV^?!=8-:qHCh(fSwԫMԡAS4>. Y2a ݩЃj -=!vA@{ql5[=0fO53\6;ܠICtgUaɚR{Xi Tkh79|uq 5D,P}JEnGBTaT,5VŶDٜ*e/Hy&)7U9]N%}Ik2*#\gsó֣T= W|$^h)Ub{ -Fʳ'+מle'I.;FY)LTc|Pr:#x>3zhL9eHc_#yVR!: qq)ˑLS,yJO-QZZRL#}R\ z@IGeǕ6|W<h5 ћȅL|}^d+ -W\QhŔX]tȑ_$4(c,J*t=TO\K%7MEF4 gR]AQg]wEЪ(* --, -BmăD3iFUi;1&ͤNkNc̴L56i֣c,d?Y罾}FL+`WJQdv|dȕQ Jv\*C ~;+ιOcqX^8V±`>( *id_+;IFYIJdT'[y*u)ڋ'/ыp| <<_h&q;(@1τ;~$J ~dʼnJ**@0 :3"$ * !yURxP JlL_qÿ~Llu1JXbPt|R.Fz#ìCH Njų#aKgpK-/p -PH9ĜE̓}O?/Q_µEgKO F+k+:w%KF.(\/Qu`;ϰ-DMT\~vPBsy&1O _?f4`9VAZM.?Ppxs{Ez3r [d!m\@̳p}jΫ)$C7XlaX?X6N`LM6s6U|RMySpw+TQ"͡|ի^3uK a·A? XWY -q/O=r, w}qKCM~'q~g<>,O ڙzb/ku?#|agD:a/Caq0&Xku7F4(8!8G䠿&M sA ";`4"hu&x`x?NsfO8)w /:r΄;M6HhD9pɈH#88rpu\,b%% -~O -y.!MwAQj@|ν:+OQ8|H❧I~E?"sphBp;C->Un3o>$}|QX5=:7j ~{=Hj=k? -Ux3z]W]Rt+pk>\P\fFi3[GP'^uz|:z:~CE0-{/J'i : A ƸE+Zd$,%ض㷋\DKè!A6]Tyxscu9/pޏ#N[f|a -Gb]m;V]a;l/nvS<7v#dr EA+|2;17bۊtf.v#ʎ^DZ=B]F yBz}d%,ã%2vb\lQ*'a{:sυ.#U{~=7QBy5df'ީ~.=$8#`; ۓ=beد~ ?:CZEKo -rzSL9q,Ǭ`#vpFHo~:b&'2B". -8p@wtұkuԣj .3HxU32_ Vq G-*3VÑG&ȃceTY 1GT5Ii -De=G(\jycm+U5qr ?'L84^zJKXk'/SIF-6X3k,!K.l-HWMbHQuOzU&.UUfRqJL/tBEp |'6\p-^~w[62UcJӔjTM3Te|S**7WUV㖫hjͳUk}Eso*!=pm`cmzk.|q⛃SbUeMRŢ -MI*NS5[ֹ*ZS;IyW)7urR٩O+fL9p{HC -U |w*_ԖTRST:A575Kslʳ*VDEʞT5#}2.5-cD55,! ¿4`$|e}oJx  b I5AI*;œVYKfnVbUQyUۺuն]ﶹ]n9 d'y^z|*|̍W%Yety-Y*R OGrjU(Ek -&-V_vl4~PVg~”߬8Ki̥*PfYI(/TzT) jhQjE'Uo@ɾA%;Ċs2T\*>W?a;Rԃ|ǤJ pϊ|THپx&')ʨLiԪP*JnRbuDŚXFwlU|^U կ+|DUݬmo W -TP *1Q -<|.HF3ńٴF4P(NiO;JN3X3.kᡖ&lAĵ)0(41{$f[3K7E,^mfv)##ψvl/ dx:4z0^oQ&R1&J ȵ Ny=/亭Mԃ>!g}6blS|s>imd7yp.]6E,`c 5YQ>9fq/r9br9c/[yfg0% .mm,o:HCYk7f-Pl,`'&'ߡOQ!zt~"'(~sbϫ5*]Msv,!{_3hl<&Bh-TlDŽ0 ň2=r?F(8 -a:tPuVr4%-|4.F&1BJg蓳q\E?OAr3!pFpvR#<+;<au:Qx\(.A]6}fJ#+{^8i=syS~}=*:+G /P]Wiԟ%.~J~B.i\:ops0^/c_>Q\f -4G5t̻jL?~ʹy -JCxЙOEh47jvP}hũ߄3,ji0)(' -L5{ #u̼M`pEWhT՟W<~`;۹v0Ŵi%mx} %rǘ as9jj=7{L`e R5:%.Z;}Q`O#6Zm/u؞{݌VlEݥ Te е/iVқbX1\G.t욱k.l{]Z쇰V+#]Lb -Y:1~6ktv 5bׄE g?RX a2)snM?ӳٮ:e05&9(Fd}{\,XH.&=Fڍc~t!셱ۦv,n/f:z43UaKH}$A+oX&fp:9/:jQ6LC8JdRruaĉc;ǗN8NvlDZs:M$m״ K֭bBJAVSV1Dm0؀A h*h6&.ZQPG'e=:3Hì1V*f젗 c%Xz>A4lsGX 㰔gKH ;;Ѩ$:u42to>.& zg=;6%ʯc³x/U|8fwcniL".|5ը\nsL]:Yuv0-WxZ(m٣fA,ǔVr vM{RaG^{jSWKVZliDҸZJJ[;lWʺPɲ1%(n۬mjS" -ۧ:\G8N -6 -CC7]'caVDY]-vJ~%uJأjw)UԱ@ 1E(llVG!~*h<G -W^k[KBzNUy9-粼u7 ;\MZL3v@gi%r1O5m - ջ+rW]'OWT]HU+ީJ.Uq}\Kryr{oj'荓@.pm4$x#FE[תסץjWU DJ[զ~UT㟐ۿA |EFpJ偋rPOtk#Z!kR]'D~vy*婩&.W0#gGڅ2j<4)Gh/òRYcm݆]h44O#"YePP\u9rWɨUy}4t'[d"kdlUidJ%#DN *\d ԿEسA,$!=P ˀ91B4B6Lֺ"og4t@ mM@mݍ>T𚱮ib8d6cLll&qc|-0'3/<~w4\|tzFaɪ{Yנ6t-#Hb3ı8VjXCc1dOT -3oce}~z.hE75L\=5-Ch,I5$so%{sIFMı817v0&;XTVfH3׆A!s++z ))"ö[/:@ndwt/ ıv?~ޗ}S) -kyR{꣯s"!Rt{^sk^nh -Ƃz8K!Lt?I!q8feep#TxplCN.a0UXR|e>oH])a0K$SgX'0ٟq%=y2ղ1@ۏk#VR+{ @^y3xޔT'Y{.o?$ %KE&<{ŋsgW ml}y`}ò{ސ͚:Lm`VKs%O,~ccl:W {ś4썓dŧpO/yC/s /d"oGG,~~ͤyIKLWW^/}_%Կ,jg'ހ Ufyw?6sZ) -:2qӺ{Esxq~&̳gcۼ8m~v|;׉8iM鑶뵵)F=Cݠ$@cL ILHCC􏩈C$PP}~{<$% ݜ73 0(_fѯ=MgP^ O߰y ކ!$=~7V!Rd cse e:#h$>+xyK+Dgt*sB?Lm* у_u]S25t,v#Wȑq?>2S{R#aCdC/6k*< 3ϋJ\;-[Cw6Н@wY4:0 Gt7)T 2d V9-hm[=c0g!X=GG xl'[p3=ѲЄqQϰǦ![[-&v؉c'vة`;fL$GS\VY<:ށ(Na |ayjiȓ*ʝʕ3ݔ=$[愬epf(Hicc{SP2(:x$!(*n?/UK/w6$gGL*)r F%O9s* rg}-ckl@%!4 -AhߌM-]N9K-uma*$MG+],ljj@iCePPo)CН$PnNS!6J@e4U6]?MS'hu>[w4qu:@zJʱ{-hAz<2Lrr®Y~ΚE~A!ah66@A<0ǀfq&m&А ឦ ]` ta/)q ĮQaE{HYaNaV6 3]Qg6{9d7l[ Pb F -*e(P*SS -J/Pʥj-2 ʴ:ڱj 3Hm-ɞt;oel?V~YpYKbr5 c̉ջc,NY{&Μ&38]p~ᣴX,k:gHL6}?ѯ' v ?mI[-~x;gr!q68wsΕΒmQQ]·˨#rs[ 7c?}&{vdVĻH"8sIKi&xA;.Gd##h^e~WN0?HH3(qe3~VpNEj'[ٜ;nG<$H9X< WU~H<^W^ef\. euqDINۿ^p᳹ϏU6K<`,D$+5>>ɿJKb&>f- | -Ol.>IQAaM2z 2zQ{u΢k~8 p ޿z]uq-l$.%~u9Gem~~|?D~bz":'~BiUh -^VXe]SNڟ&hq48Zj%v؝lj~>^n.NC)u}v!~D_v<mv\pǝ;vd`IЈ"v;;eZu&v;#bl/"Vc(p< 4z"%kЙcp_/;muiG:ў؊ @ENA{;ӱ;arXeQÛ rW+b f8S a@䩾";=}ll>B~ *YoaT1v|*8=ط{Lcz\cQlz+۱ݍ>l`o6 ;s:>GNU QuCt~1lEоkپ Tc ~o~;@VdjYdg:YG-e:5c_ ;~σaWuMC,lr2ژT2c^y;u£)TE G7Y.wmkUh9WJ4fy$;B5ur%X| EΊ}ṗs&o/E̻,HK}ܥx#+iժDb񠂉jO˓˝lSMG;lqf܆i I|HbxSGdQh- ϻ|Iy"QX+3SD~ -& y24Xr5 9gϢ)K{caq+X³Yφ$/"\Cedj(fsI>'ݲ=&=#U0?;ӼMvū_nF5#\O&~mXflؒ! ||e6;A+h9/)>O&d\25 -r73D V:HJW xmǶAlcoC%K"K+>|pN+=`hiy׀)ޅ~F5}faX5 ZZ" -nUƱ3h:Z+neJ;=HYB6BIH@P !Ѻ/NT;նK2x:ɇ0p=!?}f^LRpφ`@Vr@G Aw"0<A!\ŜԪX<71 1 '#hGw_C0" 5m ṫ` ",B",BPGbP !BpS/ t3Ϟߧ"$/0` %:BrXa`F6;XApٕVb\r>i:_PK -:G/Ґ9c+.q|h"|X ~5.5uбFl 0a|x=u04.zE4)x C$Hl- yױ;'jn i\ W8tl-бk؎nA pNlEMlaY6{ר` -Y;y80_w97=Ecg@Ҁ= бQR$Ή {P1j` B΃Vݕ Yk`Õ(,7U -U+'F|` - ^EMB@n/+iQ'B/ paT/D;C!XB"0cr>Q88/l0݊M?xy~n07|cǎ0q)SMs^(d^^2l/WYn_zWl۾ܵ{"ވ־o|#G?>3L6ğ=w>1)BY"D-U5ڂ¢CiTSźƦffpvv]|nܼu}ŗ_o~OD%}y1<\'_ gK"0X8d$ D0QPp)#`@L6-F8n#mO@zH(=&c̾dݽz~x FEyy = % G X'$`(,K?W-=C o"[ ;=Qo;p0ȱ4Ï?!Idr -bXwAWM1 0 -z޻}_>xo=z;xɓOkMuDT__ba~CٖsJ:CR Z G#e&\WfHKi h0a@À 4 w|kfdKeUh_ݯAųs94HASe *g)AxӀ n_ToO*HSoTb.W]ޠZA Р%4(ײ3n膆>nE$YL!`*_mԝ/QsР 4y"ySIfuaƹgc,i0,5pCu~S9Ѡriȇ۝+]xWY"Z:ӸdM3^Dv 97V0N6CC4N۝#>1tdBG*@C'ie$5hͥotРUrS!\ʖrz$N:Ҡ#{脆ƒn#Hi КʷkJ -A˱)sNy6K"cwgI=q:E+6 Zg -Uo-/4CTРa;rV(ՕБu9'_4qbf՚ *ʶ̅ڸ|5ǢT۳,8Ȅ#Eƾt^鎗{<6XjwУ-VZzQQYkF}QLVנϋIh4X$&}49߻w?cW{YE˫}?Q -˱lpWDL|rV\`ƉмVmӰi4l6 m{Pdžg0|ǐ0aV]ց灡F!ʺ[Kn۹l{`?)`oh@lǧ"sf\޼-RtɌ)Nm-në= -5e'#1=0htHh#EAg"F Vh•Ibm0;;6 7`2>A :SvIQĢU]1W B% OXoL[n` `Q/c×hޫF'Jcs_+!DtU3(˗vjYy`xN+1™-x[VJf AƻC),ȗfjkۭTkëK/ck$fLGz(6lj;^i<)7m}Uɰw>&t%4aS&Hsĉe!e;l[԰0ݸ/WioƮOW}/>{cI_ᜲks,p!m,g9@Ov.Rgu6A$Ⱥ[5X=ښWǖͯslwrl$&";$&,aqJ'=ʲ[_vwMæaӰi7X?ښc˖9_ 0tJddD'%x:,&rA>'>\0EEh`NӽGWpkz^`x +Wc"R,Bq&<$Lci7_uA[=kV};Ǘ/ b$fǢ* ˱\PW@i.wEfx΁HmjiHW#-]`0(̩ IHL` HwEĴϙqrxsvB@E͌:yn8~ ^I3mfځ6MmҔK IJ qCwI֣yGﻭѾobKl˖%[^ p(t4uU}\?ɩk3Xb?<1{B1 )ʠ)u -e;5+jK״4Œ^S5x{z~q_=a8 ie/ŴxXj(Q@ӨʨVf =[rSPԤtuEhx{~ {/ͩ0/!=k[8P&ڪY V $7yMRULMogn`##4n%ubD@tPf*haTIȚ^ʸ,oe>OUq x -"8g3h.PԗMЬ] U,*WPW2M~K(d+\+x{ڍ^o_=NioYz!pg'ئb -Z(e^ik{dEDUۆa}B{_k_ӜB3sޔJ(6y -%<$iCPMAcqd"mnf:p~0HA\^0K Ì*QJ 82Eg`*)=P3؏6r[h/w`}o羣=\[u᣻nj:|ͶoZp7ȗ|ImKu:mlB%a50as5ޱwDGI^{Ivx\/$ٝh cD,IFIdB#mZ47"TՁ>m3V?1Yiޯ-:B}Ky/eN(^, -юd,A#$9Z6mtoJZmio=aqS5ݾ|OӂSacO0.v8hx'#TQ*LIHLʆt ޜYޖ~0˪a -аm=ć“!A)# xB1B 3QFg2R!@ R`, }owYr6[iì+auc71'R 9#lD}qNܱqZӝNUzuuk@zWEAAP I\Bx $F$@BȅpAEVԺ9;m-ʶ?*9M8bɢv:jh"(VV@ߠTei4EJtLpavwk}n䅜4~1+=n*(NU -<L;sYINiBx6 -_sZfFGܰZ)HB':!TUr_JDot$ H\$\VQ"Fa]|VaG ^j2#(Q6"*r*&!i"$]0 k A]0ݺ4!>DZр/rz[IV-9~`qL45z]ECmdULDD](ՀOICVt^DA$"C V[+{$SL:Q 1hG 5M|CF^kʇZx3UAPi/  n҄di=ۊ~i+zd%C6@>k\OX["d>Еq]iB6gx;iذ% -gd9 $*MM//uxUakfR2ȕ\o`*X( 0,OƤAq.<1*; O[T{j8lQƒ .3&Ba:A8/ W=hS g4IC΢/}ڐ:=kJ]* *8l]Kh-nH6j &_ciS 3Ҁir`xaؚDy]Mݧ 1M&o -Zr-s.j)kjTAAdGO۸7`pHGܤM$Հ!o?f*wm2~\?h2b۩z2lnʯK @1'TYY0FG)2UhӲ4`^2nK֬f{}Vm&pҁ-ZwZܥ5UUz(ԦMjJ m3GrA A%h4 `Z ЭlVy1>g~ |y؟~uG? ӷmHozޯ'|%:WS 8#^87Ѐ`SӏT]=r{L&u~C*gN{i%8 dp?3 x \aheeh jOy`~RMOU!KrUh>Du38lj,J0pzT~ޡ{&`jmյk˦t˸("R(HɼX&QoAqq˓2,ah6EeX=7eNަ ;63e0uOɧ4]jnH"QRĀvߌ帶la,/1 G#Œaܔ>ehi3~1k<ʞ1tȧiPQ'5D^LRMl)l8q(˛G| 0#xeX+)z 9Ys{xJ1?o'ud^H2kq2,9ʄq00|hahe>o옵fϘ+&;jpQNj$ -%h>鵐ifqfb5\Éߊys&``1k{ڦ4vTᢁ/AɩYL"2B5=+ v:̂*;\q`r!=\= ycʚqOZO:ᢉ/),duPbM97Fz\Wjz{Be7&H΋ ( un̬uyP>8Z?]'[E(fjY1)QUoh"^jN^l^$oGs4o-Ҁ28>u9Ƚyhlu^sKO3;(jzIyD. As\5KT1E7w>u>3mu *].NQ!iWcZDX ޲=7B^UtpQ+.hD2-hM;[l'Apd:d;,{OHgpj]<5jT:hCjJ -]QI%d@ [[ߎA;.}߆w[|pRB\G;A-٤}SKUT*K0)!D=eRoh`2xo.cxk{wt#;]ds=c?bv> k6`B:EM{MDZ"VE -Ӽgwo2oM{ ireӇŢ#3PS - }fj;8wym>3tE`uÅzAQlnwG6{xϫkE7]HH ~5_8ɯs뜀gq+>~?>].Lm`=acܜ>"ˑ~RJaiVUXaS/%(\bxa@ @Յ e nH\tzK?Y)ƶX f#fHvuqҨFą^DJ +a]XH:$?y.d_Y«ѶOo~~ZJ^]rrj[Eۛb.A\Ԓwͽ xYbN8ww`{-CplInF'LǬ/F>-/,zTB^O>{.V~1vtnYHI׽{Bc{C: >gώP:}$%_z^US~nˢeϪq%kҔIe?R˒6^L|,Oxri' ޥ^y/ >9}Ǿ+22AnB:@$dPɈğ?Ǐ۠d~u9;3'ܝd}/Ds;d~>O`?T.@WY4v,dG$xPt2\11 ЧO| @<(1>0nN\x??G )eUMuƥ6-k8b#S͢v횮馐J~Ү*`wo2`i(`!8):W@KD|Ъj){g3Wzǫqdq 1>, Ay-"8YhfNS%o_%B)X7oǶ;LyeT;- DA  p[ZT ͷ4zS>KkL7tDa 3fY`l^{j{~8 &x@ ?= -R7 -EUne2^dQDLr9I[M#D%@P؆~?VN8 o @A$o @ (pM@/6,qkًxդfu㍼*d %vk\Cn\ӂ9Xgh ?)lń(9 -R7DkPPqKf9T$Y?. c(w 5A3xی{6gsv` ;llHklԪa *,ђY.I38aOr791fkpoui6ٶ0 ( KJlK-Xo;_*%/K8 P*cK3\iaY< r|^|ǐk2L=>_USI;İ 6mNH OHT$+U=Td웒rl+Z3! 6?9(zI!73`zѯP^e-'ڜ2a@d#LҖ*1:HFמӼ(/J pEHy,pWt:;7 ^)m.3ȷ '=Zs&6qg -6q[ͷOG$$_py"!hgT6! !E f_+Rl.[buũ@36.}"~'>]W6SL - 1f񌒢Su<*qOhfuqi6gAm8%h?w=Oe4Ĕ=1a$P[k匭sH_g7)hv!oFVϷ0&96gtdul`5( _YT8PG]s߉5{4;~elH&{aL0Ejm<,P2|sszl e1- -?N٭s׏oPʝ~w8 JW14Gu'C0VЮ#ԫ%JFWV]R-fE`%la*2 -& 7Ym((C U5XB~dgr[7h~ }hč87w*A?:Lڞ64^or]҆Xѝ&jL/RiYvCA)Tu6Ae} -{48=?pkbPVg(3]BGiK{hnzicgXeTCP T!١} փNt[>59w#;vމ)/)+F $ev+Ӥ(󻒔.RPtSj]Τ -eGrJc(D 5f&P}j-~&swl&n.Yh)YQtвE~Nkbr[iWra;=VCjRic.TڄjP E &P)46_.K{OkVW<>D:Ewa>r:lHd(qm6r[uKT[|ks+AutpP.0Vhaf' ,լR:!]: sep1"@L)FK%tەYݑ@ 29!kZb.zۖ7.nޭY["B>ߝ1cEGC z)?"WWc{5: DUՄ/ -jDA?iW7lZ7ʷ;[%NJd&Dr'IY\hR60r-ʺ6WC`}UI$P,1oDAÖ/V:eņ-`,oY/ݱ)|! 1iTܽشDιt^73h0!-/]6(֣5~c#턉ӗR05nl:CLy! a1Q_sOq!)%5#03g!0̃T2^6:ע4C_XW L: ip='>sCa@Ci4kP z#T=saTؽ;`fVg  ;`xN@vvG! R\!pJCPy8Otغ.̾߄?m?.N8BpDt=~8+[Z!H[Ck#`X 0- - "dl2.b" >c @gaЫ\BXK&=ה%?}*_Ŗ͐iŢIbhX<" JFA0(&~> C e Cfpc/شLVbJ-?k.A7_"NDˊǣ%cƒ1;;AͲ^bYgT2Cb!,OK= yЫ7DvZC&3O&L%Hq1|4JYqZy->i':OJ|C> 1d#LĐ3ѫorٔTÛcM'M$cؚr]0IU=uf# ȮZT!΢<0ZOsjӞqkuQj-"eA` @XB$d%!@VI %$lj@AA VG;ߙuzݼ٦$DbRfw9WiQ^cUT-U3f5URmJ*0P 5ṗƝK@ޱ C? - ;61|3$-!xUF1x&(bJfX,tf(FނOg5p}o1(f|Sv/%V})$;͚F.MeםuEmvC'hQCݢYаsh],^trx77n97Lw@,Ddu,B %k{=eե:uS.uܥʐt*ڿB۷/7&V,tOmx} o*<^DAxbyލ0>P,8OkĸDT6.HO:{9F#OV{xAW~%=3ϭ/?ulmWۂ%/=J=:U|?HdeP2дpy7g3w{jd8⇃ȀX <&(Gdl1?Ƞʨ13?3vjjqBn8J:j`G'`21| ;7`&oPh1G a}C )ȁedD#O/6 P{]䈪F (䠀Kc.#KqgKhpu?ŀ׊@ؿtAC}"c_zAW;(v@ہ;\BPn  :w#-ya~ C'z6 UC_ - B 9t ;{p?*NN& n -nlw p?8_QC< -Lq;FVk)+>eRƜ%Y8ωgz4Q0kMa?M47q1콌!} Xu;1pC:b`!7Ey!%x„LiRK33oT-"֋2$+Ill2_;$'I$ʻ厐7Fz, \ GN-M"EǚT`R%~BL&6.dN(&pG~H988l' +]mE P7ȌE2&GrpI/9iγ"Szx2*}L|DjP'^81Nh~ʾ}8K ii1U vp9l Z$N0gy4x2L6AT'f=$7< Kl#&s)' /S՗@ A -N*1hb d| Q&O%xΗL(Ɠ+jU) QS4w75}M{Ҁ6D6%h'h ĈADA pm|("F-lTže 'Z88kaVmFwII7 -i~~~}FY;A2 Πq@PB ^WfΔT! sF.JsѯzJrИk8W\+e^_4 1b ,oB! APw}A"NUqSJxBrR9aC۴s%Ime]+nnYfSV)) !cHɽ_oCP% I/ ֔J zP*5aniԚ>Z*|a98fkz.7q{ʹ=O@dA (F0aDY0H R'uJP - ;-ִWSXmzNf+2~D]nt1k%~fo2 0~Py]܊?K -ՉLMeQkj\rU[kתKmVHaыLzqWb1CO@s0 -&߷uasQOԑLe-ZyUqR+ -Ygԕ[j2ZkkU6NQt.bA&b#VgL{BPz7CF7}V3GvHwVeU+mŲ.5[4my6kR-4UN#rH|jx>A2 91PRo<݂x.NW@Ʋ5΅ʃvz!0$lŜ KHH"N_Ԥy=Hzg04Ay,Ey٬,G} "}bg}OXeeK'!vD _0Yǩo"ȋąs^kJ86׍z99`t2~@2ȓCByvK߿靐E?)ԯ&X׺5\L^sv:F"ed? ƿK \⇻)t{]ue5yn4nq2ueI 1@&d tGeɍRR؞Z`nvb, S!O" -Hu rK}*e:.װ~vxcOѥ$Z"oieLMoʲ@[ F{^ ؙΜ.zD{@,D۵rZ ?8rD݁A bfL6lL0V;f`Kdp3% d 7 l+Gq@#[8ko G-x -,=j] bOrT!H4dT2-pSbj'tC>ZMISs?Ç k -LDFr$j@#H$C!ױAU&46Aw'(vGUNkp+o5SB!JbD}ӃP*CD}qIE3 aQ*qGt7Z#`&gV[VpV0wEJz@٦ }}/DІ.ݐr%`U 0j(6 -pUa/S 1f-u%o/&|E@j R|iA -~9_y" -c>CzϐBT0Bh2@EjpB e(;`uzP/R e@SWI-A+vw>o/e<{g@|˚]b={ǖ lMi24kp/70D'^' RʚBka~mg}#|%#3a&ϰ&5==-:+ZQԣuTD+ʅuBf! H,'$!Ҡ("e(U(Lx@e(λO}s7i /l>BG/`X/Ш[ DՄ.3#6'=0] 3ĉjқ:kci!i{JFӚ0#NI@Z -݀xr 9{"=qH{\v[laSBzYF -Hz1|`D>e1̦X 5Q5P7y7@?H @O< qzܻ,\>5F})b_d < y`ۣpnapE?tݦ,p89 ٹi$,~'<=E3ch/qǘcӬ*h䥄gx=?1x~M\!_;_[ 8> yȷ/5 Yt Ac|bIo#e\=;0 cÑ͢GV\_͘>؇:Cɹ>q%y?h] zjPo4L A f~ 'J8=leC5Q QI^M|or=񁬊@vܛ|ܛ,`:jp!ul,Ap#@䐏bv/f<#|`l \QރR܎V^N9OJtQ'i= -G,`Ow& iנ8 `ڹ} 3 ѻkJ&DD0 GMIT: wc;rjޑnct3:S ])lG en G `2w, oo~g1Ag[$KiPyRT'5kkCWlǷiYjl|(9Uѱrfr% 503o':M,s&[W8nR)UK]^6a֖ 6X~%dgEl|AWIg)E - b K1F|q B̳(V=1mxCY0;̂c&εk\,č `rlLjxcWʴ|Yu6NQaK:|a6.ݮX:ҝbMf*7CIC<\:W{}w/<صSS~ՍuquDPQT(bIl$$,D*0:ŒZ;NZ:nǵZP*2)UdK9}m^|^K.7VzaZjBK5}F_\c<\mzGiafӛ0ܻ=|j|4쳨Ǟ$MW?l{I]voqf"k[եm+UnzZh|:^Eh[m[?QIT"bŋxFR.p\T*m?;1te!WrΉDyjx,k#]!ԳQ>ňX&gk *Y>cȎcd%rQ)#5Ңq+QhG3bwF-!?&H#!EjZQq_qY_iRH #ܰ8΋ŊhM\ sp1nq9fG!~%d͠3Y /RLtFkӡ\Ob ICo2 : Ʃ:KayU4c&ϜBp,4? #G2_%dBR+>a.| sxF=qs@ ݄Y0)։AXISQ-~bOqp?;"s;TR4HH6•%t0 `Hp\"b4GvnM-13Vw_,Q1_@? `g]!gCzztPh -á.r=3'CM*${yCBEXtY m Rw26MV/z/钼vH?i3 lhS`¨DFf(Ь\_ܜvCrH1D%3O ;r,jߥh@aEvy7;S0 A1lz, -8HA6 MPnK|bH- z9DWUB𘂠z'~٨]BfoU A %@ǰlr2p`^cI<BW(w8 V)%$uWT5!zJ _6+_(ltrH e&f|U7h2}`t06 -cP2A J$7?OCj!L0lSAG~DuAYgV\7?QtR6?I:?K 94d0 \`Qr$TOCl6Vh%o eLpq__ӫڣI7?k~"-ցjWuDd !I 2 hQP(ThI ǭ{{̋yy~y$A'b*37EmJO%\OŚx4C  b'iݑ/f F}KF-%:v22vfAi:Oǡs=_H`0Z:*J?,m: 20% qqChmݨ6foT?'j݆49u NU<*А^ _b`406YAP24]f2e\w|D x~j&TxXp%=6s@4j rѐǓ -) [`bc1` i,p<f;/_ -|A;sT!5஘I 7X- eI$->CX?\Ij(cO3 4#76N0 Zd{߽\ml׷m#šC.9 !ƶ˜LV]Q[j6,KeDŽ =<Àd0 x9h@ZjKf{p?pjw˓S?+<ڕߡcSX8Z-PKj~!Bl0{R2Y:=,VGr=/mDP\s`z[k sBfjv,t^<{ j]7wZu@E מVET$xb%Rν)S $"B˸D5ŕhڷxHGz,߾ோ;^5YovYcS%]7+Îj~jrXUPPl,S.)Du2qrgH\&餢aH8, DO7"@@*,XSiy}-z.h umǟѨ1yHJ%e+f% b~jږʑ!K餈tXHFy1_d 9i9%FWa`FN֏oU6>\w1ҧ"6TU"Oe!<32%Q*f<%Ii#b|TȖ 8)GjD́dtm-,_tmkŃ]_t_w]|`eDAmLpfV"tnKR%q)yI㲇%dՈznLHK -B@ -6X֬6c7WG0}wv]:֋5-a9AZRNV -T#$Jđ%"\hrLǟ7J#rn<[%/sڥY-xg ~5=?Xt,S~gZxB/sI$4IŎ gj/C5z*4 F.!gCȚ0 -Em-xlۀl@З}pƁ}U7ܭ>"Ϳ{IŒ81k5Rji`MK vXQdbF0 v<[_o7l@陣UeEmz]~?hn/$%8vC2]$ow/4WԀWKկh!Ab;,å` -tYk24cGfMcݬ?Q }#ف!'Gz6⼆pq^o 7}:Y0y!`XNKg j,eUL9or^!p]/?4$BQ.X=㴞0&+Am;2]>0GzbL;Z hk ,A}kPdk-[me{Vg]1f=Ϝt{jx&{9:jo|}{׉ϾGt~;߁pF:0Yc>:̓|ޖy9ӡ7Fy:-p.]gQMy? q -.,* l!!!{ I 7kKGwKU#-X+:uA=zL[8 -B|潚|w]=hil*5{.]0wp3GN RqU"֘[>asbOn"){>G6bڸ-Gx}HY|HC4ЄaX(AQ> a@TNq Gq2͓$ߡ(2)*%`8z dE!; qL.}6D3e|4|Es262'aqh/Ȣhf3 2* (\GAi,; <As -Ru t:3ALd> 1y -J ' JCʀÄF KTaP-!DXK/ldAV'ɺ.g Ivg|[xbd=xM4d'ѡ`1IgB'^9pGCI<ے!ٟ -tNf@x&v.Ywg!>Y/yB t&xCȀ. &E [D(@/8nBܖ>BE<C!ρ ُQx /(#hPy#o1&BPPCUꓠ4 ʝ =GBH#3 KGR9 &'}HNJ1&QOn=[}KAݝ <Ϡ#4>(:qLT}å -A1(Iy -|v{8TgP^RWhʟk4Owyw:?.)4½a#*}P23L}*QhAd$?ҵj}jzoW ˦QӅQ9g0"7x&XśU@|e渱jGʰs)wtuV+neEc88ᑾx_~aKyrpf.l=tГ|{]Ċ:&N'ؐ=ա#1+mWU]GF&K_ -n[nZd(0[mmECSC-_zl/yAo"ؔ-Y#zY[|%+p2\+9TcqK?gK:-;,J/Y_8Z4h 8NJ),9yL~#d+ȷ.ͱLlK2ȟ9( vmpo]_JSMk{As_%Q{k7%γfGpYeM>'( dȾWOz4̣a[4;Yp؛=n[m .ѕ++ۗn)ztAGd9׉+eU|Yy+׾ʾݮ~.'0FfQC5&2%?1Ad袻[~mC?h9|{ɉǪ]]mK:j\]Etm_Wly8yƟ8H%CESf_˖889v!5dl!ҴeFiK4L^XYA@3AZ6]MDj+.;fw9&G7%ƞgTF.8M$, -%tIIlb樒I^֥N{:+vxof:4 kRe i"anH^lYXVt/#\Ԉ 5=/%z*"9z&,9j649j>$)j%=֓0{"_B4{YS.uEp@ -k%Y5_qOfKf|Pw .F -&BWLxYN\;.v% -#<{+UͤHߴzrLNM~jK -ODdg%222YI„)x䇑 ~d7*a:<:~7ǎ.DDaDrxY~nSћjᮽ&ʷmZ_s2P"wZ~ܙ *d 8ᇧOq#Rgy)~[& `A O_B'=q/n&yd,@؆%`mY`Yn`ug=w4{@7|I:H5 ?BHI t`{R"n>|bf/s/m!?삐OV"xF`'!,ɹ 0z}OX ҂Ag,7{Ɇ_g"D.ǃ -QvGlYMtBt"s+]*W5Fh+ !:i__#;?=G+b `>7ҁO=3@$fAb"h%[WWGmtp:f}6aי D @+5zq$X?r'j"Du"֕ -S g8@> JdHJ[Q+<: D3q,]bk,d;2{!8?Ds3듀UHXAPAK -},N&-*unH2 _x+lƴEwÆ؃Q7Q9/9}pŀw3Wq>&!?{ԯZ{d>@V#֊ArArUU=,7J$6^Z^%s^[%*7!q+C;Q 8/DN&A-d_Ɠ|Ň-֑{@w. …lٲt[R["WQT;KRgIO{[7c! qe#C1$WLhb- -#G4g _4egy?YH_κs[+▲%kҞ+o.J{IEeW@ܩj$>đ|)֑6UTN-g7G8/yZ\ИNn}%7,ܫQ=V!Jy27ңv[V-@g_Bidg'=6M%sz_e_- ~6K]nt^7 -r 9戞;O?O9$w&8|[ٮ]ٖ2h[ͩ㲦ԷƴwI dgQ@zlZhRjwZOkCf>VEuv$ٳ!}*$\KlWv#Ir8}`ZjMk귚}#ꆵVE}Ƹ|{[)!yDmH@6o<l&} ԭmݣFyN$,P}U.+*wWdS6g4e6d\Kٙ٫NQdsqYUDH$[G dΥ‘2VrG6O]m5n6;^.{vW6g?h䷙6 -[ -+eyU; jks?լ}0RiN0-1VU0.{$mJ l޲T͡ p<߽Vԫ{58xthWflYWf6nIY\#-lTWO0vZn|Z^03 iMqTU?(˷y{)L|28k݃(7x_h {YGՌF6Z -Ě*yeNfSkʦԒ4Sb:ST41L a&.&{S͠|>rǔmݭ%"J};uʍbBf\.1M),,ոLZ^ُĀ>ӐX:)(UƔLV&Bٜ3(CU沧iFuh:'ʿ۝j[W[Ģx=rzSS -nW&./fkIiViqUX٬5X9SY׺-CuyTe4\ѪuMBXEAaIXE@0qWzZD -REAPAܵEܗ#n=3v -cNUԞ,gg|~zy}?ѐf͂1=ŧoA4ӵV+ok2?mW{$QRYk+;.b}˶S"{qIyy%w,>{I@m˶\6E~у*!ݮ3FtmuM原Tյh'ly}OqOj# Ǭ;&a)*>K_X?+w᜜}md}=@V^`O2w  Y٧DN6 u1ֳ.3&sՒ"/jT6慮;TnuÛf=,=sӪo2/ UYeCswFRևD"_IUǧ M%S,\RU\,=㰽CQ>wݩy'G,iY5-yc\vSѬc{SkRNo / Æ/?R>*FGRGCo#zTFtb=tG_]ҡkT%^ 1MmDd+/d/>08g6;>'^:1U>>f6#9(TѰ臝Dw]۽j/qTyÈM{\]ۑފ_q3m,k |VS\1s6zڌ1יӣ vyŴ#>3D]!h`?Utr뮈ӖO}[8:>˼&<ت};hVFByCx]DFvAu:yDgD7#jnfʯӖ"kNkzżr =ZkCO]JOxVcz>Fȵ=U͊t2T8w(C@u752ω.4>/N͈V/y/eTFWfɯfOxdחa3/N׷!oc.܂M |{FD7$/!5Z!Dul+Xvv'_=7-)_3{p~jZxY4C -UClw~d5IJAlbY?hGXaD|K#Q;#JÎ7n:Z(3 -BHc?d`l.ATVK\_0l_Lj*P5˿C)EpVCԿ.4YEjE( "A% #@#r A("HM׫XVG+VWZ]gߝ/g|g]ך$i VcjD0!D -hzG[Cq n@=_\r}As}F} -ns[x -ϫAy9*Φ9|f9DY@DB(KD*׌F!.mz?2a4;Na1vk -ZC狰oR # ~H{/px*ٽ_ -LJjٰb׻ͷ=o:~y_#!|\qw| $|ÃQ>P@)wusW`Qn2#5hyR/ף5n3Q-߇/5uM  -N :!x\$hB6&P(APo8.S3)mOEHd`\iXf6iK'Ed Rtv阽';' :>|$l*@zg!U 4S V, =vS^jR -\g [ͨ.Ǭ="w99)xOHKU|%i t D0^y(ewE&:bh F 0$@@)=Į%Ωs?A şS~+[ovlLqɥgr"2.GRIZYEࡄc|;+#vl6Knsc$SA -j)0@7b-ǮȳCcSSfz3%쥓a㹱.#->J ;,3*o&e=d}06ߐp]PW%n 8r r`d0q-=-@Ѝ}M>*g./.qL'꒮O+IX") ]E7!=*nFgfONTF*=ERώ\>fP陕}z;D/*'Dˡ9a~5i(akRe --D}/ -=˷Duz|o.5-Bg7߿f6x@ wqo]GSI:mu~nG߶a6޲z1hQoge!̩R^[.*KחkUM/+(L U~P^^Z6j`0pXWwT hu:yMt52-&bEKh}]m[UM6]e_Q*P+K+njQ@ɵgCe"y;B;9S w!!tC}fh@nj ՔUOguUMbW]CVm7ϐNԱu/ D{X[~|pL[V)DBwNc=fh rͲ]5gm[Gn˞YʆܠRzBNQH~T -Ș -Ht@ĖZYpـp{C |i/CC._-+aNn݉[S;mŴݭՌV"0G)js23^;B|3$toL>,u'{RFj+E^O?dr7 N07]X!@*Bw]Ad Bc _ݤt{+k/7ZT_ks76mDna-r[;~cx|D_|J>KˎEћԂEG->v8T)Nв@]n|;)T{s%35q0Ͷm@yW5;dd&GyS-<D6zvc_֍Yco,dYbjmt"\8\ۅHMkD Ds;^ ,4㹼~ocd 8= TxV{ .\;vhH5mL¯.CwC׏ma3>^gsX~G[BQ(e>*  MCraxayFc xGaw$xKp' l`3vog&_$*BM# |Ʉ@CBZ(( *.\,\ xH` X&c ࠇW!fpU3+l?D"\" Hų: Ix -C =q?/8T 籎簝'c??g5|M˾Erb(xS(b -DZDhĒT /j!8K"f5SdZm$=m2] -{ --HEbfy"z} ];ҏ|!iү 9ꏨbD2wa1xd] ԠkyXzLVG'zB9 q h( F|?b2 ?ɜgfn3~_r -B,#dX,TzGPA}1a4{W#"f2ς友#; @vȨAKH?0q}5HpvE,UO ɯ)cI -n e@t 1W͈1Ҵʀ﫧4OmbEۄ?+[+M:VHiPv}>dj3q]3r57`g0o/iK9XߎM9#sdkQ5nBN y\8 <; ?QB+ y#p!uNxʶ [Ÿ] X&wg<%ݫ:0/<8S6|n:9@틼H뉸Axh|KD~F!ZS4.y} - -|&t3I l}#fr+Ȧ0k4f,9nD$s& J{jUwQ1k n$o<.x:rVȖQF"vIv$5 -Jst0k울 NeNEOU{JX( Z0D] -(ަi0E&pJהFߍyǷ ʣl2v2&%ݵI ť3ɵD K%)^U - -/ -Es -!Bh`/ {o. -c2{WTKEV}9{[I rU:]M/6 %}_7[͖7[|ĒC_dD[ :U7JHu!ܪ5*5LNe莖˜=jС&K<\YH)ʨ+d nQnz 1!Y*bRSv10x{J.7[$5; לvU< uSTbt<%7GEϒ׳dYa$8̯~Lđd"412D -Xp;O눠kXMaщԭq-5ǷUWFRW%TVzeRkYE;')O'̝{/!s[Y)(J"j& pk0hkZ1i8f .ZU*+{H˔Ԥj<|/_|b +.1]$[=gp{W#vVvYB{>bc'ٸQ9jU#'!@jYR.:S%񫚙'+|*'88|"*;R%S"h5[KLqf`34&w3T1Lz-#6-.Y(l5+ȼ&WdC#- n -Va#FpV#ZX+*_ͿE{Wp ``#6ფ!ly +@N{Ss\»JC:՞A=q;mAԣ͈zL(Auy{oq`w0@-vвuq1Q -q/xl#GN *v:s9>Վiq\r@ o/"s;ٿ}52GpsgN kdӻ iWRX0o39jUmW;'2w(tێLݒc} 9. ra ut 4|$@MH3v;b=IQ>as7[MΦ[sf -fjvg:`Kږ:duȎ1{\E+WwA'@?@ίXΟH m!f[Bਞ_l쫏^'1)i}g6Ky+wVn|8x8]Mh_ο-3'pC"HvY(9yѡY&/J9hZru3W/~,=A}ny;P gD.~gЗL{(m# a!: 5px7?ՙSa20 f`FP"JQ,X"q%Uc jtE=.Y{uƵG"%( -!;O}}'~$~0Ofh#v^R+uBW e{; F;m_ x(6Q}اD֍"j)]5GPps`|(|H?-"")bϏ߈5X/v~nH>6J-߳* .C4'DD8?( - - А:H>0ZArCOY -yJLX R`Ev%,M4/q-T{cDAD 38Ӆ㡽.Cw&]mqm{w'♯E^d֬QSzɫly]jyh'P=9]}GK4wV{Ju#qg|&xBSFӉПHD1v( Cjxm#TFtfNLPɮ+( }߆}fDTDDYaVePYM*X&FM0.59Ѵ1ihKs޼[ą3r { ʏ2hnڒ۪1Sb_ǯ*Ҫ=RDna_Y9sMF"",MB0R߯iPQt &VX) wj+\ټwIl徼Tʜl~Yv)(NBQj& -S(Xlaᮔ^;4>#80Pk=uL{Ӽ/xE}ZhBg./c$18%#p0U$MK]O=O>d(NGQb. -w`1JD}P:}'ih`A=hcZU4u kbMeՉV9iҊ~-FX_r'N>++D8E; -QB`4ԃs5ԃz{vH[Cje-ZEM+c-$u))Y$TzU7 Uxm];xs6pk -bJsS 5PH3@/*Ʌ.3rev.+k_ٶ0Ӥ{,wdh9(w辩KpBr_:lEX z.,^.Vô6T~GK5=Z)GvMw[n̳>\Q缮kD{xv;a="zNϤB 4MC rfh a]';m$gxF[bFl6_7 o7䴺)AU輺ɡQA5h8AzvV,Ns!eL83 Gx*NgLбB㐱Um -kpooȱ>^AwP~1?OH1Łi=3LL{յ3OǨޥzZtnT!ACӷyFsh"D3\p-Ds8I?DMy`%6U" lBgE b eJ2L^U++fMOe?Y-k7g]ew+bG)F)O+a5Xs\3 )ς@x+܊f֟btRk(j/˔? 'ODT up~ `$lF򙔱xV2eы,?xO{*PuAo_t?_#?%7j`X~|0^@0WANx絔Ahieޞ`og?hΓ|9g|Ht7B|{`'  zh%hp440ppX%B0H1Bo FʗRQ>= X=Q[LɅCy+)hEˉH #[!`|E~\BAYpS8RB7(ˉ -ro }bL x`B/Hb͇C<hƠ3̕A#z jAM,H`Z&)&5t>2L$U)}~D^ KK0hȠ ]̝ACo l`rI$! 2A%r|INeJvv :2hOZ1[•XB\RJj٨B: Bw,\'u}GEugqSFA"3u -DPAd230 ",BK5ZWcM=hbY-b'su߻}9(zy'V&q_ Nq%]ev^Hihde-r8hQA:'hE"[|}mqBLb?ǖ( zŨ-,rw( e}ow?$kxo%7WCgҋ_w?=߷{'+E;oKQܒ(['e8s21E3fNPxpz]8oW.Z ?Y̬ Y 0/2]7\ -g'\e -/p@w$@/#@oZP/^z~>+]}A&ݙ;U'Eb;w>3_q)0JƧ(:@38]z~@Iw}҆<4{~ެ>;ܛs\Z&Uٳg7'dY>=x5qχ&G<ޚ~f

#z}b!\ C a ZdC_E yN68=qh~y&sL?ݢ?`xOn>A]gwd-MwN6]V@A`Wal-pM9G2p:ҋ},b>H.p ,ݨ?$Ev/6߹r{Z6A[K:K7]`'QkԱO/&f~e%<疈JGT؃q=ѱ{#4=]7nmtۯ6lM%YK#٪w͡hOPc8O7cq>_'d$8,d_۝P=>Ұ;.AԵ$lSlEGtmMֈ6eY˩1sC9z:N(#5hWұ0e7gRYp" S'g67c{g7'upKJFu=1Ŭ-![ܪYĕ6/Yn"UVѩ6̥2+yy]7Li :Ƣ8н}I ڍ0۔)oS1ņ,؛m ;s䬞l/^g\Pu1$U)&uMCR.־:acE|sejkQ)Wjvţ3q$2 -ÍxAe Z!3|gVglnG^[΢ DY f itMuZ<ʾ$ɱHѩII'ܴI7r/Z52ĉȴI0.x82LcTe} -AO)tX6eiʟPj=VٵuZaIBC]U(ReS*,˶I+-K5;w01E]#.BdSc -PFF 9Pg?\Nay4;ʛfq+ Fuj,ĚqҘFYdNʊmLXŠKhLX9:RXU[<^H}ݍkW J8 -(8g6NZ`jNmzN?f`afnMPEESkٺn]6eyZ(*X -)JYYػہΞ;0}'MZB׋ǽ2-c$)nJjG%W?ō'=vpUB`J56<ցYki3d^S`gꪉ~E+߷bz + |NXc.tsȥձK,i)X,1$f=baoy-~KU^)5cFi(ޔmJצJGxiqoMnx$p̆; .X$lhIix^IUDnIcDܵZ"sVIdގ5^u+7r~v'l3`Jy*qEX[Qsl$S}Fna)kֹ9[V̭3ʮ؇-%$}0=5P-gťʁi\&TwWQXJ(W wݣwy2df3]/ӪKR\;-] lI6h )wHp8_\ɞ:P;`yVCNdQ7F׍j)3u{&կro7$1T(c1f`6ɝ.`2Wûùf6hXt$ G<gSFcwAUQ˴2-Z-~ˣQ;"ijro`R?PTY@Ƈ& cO!g|&_$#%;`?;}MCO"h-ݰ} `;+BgDi#3~n`k/b݅ F I'3@9=.ak[,m03Lv^NOй^6Am?tuނvU*3N5?evSO Hflo|oa1:w4;pPA7 -`s̟ɹ2;ك?e[V`'` x@7BdNqL9ćᇡ7\.,P.W/{rg̎ X>̽o,v$'ehB| CG{"$(C iJ0~OzJclr}jO][B 9 <9Sb(T/yf(ў:-TDA@'/R'yN[ߛ3?;nD$_}š-&¸P9U^x<~4^.0#;ߟi%G\ )PaI6Re١Ԫ֏ k괶MM6ѮAbN} :F9UrꧠǕiE`_PKufT :kA+i_ !7!q6Tt-? A$b@k"q$>ǫPZ%vٱDX}ب]ti;֨ڹS+D7Lj:##ݢ{-T3$88t%|t$ˉWӵ ki-Η=>wqڹ\wYsssy6%6{6&]jH`T$>5@| q4Ay@+#Wӝt[ZF⋴dډS5?gcb)+ )yLeKgMi4Hm5M'UvSUX*iIXgk{YjveVc5 Sհ|w cemyUWo5+ o" JbZE( K!@k@E(޸junkn۱vvt;ad?=s9s߰NJbMH k) ^ ك{x s%' 0!n%&,%^JR/5|ϹR3qS։ةPG2{4!xW!s΀e$ kg|¾Ct+J\V卵WI*9}V8=0MTL$[ƒۘI=!CCY=2/.H]r³ זDظTuYc繥ΕAt_fMMtfv<gTF0즎Їyj^]w!S[lϩ mn6gu4Caͤ&s>*Ie#YBCDHYCB>9Ήװ{^.p!g 0e b GP5&0z -ޝ,}`k~ I_Zȭusf털\;')Yh?P[xJ$  |s×jߢ7 A R7 -`LRʢܺKeM - "]`Ȭ3VVs͆v~YQaIH?+)/n(|+)1"4#Ucpу. {F[UQyнŜX[W]_]j6BJj9%m|cqP4*Ht+rޠ5~#0t`aB 8Y0O0{Ͳny\VQS -(2UXEj/-唞ה^dޗd3MD1AJ^W%fA=X4By#45Zѫ ޥ~E@C]S_kͭif!azSz;\Yu:\YHUITf"P _]AxkC?4 -`Cz'f,@w -;kW j0\Ž-nؾ$mˉuY [uMeW/ة)ZxM* u]xpNA{&q38;p;@57h~D@t[ۛ NDn^>pW BCȃz`uP y2cc}8ܻy3itu` cOx>>ޏ;x}~lFຕ@Cq \֥)bJr:ɣP-g< <ܗ\;JܖᦼUp8^E' 霽:'8^vMm -,U)Q٬jifM~/-߿-4˩ŸS۟*p-lQ犓|P:Ma(UOUϰfRn1MPm6MWf7 -l0Ԭ7m\keYb׭Vh %? Z+jslgXgzj~:J[EJ,6PnLW . )lڜk\]n^bԼfy\d\h,7W9aSs\ Nq+H -eu-??;w -WtX1QcJejtȴ* -OY4KTh;7h.?~vP}^P}n#~zБ]N-:3.mKvʺ{:+=TFiXCEqYZX, -SvfU6zY_L.4W:~Frǜ !{vziBЏdO%⹷7ubM7gjHwP,,ΏL떢u͌lsdvq);|a\NwYo _G=97Y#Y.{{3~,K`E=^&W{^VocvJ4yRp }بR=9$A_ٍCf =s c;eH~kZLtNr"}zpppc-4CJbe6%%ppj\&#}YI %)֘ꌉ!;_3T#R4b JIOde7 1P,,.V:,UHA@*`-k1Xb]QQD#UѱrԊ:k+ڙs@wŤ,F/(GFWύ8;jSxTQWc(a>_# }xk+$|dm8IZ%BN(If4-yYrR"!1ba\eLBUt|M,9"V6:p kv - A>0^舶Kgųf] ޹>-)9;r=$eѹ~Ȝ9aّʰ4$Khz: w=}lIV|(fYb.sFx <%!e3˦˂KQ~-'-Vy[M(Yc^IWؒSڎ]*lH!)6=g;ؖm^!I.I}*$BP# `hKWjlҪP3yU UeXxUYRzVnQyTWW+>j -a^c{s2|s@鎭WU[` |7q8P3kH̐ Y I{6+1n2w55w1lmxk:VXX\s;}FZ:K+* <moԪYG]׏[\?Mx,i+q1K6HVȆjdCLN2T+䃶^7τ={tW -MDofm]2 kPO  3CwǀPosc6.C}$NKE%q\[Hv l#z,za ˞u?0 &5M:0h`<c=F`ӒrXBz\U3X>"$d382;s `. 00(лв]:!e -mv0o E2 -N?!kvN}'5) i{M'܋HDrA..iT5/Z\/_\JyC2h/`pB/뭐yO33OW:赦;X_*8kx!v7\[cی@77,]N)KOgͣp4x0mځ=jz/ȏI~"r~T<</qC.נ(++7&F,(,ȲܖEvvrY˂+  ".!xCEh&Fmc6If:i:MSM[vڴ}z<_9y>|e >X6e7pmŕOK\@$ dXqu,xFVe -*U-])[kkݵMp={aj1drrr_w~ko7CfC $r"CkKGmoWkqKp/4 nRZ.GRZpP9E;}VC)g~֬(b}Bq}Lq==WΑHH둄CHW ׇG17r}G͛`!:)3aNi(-)>)wfi^Qg2z{88w}Hca kl!Mw07ߟWЧ>(U Qϊ귙.=CӞOQ[2 $<%b޿{?@ωlsc9ʅ49Lføv33 @fkזs5ތF~OF-L/jOJ[>})iNؕND"BWO_zp}b0L -&tRݱp@Gt>ի/`wg[]6^g@ێ֬@wV?Ӓt3Fݼ^wKZVw#ơ#"$9p7\G߷`=` -ci`@J0C1)Q0󊸞<+ߝ[ВrE 9{NÈaޒWޕ m'2H1D>O1wW9K(D}7 -A) iN3X&{m.,5V4 -ZE5=!8)Ae_HSGD瘃[xqz~\__z_ΒhlViI]lvcI>Yb9Jl5N-,+̃RaYLPZIXn6iH; \>b';(}-ügyQۼxQ}z ?jXxc^.=.vv)jdҐ0@+w(RV, Ư\2ZBm6^V{Nr1糨{{i'҈ߕ>j@k<ɃȣP]S!> kjX?7vy@E}eaOp}P, -(q]ՠƂADET,NPXh{,G$1qu]{Xۏ{@xgygΑڛ%_>`Q2l]f(2C/)멷4y赌A.| b38~Z9P rxë;<+"Q1ír\\p4éUp,2!9V3yLYǻH?RO VF*gS݀cju#`WDak261ZCcIڲ*K%\@]+!=bԝC݉Eݸr6ԯ_ȠAVh6#GdeYPV: S^ jO-Pwm.߃k=?CIl3Yw8ߕF6eل\dikbR5љ&+"CV!V`zmDQ7+|; R@.Wtll]> 7 Lb|II}g'&w!h!y6N(F{;Q׋]# DuOrLhv/C?[7lO 1yI#_ҐWhv<xռmּExD3=桍i<,`!Pqk6@kA? $#dYM6RDJvRK!u/+~xI!쨭PpW;H32$t䐍dur -.2i.'WG ƙ5H?2|B>N"u9RkC:k%2SVo>~CG7A8RWm! -GzjXjMf|tX@Tjds"@# ~I p'4q7F \hK_hZG9&ۇGx}Lԙ0&He%rM8O_ŠS 8tZ%#R9SThgG8A'5qU˲h%|:bN+qJ'98̃UYê -4jpHՀ&|W2cjAxQeNW^/'7~}6}pV7lGX3`?`8nsWu2:AC=84aT9F@YBz7ˈn.yJ\C;N;tQwðԹGT{$aL敃*|Tx{JHfKi -IA3!!Z=k, `;孁Pg} lʎcPR(bdl HVJ TX)Iy'e~LY֐՝FRK03Ov@ol=P4[Gas8OgHy!s!) !!9!5!U!쐣eOKC"#TțY?]8iG,=c~3XP7la(<`G`q8AZc"[eLeʳƕLFEq2ݸS^~EX(\(I< ԝn_>|r8nU =+LXcFie%-7e&2Lt\E)EjZL1S-0FlSϏ8gj1=6 ,Pe s :W|j -{Kھ>XX? -#e&5E\F3+Pydvf>6#hE ()Tm(O|ǧJx bca@OdFwƒ0XiQGdcNR̎LNNγ]mMV71.!Fh*a`+"}ccbuX2qH &̏(͍͎)K#"q4!SR4VhuGEyafav .D&葨((("0 ̌ (qh]\Q0.cMh4rZ=&Ic\kmm&A;8}}yIirjIjI4j{'JxU?3~F[6a>(ѠԦ*CPnH t$=WV^PVOW5MߡYgMYHc֋*^TZRES.qaQؑlZVudD9TfCi*LiPlJN]Y(_Yeƕjq&˸KΘqZqC&#CT ҏ}mf`69x%RuԘTfbK0ʬcQbBA>dbJxyhI%){rs~0AZy(R+‘R9HLT I\E4L6-U]ު^WY>J"r,JB2`y)PK8]LWA߳H^FB@̯}a瞂hĺ0ǝ"ĸ3ndX宖Ns/nO}M><<P{ - ~u@7hYGo ڥŠ$;Fc@G8;#\<өG] - <M hw=n];G;65+P`0^ہN``~ jCp(C!EAeaqC1}C"? 6je6րv1.Ao8]@8B{|a#hB>n~psynu󘈿+27ԝXg&Qs459=@{?0# pZM3lF{p3,?gyٟ!a{(pm>/д.d/`=fC70ԧ'J"H5K\~¿ƍ b^?EnD|B]k4RCIX= -(z%-BR&kOm?rw޸p0>&?62j4hGLAIþxq1GxPR*Ǎ+GsMԝPS20l<@?F-5Aո޸5ZxWXwq+0"<⢤MT8UKƱs qW\ %uW7hZpYӉQ\ňv ¸C8? ΍3n&<ĉIdGoN~:G ӊx0n11W&%atrF&0- _NI~GH) -^?`ST!|:lG0V#ӝ84ߛш3۱j웹 Cv`p>ݳ10v%%U'8V? _LHjȹ{<3&̬)8>'$r&cp{T`: 5cgP'vö~ak?|^ .l ->/څCք|/@܎FcyG92]ұcq6-.Rlw/# û º]bO~qui;X\/=R}F4XLk6c9 b r&G/Ė$W|ٱVW.jձ*]إ[/vI!-;*ޕb$7SjU=c;3Ҙ?ov$/޸ذ, -Xn}+Ra%=W.H Ƅ'NQ?RjjWr^\ Ekp4riӊ 2)I~<'yNGWJVRn0͐/BBl4ԉC6 rUe8T.j* w4eߓ(N*;STu˯lU' j^,^h71nFȘyP"\ֹ-B-΂d,NJ`/( -bAAdHyղ֯dۆ4fi,5L}2dZU%3_S11׫=W̽H;Xx:O#c㳪EpFIB81(rБ-6!Q"ګ{dwY>ey&& MCMZMZr4;ej\A+XEq 挄2r˲S/dSYlN)ݹINuVR55I4)I%)RCm|GiJ%i"ߋk$UNjr!ۥɕ,]]EBLHuW ]򲪣r|ը纯UUT -U+Wg`/*!mMVXE] k#ݳFz}IEDŽdAyZ8Z1~SIOrYSZU!ϸ\R㻤Ž@H55 IFJd$LEb<[ðu ⽉f`ׂX.omBw{P ޻bh'bh*6FU {'Za'|/^@,%#k& jU8"W-EĪdĢn+ºKuG|qXYĖ,$&niTUk_p -"$DԒPJ2UcLUjj:Jϕ:Gr#y<꒕Ȭ g*]FhI#tM#44B3`i M7-a *tfpY Sa*gC~mw@^dQbOE*<7Ps#)7Fay -믐 - -̟j_v;\y)`jcmAv3yf.fN5`={e!/b򥈥Rpq/R?- T@iڔʿ4A~kS>jmVҾU^#_WOjYQx?Vv&gR\)"K/ʥk%O<Xp1Pom$5qQ cXFTޕe) -SM4PIYhx>]B IϕR)51JjIb۶21 ocR P RCk(b Wovm7) - ĚzrjE oTK;$]++>v۽ c~ǏZZ}-ͥbbjW#0Gi%oFɺUh$/5?(G ~ŏc0$~b9EQ:_|F^}I;l 5wKa MchJV0E:\:Ǣt%B{ KuL/gds2y4]!T=AOI.?H+XMXPܥq>gA*KczM#c/v?>>~_zNo:ptp0JSjc &C0&51II1/gה_q0ބ10fXP+` -6ПPOls&\wV6= 0a&~j [Z=W^u_:Rtzme.4+k4xƠF -)O ίu/`@hȉ+f7r}!>w7%,gҹYn!Kktv> KP_ ٤4*3ZzCǪljjm3S/`R _Z- -N!Mhon6\[b6R\wϑc*=Vc=?jCZyF+n{>@NZ5/bF*#r#7i{YQǍԨ+ƌG(HyNd7xg{=Ê6Wvg"7*l.an ZЭ跔=Js'jvLbR53fg̈YaT2c?5b/ScSbM#I̯\%gˌ\}2|))enE1>=*U)@=Da)fn$[IcuhuX&L;by7q3qFzWhD{o͌ qڽ]iVK4+>B =5#>4`%ۆ)6ZSmD{d[1ѶИ`+UXj,m\Fλݳ 1g$b>ã0{ KHe K"I&L2IfLB&$$C!"ITBR, @!(}cVVc] -B_Hg3s9|/>X$E ҐcU8E5IsT@U-wdґ -G@2#Xa:Ŏ;BGYn;[ycq9.YK$_mqg.j]L,kc acSUU -TE<',ݩӣgFsQ3Lw[,q+'+yN+fNj?g8IK+MuJ,Ty]̈Qif3ȝ;KnEFk\mǽvo[Vr_\GF9ƱlMԀxꉧ,'r`fE8;J9SU3GK= qɔϛ\o@^o1Mfg<`9={4cVy+󱕒c}fCV?8+/n-xBRgT7\c_-or}'w*Q?_n0#b&w[I^+Z\xm&}$=o%PF0 7f|>xhA,BeVVQ2#*RzTVj|&)ԥ`- -V(!x lc 4o2cؒbی-͸"ۈ+c/bO~o&j`C5o(]k(FӜUʥWj֪"ͬFnԴnM=ɵ(zۚ\16&gJm<h|Pu<شZՃ4>\3 i7ѴxMmthJcuC2Mjӄ6ii|qkzCz[Qk`mD#hl#Yy&-)tS4s!&E:TKXܗ.S p8.jkaR3нAWe4ހRbc‡/L>e~>g|A`fNmh5@8 -q P`%:X>qBx_]}%~1%ޅ&V#7B%B70vޯեh>g^}~$%zEs`@}xËWbCaADA z,EL -Fe;{v0-[nrt#Lqjh8Чm>GulꖀiEP0'oeX׈?L0?gpjJU^lbPx;w@x#F7b;&"awQ\r㑗#G~-QXM7gQ;O-SQp2"G#q$*q`i9-2 v/kΘV#cak6X.#/a86`Cj~c>11_Cqy,$Ȱ31;VcJlYi$+6%90HAOPKL=PISA&ze?Z#tI %UoW9R2yWP~XaJy;RU496*pz9֧1ڌv d?}ѓݙCM7!Y'KG=2%|'>KL!rl/碗͙s1  ec [Do=9 V8PxWtS9ڕ{QNUy^ g#?¡3m>K,;&Ygɸl`\*cїyŸ_΂j -([ -OaZ p6¥jJ4 ꚰ>ªzB$a-@Sf4(cCO# 1.aV-EWQ|řh/΃D*.m^4aS$E0 u3J$a"?JE>Nśi^t!:Q%,r\pVhЬEv6VZk`n&AaԾ& EQH5咸Oͫz4KI='=駛qfܚ%piQ)CSU6UhԕêӣAgF&}F F@NZOIB[%*%qX'{j}񻋿UZLXj`-P:FC#j -1tBo Ag}Bcr:#w#K V5HA 5Їf$&"p|wHdꓰb | .&7P[M`븪oR#$32R$uHYjA\, iD"*cDAtH8MENLm]'{LwXǿ *pʐ%DqEׁ /BEyjMl`֓&8֪16Mn?H}~~=~_Y⦎%( )Dn(/WS:`ʖ@Φ%r2mKʡ|2LhLcx, W<$Rk3`\r2#s͡jgQ[ ٙek,3ƛvsZ+*Pb[嵅OΗg -S3`VjeSˌ[ۑCh(u:.:.Xn0g<̙+[F_sa -SyH1g`^.@Us$z 4fp'Eg  -m=E'{xK4bX94s퉮j#MCd;srÎ]ر;ر;h(|Ful]pwr߇ {)5՜ ݌>4Ap&B4hΏ{Hc_N`G#I#ůKuX4`;1'-cٮqux-tɞ%CR[1Y~ւ}8694.HbU(Mm™&>v ~fتnc8!;ݪu.4@W 9| -Mywt{>Sӆ#I? {YrU -nGL_M%݁{ց 0=&&OVۃAcYp drXw@0C̄9P eP`~aY̍;ټ' K==⭇܁uޔ_8 l4r9 scxƎi )>s]u ~˯| | \K68ش +/cHgi? ؂.c*Zkl7ң49Y}]ZZ9flAMOŢ:#WϚdDeo{g)Q~hAN^Z0UiEUUnE&herU|w+Wrm]w?<5nk0I!vßWÕ_n/*}cJ;U *4X;<1*J,{T\,POfӌov?)E]C)!*mU2a.mTAE5k)7Vy~7L9ɚ?^3kz7P2VkJ6Ҥ3J &=UjE7%gaz<+Ŗ7fn^ jzM9X=G(eJSF,BW(5x&k\A yOcB+%FXZ(.EQ6XU<+Ė|l%69i!]552{+w >4)l&MPjX&и /SJ -ج%E҈ CE#\ 06C9,{rhaVᮚɑJ쭉}5 5D㢒46jR'ktQ@#bjxFOCcNjH, Q-נ\ZZ#mPGjRRc<5>_b5&.R)qo%jd%ŧix|4,ޤ5^JأNh`'P-?<*?we1 $n "" 2,0QNHAA(MqZ5q4զM6mzĸ&ƚXa9}"ΡlX?m _fK1SU@Y(/ir'+; ,E&C2 2UZJRSdHPR>%RBNJO %$HrT#,g= 3 -boeȜ6RpeE+#=ItsjLJ26(\MG'qA;Nw(4bS:F @QU5IVcwU^56=VS5Y!r>><ekzP -)iluOy-P0yڇ>+7{4>]5o_pS$l7SO7=ϡ~z&j"9Ff3A(h ܚ4K}i~i<[dZ8ZL-P kygຈmlyh*^/|3Xs"kĚC 7IÚ%%m,1ϵxXE# C N01ҾCP"p8iFjz͚ 5Jc{Jj}?@?6f p뤰;YT':ppzybFcI7xZZ+ow^BmxBP\wFzw>{pbGs֏ ŜC9VM(gU8@xuq?Nx;AEquO/Nj[9WuqN ?%wŗ̺75f/NLN>V 1,vb{%Ө;[|;xR>prWJU}s_DщTMNΨ@Ə7𣋼:~ŏZu[8}D|E Bm'|.85go-/(t"шZ:s_lخ|l6bsll29=قldsۮ"G'#$:D b]Pr\l.`w,$ -ϓTVWUD̮r]9 ];A1B9 (hr4*Ѩf,Ry ZDd+G#r"pvrԾѿ+`ܵ^ Gk4:ıT-TiL\Yn0˰/2,3,31 $vcxKʭk7V괪*RRU~V.Q*Jc;R{;G: rq+YȦcn:JFd)ﱰG}VuWj폴~UeZr6f_T=/F|Tg"S8%S[K8]ͱlsrx[}嘽,Lr fÄce.vLg2=&> 1,wvi9Tk%?k5t2Y$Dq"nG9orj8`!E8\IL&1B(iK{SH9#3jJCq'_vDSyʝi%SK,'r %pj6iLU1݌2ьQfL4-0`tݙ2y_ d2LG_d}>'.zi{XdU˜jɌf*3Y&w/4i'p y-0ws_c=om)]Ɲo6t=&ezX✖.kߜ,ȔjٯZ͛ٗs J*fwa=V|En+x O=ūt?*%o^ΒVGaֺ"tQޓDY3%R=V -=xz1{GN]a92k=c`~53tRLrH[(m $`H#Ϸ\_!9 -}ue1ӿH4)$(~I$ =5XE_Z#_t ^}Wt,RT$k$S @:;I Th$9")Obp/ yvOL\Mb&&+#rrC|ǥĠp!ҮZjBVCq$Y!6BLaCSl aTdo1'"lgqHLݢէ9(Ji+"J_1uBp:ع DSbsMa}aܰnBywx fkf?T#VJ٭aH=Aa+\89JI_4)ҟMDZYXI׃(ORS_US[Ƕ\[U\=%=@vP5,O8"Y=%]6mzI0H_)K0l>.wR )ZL-vj5!/Cp'V54Xք,(z۩g C|D' z "9&5xZpT% -vz'57` BcㆎS}&Tyi0(:5 : HtRwKc)j<)^xrS긭Mz[95YiGcݲ9S OkI7e.5ӍB 2{2ceey(Kk]XXXv]`9DPEEE-}3Ѫ68ƨǚ&5UcըʹMG϶&iLL9l?qg~e}yG㣍 Y&FaV[O?r&4ݑ Cƹߢԩ~?pҪ 'Ki.g]l穋 LhݷS c)+C7`?vj $ur.{gxhV.37kznP7I7M;*D2f;y6U+6S|}.UXzM|;]jsrE5zH]< t9}" v@ Z3a,tS|=t}M>|\sOzO BVȆB}24FQG@Ǘ9 3ЕbA\Ru!u>}p?^0zݣ׽C.RC('~n>_~fb/%||% -x6Otk?Sn)qG>H^WBԟqsϨسu8Mhl6uF*ާVc%>V2e e摍ϯilJfSQM49αͷhf x%{Z1p"ơ4-6o,P -4jИTmnq_x y-5+8{wn}W0zh%KdhVWfvWc!޽LӺR8MI~Tc&X[Us<1=/gjjb(Gˋla5øph?YCif5iJ_&U}M (Q]HѸ8C5 TFU[UWo<=ߏ2pFqZ#-93#gjҔ>(BlQmp֘~+~e_UC,dlְڪ!T~q >O ИUEktI"2¡9*PJ4@);҃i Vgd(͑TG9du(9YgdrF wAF2:)SB}ځγ$:P^ ,g3_Yٲ5(ۢt%ggWk̮*\Jp5fuoQeEຬ(]EgS WgђL1P%PR]e`Yr#+sY<9Sg<);S2Ez7+;y*sIaSDn[X,4&rP ^pV,o4 P!2WdU薡PE#Q4NE --ZE'̃>8dY]p9dj FEvǮ"!fRzD1j56$HӚ1ZsL5Gڴ4uSv~LLJ=}}}J,}Zҗe-=/kMي\!iZP[OhgJ(~ԏ0h.CM& lXoӈ`]~񣇋5顸{ ٽ ]k4N>brK $B])f:[`ki8`ogg~rr9H};¥{Ev$9P,z)YJ\BcU?t-=7L0cQq-)8ť|?ct$`]9sMxB@w~DŽ q` -a2B5XXQfezE|^&WT_?xNDH x&@QGՠoLNי1]e -?>Ǐ?Y>c2D|oI9d 88>//w@<)3̤NL ?>ď0՝?{wgROyN9%x%cd5^ \{%e3)/&.lخlln应wEVوvϓ:^<@G!.b(?hDcy2ĶuDڄ]?Sm+_qх*?J&v%} (nC -D:\fkmt*t3zs7]/Rk3ɰZ嶥jm\Lʤ"iʒUTZM8K[T`T}wj9ME$QnrvJˤ3i ƗR-gE)v8T:Lũ#5-u&0PiєA^MԬI_ ەO_ kx -G҂Ҋ. `+ܔ9"T0k#Qi*7eyG(AuY`w]`e]]6xM0xD⠉hhԦ:M4=$ΤvI۴;^37{y{wiJ>SMM,)O"t]-)n~]6pDo}=׿%؃ -M|!.oNP9M1#U3&_,UVSSE嶶i] u.XwzHb=xpgيlAS!|(^UEUY\QYM29m^a,-<ٗоY.e|9)-0pvӍ*-M0 &]*pĪ̑,Wi*-5,TRŮ:&5UW27j{/h*u]9rFc3e.KFB|P e;GYli*.RQyVe(S^&OF{f)ӡQ]FV>L+y>FG*^3T͑[|oF׸[SQ5SӨ|3kWo2|ەۯ4 \UZ FoTQ=Fh.& B -}/P06Fk[yoHCY2uLRz` XmJ-ocB2)f(= &rkxȧPPɡf%*!BCC݊S\bo+6znڌ5]0Pp]W>mĤX6&*%p¹J[4,p(.ܨ0/6&|f,4b96Dx5ƌ@=|mA{D'Rb45AC[R@pDT#EMaP<0iBSBca<}P{{$7eh6ugrј?v6ʜMncښ 0mx9c8GXfH⽓1[s)V)m)nql( ".ɣ=åM$wc:<_O&(ӧ &⩒iX tSK(kRˆpp [eg%yt2'9drcN/8&s-[ֳji'7UjCm^0}ƛnr ]"W4y&걙ztG7B=V6,Ԣ\1ovaM]QD:Ro ig3tt:~͍[`+<(f"$#I̯e'{5N1bhof=Cc@~ Wad 0*r޸ΞqM&:$fϼɀ$`8dA>ؠAd,-=qB~#M][}wuO|ʯ~g ryAXzEa N -n2.SY4yy]C4b9eh'{̻Ja,#tZ\S Z!}5}L>U3 xG;h^ms{V3]8 -Je|INS4hTO[}?#ĞIybZg)W*7eƻjArz}}Fwㄧ ShGV4\ԭ~b&Tb n_}ث ѫmM-v,ϵ'`| />g,ƒ8B-^T*G_L|7{٢mӉ9:w [iɨ Fܤ`< W;k,ExNT2yg?fۈ_FtA7 -Z9#9NZ֓I:Y' -\9yv2È?#KBh&t0UjgBF5׏p6XfƱZtR'e]o;v.p8qNlp9&MNv-mvJWrT+[v h5[@QZXA\1& -  -!:'}<_+Qˌ7ъFQ4$M,c]OxaYEVW^eN{{J;Q>!ctM:^FݏNtf6R;Iha:fmMEQڣ^C")RQXs< -uM!}*FjT tj"W5=dƲ7k 7,jpV7PE,+s_ܴb%4J{JCvJ.Ym\)Weʘ&+W*ߩd~ -Z4ZHAD˜*@#S4hтk"6P:MAZ]ƴwRIJfa X<1ٔ0974dnҀ9~sb}kIieF˜, WrFAeY*]YzJ9ZM `<ĉeȖ" X-V(b ֣.:c -7(hߡ} -Skj.7-}G\ܓ<:B %CYXc)O/;Qb-SOYrʫYVI6+^UrD~ǤZ[ث&xFNjr;^Ɋphƒ8$:j0.kQmRD5jq*P mZjj9Ffy|Bu|A5WU| qOPٍQ4` z=^0>Eü^P.Z\jvW]/ۧv5GT_?(g\.&9=A6Á23|43 #7964U_.4&ab1۸zsVW6t-ribAL"c+ǶDObIcN9TYPs46LJs;0c`w^@nwUf 5V1!a5&9f6Ԍn&لnތc+Z$_-xIL|1yyuz8c}:`?GgisyǼO& ٷ=0ۃ>9g4OdNi8)<|.[O+q8O^aX\ūÚRZ#u}g\"ӿ30+FHaW MqM7-:uqSu_qp?a`ә>^h^; s͌I.1_^ }C~w=ue|>r,!݆!ytVLX1K/W[e2c̦Z^ً@'~yy_=ͫ O8v?;NDZ%n;7zImvݺ6[E֪] Bҁ( ʠ*kT.T`@`m2sQS>w{y+8!hn X_8wp@7)82}]c^e?̫ -<߅o79|NFb-COoc\#Wp9/|^u¹|<~KhM^#80lO1|e.c_ރw&2؉_3/^P'=C%i/pZO~|1!} -"zARBLy,|>Osyx4c$csyy0;G{ W=hϳ<*wnY6e6NY]adZyN8K΋eT/·df }?edNaA{)tg"֣=Sqy9 ܯ3 -OTnawj~ m+'`6@;vahO0y$>f{;{n2ŵSϰހa<_֜B~c7EE]TK2/{]B?J5hgI8Zrs̜y)u&x"<参 ǃ-hQt1"Eo2KnB>@6X=GRff}6CKc@ ZbKI~hyYE6VhF~ , )Y}E SQiT]ILkiS;,>Dj+>V%%9ib"6N&!ˀXf2ez˪cU99Ty:;QޣeJZV2V˼jQAE-\RrU!u9Eqs mohn0*]7t ԵTiU)CjꔴXSҤZnicƄYEyj0*h|JTgX>E:'?'n݅=XK۳|ý49u_;-s)(WaQV+TѦhEJgάkpnr*[yZsrW^Ru pSngN5pwoc-u@[>C )VB.1WljtU(r+RW:긂 -ܫuonkUsJ5_#9ϫ}*s:Ļv|OA?tbqo&FxL -y,j+P[ZޠMyu7)6U>(g9|'e="|Ruޜp-ĺV|e:^$Z"s~|KwF@܁rҪ d%"#O Y_%xQgTxSJ}NBw3h&hڡ{Pa\a.UF|rFBt-:'kt,=2GWY!D.5Wi8{ 5سly Cc~;7zUSr cN1d5OQy|Lj78SEc*hct U -9?ހڣس^V>7CPǵIrag+Z -eʖʚpȒp1xiE%ɔL%I$1ILY( IpB'Po+h{$qh~\r߅uc H^"sʢ!Sʩ%) -S jV `V4NaRRRl֝Iّ]hϠņd6h3//LH/S/9t5Hu i࿏{vw #A<0sqsc,1|rl7mF1f#+>i<>]s 4y%lH*P_rsN0+8t&ܧH()^b)e`.ɐц?:񣛜M'х.;q+\̎ ')'/aS} 0Pc7!g y?QK9d% .5u!}0klVO)^ތ6)`{`5=0XI}ƏuaK}u\kWr@nzcyGY<D^%-ći 5M7a&X90Os6&j>괟d+V곟Xl!y}K؟6Sϟ!s=U|R</hr뭁H(!0 XBqaC*5?+8plI>| L}_cuM;9`ͅ9x7Z}k -Ǹ(dD\,FN?#'')3q˥|xCulb3׻Z>W]yD&Qp$ U\0!Vs%q@ϓs ,QTg~)!ĻeW9IнFsƺFA}pC&k@&0@>r0u-ys% kTmr YWIe{1܏-S׀CX5ȵG6(пјwUH}r:~\eM! s?ΣijOZ{$U1#1J ]v[>_mpmu })'hЗQ* 8fy ->=w$) t9Ïa|$љY;6f_)b 5خǶ&b{gbx O)5dt;mG/Q'*;)p XUf{&wѺИPn.NVjd ~E=Kx:\?דDtO`X >-`U2K@ +\gQQH/5?z ORz^Nl߰Ǩc~g;h$i= lYnl WP:l7`{'!n-n_%sŃtAzDwm_P|ت]>`tdJa7#u:IDQR-4,MGZYmbÛV͵ǐy,G1C,E'9 'F;5:KQL䥅L>f2H6lq~+93=\ssAOǾٚ5E^8BpDJ8jnV_D*P#ĸc7|8V3pw1wG6Q)pd‘GnRx੅M؟Ld4HR1è:g(UW^1DwAýxK#>6s13:mԐ`ˀ)𔰓U W\ᚈxC v~nw]Al}`hi`, Y̹ßI)&;U^U'4$yRTRLPij݊.V8uS_T(u)G9%RQ}_ι_m>@xV˻JP=Eՙa2åL2TYrEm5WEA{kߢ^y䶝UrF8Xʸ HjFJޗO4:Vđ,Y**U" 3X|WF\~96>>`b:ɧ20ہZyVJ)7WJI*vf*RRY N@9U*!k]S˵HNZe^+쮳9ALk=YHI;cQ5WR> BK?/M< -*ϗ'';A׃AA?L pew^g;n ,$$\ T -D8V^Bq2ZN6#2Ң >q;9oyn'[鐫%g+{ ]Il% -e VP!d"Z "8<<]LpUq-@ԉhܛ_! -c> ) $Uc֥ &Sa -(Hw#)<as29C>q-JFP .~1͵~L{ MHi 4 -JG ll+@5c`*o >40E70cۭ +W/pdK,1!a7fF3bll m B&xjG؄;@ |Ro.<)> ?:cm9&'4^6O/3JκsܺbCg\o3@jiy\g6^g8 9@/}o1DNQCbwD&AzkN# -:Ncnx_`='d62!'#FW?r&eRR;㚅ͬU[uv ~&v6IZFPI`%XEnE㮥>yl>7 ,f=F5\3KTh\5!%>擤!s`1kD- ^[csX|0>CMlFY} s$A n–Z5d||X`5?b ߝ%b&&ϰo_`aM<~H{xy71RX:8{WYIň%@#_&A%WL$u8xWXQ>8B>0uUJ &^pKd|G|FwUV3]6KD_PLpoFp*%U~/N )ڇGNvSO8jQv85mjd+~Ɵ9D!Mǧ);Lr+jOU,U*.V_ZUbRmP*Q!y7^'q~&>@^gUcS;Y))*wzT0TjĞb{*tT(Ѭj-JsHOU/Zl( "Tr O%*HV^SSSI++5W0*7BMJ3:eK1VmlxRS4Na\ɑJb1ǐ' A9J@!%7=A9$e(ϗP&#}cr?8ʫ -o6$,fwI6l~vIHBH&@J$ -$AkJJJRZjŢXZdZQt:0VvږaV;0/۽=s}o -|ӷCyd}Ke+{y3݌slP⼫`M2|ey*(!Uh+ت`>9ò7RN-F 6`|KZi -|A|lr :)wr(4KP -Q~RpBpNˆ/B׏܆hDѲ!|PNc%|a#hpJF0ߕKżbxLq㤸Sn~ǐ Ѐ (%]%\a8g|JHlHOq4di X65|ՆoexhE0W!Д./n{y೎Y|< scZlŏv0 :rEE0&u٦0k@3nIk%e o!ya|HsvB!'KA#KYd>`]*Y Ճr\tuL-1GlkKx_ o8I/9kA!h.\c 2ꄯMS~w9Xeqrđ&fNjn q/X6,ao=puV?&kyGC&g3dL(9!Qjgky?ۇG>-})wžk) -!#6ko,c\ɊA(fC~yCv&ړ{OK߹F*JyW=烀% qe#3pH\΀j<9y{@&/|N:gT "bc|'ku4Jg-_-__߃?[mrrz{ҿrGQ --@t%"}v̨N|StLF8$P3\PEM/3y^b}"3ɹ#LV92l+C -3l3ԑ)eX%x?<>j* - --tL4&qXsh^x_xONjqJtR{L(P&˽v+p^Z!3Ne8qY Squ sa(C2M~] -{mf{Lm5:'`6?)=|w|_APŎL籟.Li$_y=Bz?"kzDZ}p_!B%}'] J)(<5kn tb#BŅ9!:NwpCȎn$|_)nSV"xfO*xlyxH%k7xxSspd;I쯇o9r8+[@ -ԁ8cҕhC|E\ -;{Tn6ٻ4wX܍*ɝyyrgEo/(3?do%3$$`BH@ Ud(Q"EED -TPM(Ȗ-Z""Kw;=4s3_sg}ߖ6(M@rAk&Ь%vJ^ ; KgM@ úZ|u9I<9v,}l+팸 O8:C_ mk<b$J.!)A-A&.~OHtOHZsѴѪŭ$NVi%M["VxsRܢx:{u>4 gqYBzt}N}.ѽzDEģgq%+ġ~ŢNy}d8/aKbrg*bNE} -A3A "M~K[4[<~M [[%VA[rNj?]༆ey|1G@(h`*]S@K!M>b Gs)4 N_(| SC,u%7$ђ4ų `6уv-dwCaآļb1a11NSaȤϋ8!5F -ESw':{U!<\>y0?*>⎤3&C̙",\&57K?GӚ -4JC96g! -dSCI23!rm3A{Z"%I cfqdZ$-*<|6xcw!^"0Å("qkNƤ5!96"M%3 Cso#:24s4/%RLZJkM[f -ESjhrٓ\gRGB %xPEWjI],-VI^T0 GFZќ^IRkA#VN,c.'ZϕW:g/\hL1$iRUkkuRG[bNڱEAΚh˺نTIn}Rɻ@S3$( 4[f\" h /\4DSGwΆ݊+yލCF3gDv6gTT a[>u 4UFhF4B@?hI| baX_MbHДt%0>BS .z$f*|ըj@8:FF0'YPyH`pΪ4M LH9:KFf T{|jHh#OS4puddq'qm*i~RRB7 ೛%ljۛ ZoIq@>CRM}jP~D&%x8(D笚|9*Ŭvh֘Ws/wQqN2,AI5F(SB)RV$M׍luxUyd./(ǘn+ДЬB݆j>׍l[ {T38؂r99<cBvOUdN[`(Y9y -6izEqe-Z^Yqխ暶U_WݱSv7٫w8vC 1r17~¤Snco5{gκos~?.x?.|EO<䩥˖xz3ϮZ k^Z~Ɨ7ymn߱s[{o{>Og𑯎~}Ϝ=ϟ.\JJ]IYWQdg+/RKrR+kAݔ7) nƃe-ʅpa2b"NLUV܉w+3~ܘP~< y G+K<Ô+/b:&e+8fMy[sP>| T}<:I?().^J>\ͻ%~__b2lvǣaO G8#1<\|LҚ7?3' t뮶;f[mq&O6}ƽfϝ7 [xe+.j[ȤƟILiLT*Nhj~o'G~83gϞ;w?]pƶehbm֮-;zW_5a3Жj꩛sӲZdOG. 54PCC 54PCC w +u%^]/9 _ԏ-~kwe ܝVOѢ'~HPG VFծ=Ͽ1}C&|ww>/n{y /Mٵzo.Ѳ{_d'װ6Rk]/޳̾{ttp|gvzffgi;fiNд2/AoF)TLP. E["-)'-߶g/yޘᎀN"_ -)Uka"a>W~ՓĢ ?J!&vezzc-̣>$CkSFkCe\ )j3J-)|ʉ鱶d(;nZs>w'C71}fDmX4 >J6 3O;F.^hW}F]υ -{E"f(/c`4Ѭ7h^!}w>85KhʙHAΡ 5.-F\ |{Gq{im=/ZtUppdK*8+#KD0 -3{717@AoPa.57d0~DK2qH9yBCOmHLU1tՔl2"8dX2, K%e( %6@du= -םj^A\ˎ*$,!2.KGv[=ZP -$A=]+#zw^ XaF*bZ*SRD &^\4h`C͏@v~'x<\վAGi]8^@(WI<}IXA-edYÍDVa( e Zg:8QCHz,I^PQ%xI-02,탡2Ad jk.s[g-HQ$Xi}yJ&E|Ne2@ZFO AF# zw/؄9Ќވ0RYeJ($9J;C`M􂜇}0 P/69&Il됼:y*LYT~?W@lͥt%SYBfaC*Pn;ӑW_ހ}]M*'(bN)%O2qbzȅ)}^KgX $'mo N_u_q y[r=P"fUdVp=!:qܴ.K``ZJ s^9z5-&i *ઑ9u(I#ObDQHnƖP VKE2jVwn '\G&ǼԲ֠m0NJ\ɰuUH Z %4Y@" 5Pog1ip`䉳ߦq/g(MEɱZ,T"V $KM2TjA})Ï9;g/(Ė>Ȝf+Z5b m @92v~ jl̆q'G%7B#~̇ސt& n磛#XB. xю?S]1a^‹w>LpP8:H"Ol@H4@D E> Eߧh x 3{ =p~@bIb)y`o%65~) -}OztGr( yĆ_ x {L|@mT+5s7*Nȁ3GR]xڅ)|9x^d\ \dyb.pPdmkkm_"8'q*)c{&B?P|5 -b} \hXL35j1|%/h`?b!4У( ^@9 Sa6r%'Pb 5A(=)|FIS|F!'AcIn#V4jBkn    {C.bE aԞ=ag*"tVP*GhHLf)sUQ͚H%PY~5[y6V!zgkhpwK -Ly}\DM3 3]WdkijJ$#d=U$yA -7B-P%P_6`ׅ3$}N+5AIL62U`#%yWFSE E[V\Ks2[nh`ԱCbx. 蕽|Đ -Cű-NV>ߊ=jVy& ޠe=ή0ۯk@G -*h\̰O^LA m(U42fwO'pxa0|YPa47(t%1 Ο;\0^vwvgԟg oB l;e^cdUd ~Pb07(PfG?^;87rzĸ~_|)8Wх5SSrITdC!b@ As/=s߼tD[gnyQAA$LN'3 c8FBm}.)u#=g4b4~noUc[Q8ܾMYBy͇fBHOI8t8:K - |H{ȠLdT}ūoݸ\7gMӫ[e`Gp=ϡnO"@EdYs@\4yjlflV/o.=ٴBj:*n%}lNmg[SeJKn/T^P@QQQ@@PA/],YinmyRؽ -e[ -C>yE@)H;a#ssEfKϖ?,?_V3|~lEfAx8䌃XH?Br_({Jh3[^^0_S 3`p^AkOot7$磐dmÐА -P+6/ -f^ Gp1~)$T1|RB.玦TMI千.YoA777SCkCɦC ?#4wPO?Nfo0qZbNYLn~(VbOKmkZeu8MB ̵Օ??#Ȏ,kNDs:#:'>V\9Oe t̚R2'L掞8Nhmנ镡1!?mb#~GPqEfV 7UO$K.R+ \f9\cLu%uMXyߪoNhkGWF 5Ak(9ԁZ$)93Ε4RfiNb]6*MZInAӉW2v -&Ġڷ6[ɐiuL Ye>3YK@e6rU/"4 SjbMCb"5b@ ЇIUBEI*w:SW.+Է[i>9Eߔ+UKr7Se@p^*6A3x!Y!,!*y-vANEM&ACӫ cuԆnvC$e^X_/.bm[rĠyb g \)ƴ&} IQRVmz;hgyn46,bhغ ;WB;b>tv/KWt:A`jNmg\}CEŴ6NΠ7Ҥ}4E111t"/,1AaO\4FٴC AJd2ȫ(8Um3R HaD ذ$[n+A? - cSΙ# ͺvUD֜Zא )F/ -(F;Lj! -14! -rfס^w Y]1|6QHUU5gvŹ=utsԚSͳe׈2xRKFEMP81H|ZOgk15zq4`.dzXFPV(3Kd&Z/asbP+KVPBCǎOf{~4]=|RPIZ-` I-|M)z圾ЂxME os?߻IocA?c)ґ]쉗RtIZbAe򺣕EHE)B)2*AW8/1/o\#=s#ǒF#l{t%/NՓ % ʸtM~aB4|}MG 5 qtl^44ra8((>' ) 1tIDIz*5+ @};XpgӻG\^km'#!_ٴ_DTXrR,-s4A8K`qqY9UX[~M6®QUA۠]tw \ԮUGg<6`**9{".#u 7 '8́j');m{k&h ho''DX_A!NW1K)LY[FfZy on: L㆚ P 5ۗ@+@t =  򆶤YIVS~AƷST*e\W;^^'cF:/n΢wt@s[\d FPEt$H3>eOsJ0)/(kh@>Ӭn fvhN1{vgo'h9_ >u6EgZcin=aqWu7h4(N~ ="l'h0SFI721MGMl/ 4d`B mȠ84'<@sgV;5Q9Ts ny*rQ(FRiھ,&#{ZsJgK|ݶ ۷6@|ftgB&'=TeH[H}U{˚6˪Zs}I/S9K 'h߷ -T7P1!O I=;scPcƪQm%WY.(IM7ـ Ad|LgGX"nJVx+9J%rS}Zd5LV%THdHm/6%^AfdP] ={}-*[[|SJј*9(#6a"[R)|Υt9Xe*\of{{8 o@eЅ փos{Ȑ選 WtEi2ȕMe*icAfa<&ZOki*QCP:gZd`"/zn߽ʯ'mytSqф̚t^s4{g%wQ5g,lmEڬJgd୛ m?gz݋NP*|3I;! RʼnC䖸 5mkey_goMi#֊NǺ_ъbZԊ"( D K! ـ!!@BB "ua(nXQHU^h?ܿʪ@-g{~o٦w7iaݯ'^ EQG TŶȟEa|1Hx]&]-E"͚`^?eܡaWtLmqu~)~?;I _)OԄi_hY=A.3+ݑq+rnW-n/^2禰gl}'=$qϛ2 &[ftC2.L?+N -z*)ShSes s -)[+,vU%8Zyps T z;v6ُ.3 -#0l&. . x D ꞏ|ces`"5^Y{JOD'"E5 2p!8 ;Bq(7n\v{>F+,Uyv.5ก -.I- -vA@Dl 8p}!:.uXb,15(`ռbYȅK2Uw ׇ _ yC7^R@qX;o;w]8|6s\FV[a4,/Aqp[k=;\ȯ f+7@!H26:C6 pm mlO +8{q\ׁD!P@rԣag -BdIR&ԉqMYϢhc.ƓޜG{a87Nso5dX݋`J#o;A ccx⇿LNHVvL7(I sU g F3LcL.SITS󞞹's1{.bXܛ|mz^y3-9D^鮒d L4T ~m8d{嘳@_h[;V'(PJIki)%F'\WTonuwWIeU'z+fjol/c}I6K m+ΨU\f\VnFeoÅwE*I> fh:֥ݰ˧֚.n&(袔[1Z(>D$h $k+{*%KGuS[o{d<5mq|셽qj??gpzB9(RF/oeV[׃ZE-M]5rI@.i' 7crk]:_Szp؜q봋sd 2hD Ӌ>n_ҧʫW\[1(\Ax*P& e~Qaўw5rr,` )||dh2e"1!>LpW1CBzmM><\( WyPzѿdf--hxomP1^R#g¦iIIVD6)/D~R:.rЬ,N/TV ֭],[y -)u߃'/RBHoƒHgyQ0 -L=ED] -DI(>݂]+Q[W_M[f1-cC\ÿDa)5:6`5\,+E$Eq$rV7z߂jZ'W ۄpN%4X`E3@D߅P Y &( Nv /Wm FI,ˣF9"/*J~ ?*\8 'C| X d}51RL܁i k|s'Z8B$1"۳ uod8:;r8 8DC+D8CN@;)/S~l ZCDݑA5"*{&#Ud8p@>3Nl ]80 {`ma CVsX@s`5Qd3c&?.n)TC*בֿ$\)/]N`,w[V6/SW1F@g JUTC4$"1 Bg $*AHP ' `jAj9H7Z $d;A, Q^jC>Ak6*8$l(|g<59jCo_j@nEDioK 0W, `{kX3_*yBJZJEZ n $/F.D>y;5?5HoCIH>,1Sd{jC<, 5ujS h5<:y)!j@Ky4@ݿ4$ig>3MM$" q:y;*CaӇ,.1ҽ?'qL.\|Q&$NzEseP ONM%O_P@<Ґֻ<eh@!Z jYu&o5C\D.S^GU$ͅ`̐n1p=pS4\:9x Va dYˤ~Hg/$'gK'd= iN%  t=n?e5&S74!i HCJ9@ї^\Z;=z4C1O# - FI%>aOЀ~9x,&i<4А͛lG68`H7K -ANmtaLs)JM - - -.QY2%Y")u"bb] -q@$8@50Eڿp0̿bv9YCu7DrV,8dI6Rr<ĵH i(ߴ׵G>trÛV/,أO[C0L]5SI =V E fKt XBkUSjOL~Wi_  鶅 ^9k>=퓝#ۃ͑QqMzZA(VIXb>Yʙ,uIr- RzV]@j -齒B  dW'`#g! ƽßo=UǹC;|{Z#"[o]MIjRE6') oTWV(VX5֝U[++h(\Aj|l>m/^7K>fs,ؐ'NYJdXʮK2_f dzh F<ߪ!zA=P4;A%r@Esw]kЙM? _p8#,-:Ym+jQYņ<YK G*hez}I$jp^ixo\GJ9t[ivO=W{y{7QUmqYd -݄/4s -b)R$Ī8V}pKsةUu_8t#Tov4t_6=sꋈw]`X ҊsdU4a|S$$E#LMaT! LdN)σM~kcK_o}vvo8~,x8<⻘4j̒byƉ_Qy"Z4`k0mz{i4hހ&ǁqܵy 9O"n|@F 'gR> ݈6fYM\d2m&Vp73pp,gnWA8vx2 5c/OOp>D962{"1>m"S^gEYQϸĐ҂z6 -v|yl@jGr j`bCo+ o=]ޜ3a93WgC3"㧉1ԸkxI!/)uJTm sE7Cj?EXzf?l^s_);xًٳa'fKf`cfKʂCgy!$a~W}g{e(]P;# Q7la -8 -` xd( yBr&gp>;~`}juBR*G!lu))6<קix A;/CԞPH i =H1= bPz\Qoh<-^(eCUM-tZ':-"%,X8.λی-~^m灴\qJw֮ udO~q|2PG( ac>lpNj2[Ke8vGEłH "BH)$${$!H*(( X.l"(3{9W?p.Y{ofF0  dI_}Rb/Wx"xW]^ <|s8ųq0}<Nb8!cVx} sw[1,b]YHLelQuҬ`ZdY-IVQ¾guDY@< ␋ -_x:[9pˣ!5Q-e'& -㤞-Pq$FDk -(ܚ|9ǻenkxn1P` #|"Ȝ̍j]w{MEP[[L('1F:רgKjJPER+.4ŧ3M7XN-Kt_>kDP"ga^c'qqK>˞^=SgsוGŜ*OJ)*JS9ZSXW -tr]:ǐ,ȣ+*,4EC>UuӜХKe"[q`/j, -IgMk@76޺|tŦڳ1ѥ|~y -M_La˭LQM-àͥXL$}9UBȹn&;t$=)Y-0epD`anj{9vZkgoaGQudC g Kv1[F?!RS -zdH4%Y0aN8u)H d9s8g[lIJŴ\u]I㚚G1hhjsP+]ռ\}xn~sDr^G{TAPI =uz:ڬ5>tk7ͤ﾿Mw/uk zDs7u#)y)6YAO9e;푷T֦P{@!AsSwZCRs?U O>Rj&;ߨ}M[Z -W}^w76MUK,Ց=8RAbyڰsԁ ֫ uTqZV"D '(`A^Iyݿ20duQ琯UPi5w["I^OjfƵ<ɕawY9*y;zZ]k^wiT4لݔxwg 10dBVI۰ՙ!NG|=xa~iw\p7?W^)m,m*m+w4XƋk/ TA!GRpn .q.qst [q3c>VFn=RRL{ e"Ibw:C/8hΎCBh$3b\PpJx֮CyK2c~q_vdrT]ptInpF"Sp},t66!l"FMl I3Ff9X!AN{H+RƍQgSwy*wlHڨTG= cpsdX"[,G\Z $7@t6KH  -/im>MKH{ -D6XkBA2]d (["O@~bN{@i 9Z F?x9 ϙaciǩF9ݬۜ{1HH@?!i&Ri#g.Qi@sTnBz!<y1v> eJ ?Vެ#Cbhw/e=(}z,~L:%|HŞcdA@6}GnRŨj=˱j*n_J2}rcf32gҎ$MIٔqQ~7לG᜻ vfOJsE]Y*HPkPNaZnZweCI~T~%c"7*kHNT$Kc͢ܧEw/ sGxvt'g@B6$7ϰ&5oT#G:i)E edY$oH I؄ld)C@+R>}/Ç}07H^0iƟRO~O$,i,K0)QN|BȢ`z@`/A\$%%!5v_k]7t 7S.lt9nF f=ް|܁Uǵ6[B!ڐgiznT[$߮sڀxoʐ8dgWos0f3iApZ@bڢ2Mq? n.a~DX+"UP"  "d&R0 w~T]sJ/)h -k0x QbF-&*E jQQDkbaĠ8?R9Q_3kỵ@` ܦO_,Z_t86]aɿe?'#a dëcV[L:@?}b#o$`W{tC -5@; A_m@%kPkj~CfeK0Jjn@A((g4:I?|kXyI*x $%Rtfffjj3Q*jS$bZ\uab'q(\} Tp0zSs( 2A hBmT‰c4탱!]~^qD:M"!#M)Xag -'d&n,D#fa$ȋO0K!G>(B'j m6s+.\I9MD*9N` (7Fx!$fツ^kx]2_K,GT-t@B{,Z_]$$)\Na8XE= Ìzs\2Nl숄^ RMVBd2ePڠfV*`u}P\\cB瀿<JmQۂG0ց~Ӯ^ 7Pz<^O$B°7yi}>Ǎhc'|>B':8<WC𼍑2M0,؀oNXׂ䣮wvkʯHH|;čWH^: 4.x#=h1, -GrR:" `q'yq~=@$= &j ڐw c!o& 0 *r4bY0CFsȒ,!M[t=NU")3`^D`0~폰I𱤡-YI|89H  #D:#eDN3afȎ\E%+!_q U7xw܎ pN}!kH) -'c-uh&W؁/1("B`p7Yke9⬐˶ETxATGLroc?KO~K RSS?{i_c#:u6ᐼJUOjoSp -'r"Rqΐ1QcM#)'XkF/%mG{~B?d%KD.vX5u3Qi`slBE|q볢0H+z/}+K~?Hcd`v1n O ?dh=L-6kq=NƙLR -=d,GJfb *`[%ƶh>U^ذl`=>ԧٝG0odg>$nޤ__I\2s)pFn[lu4v?5c5vwֿ) eG+ ˙%5oDH:2p~LξɹΧ}⏰O2t.KC 7:y, TzdVG[ʾ=VF9Pis.)/w"`8P#~}bC:1J"n!ȼ=sSbMsF])c~.SPnrn(W%3ʓNcžQ[=T ,BBxyɮht9e5hp[Ԋ˓vja̭J9jsT}vi.|oC#$ -$Z-2dAC.q {'i:&C D,{ږ^;2(+r9gJ' -*GUYs[eu-FvAdOse}N6V i(&A;}_;c䰛p­s}Vt*YIVe˯W6嵨~[TsdJiujejekq@H[2YD,%!iAGF>s:jh[i~W#J:Q#gW *J-eٍŻuMj*UgirYB?uz@Q5B];sk:~#-4)A)ԡZ I - -*HDP]@?8*~Yѳ}ssv: {:,{~'ŠBzjZƲԨ3Y i%9ՙp0W(D/D)췉y@4!M?a{g6-—KR.~4qj4h^\:Y]_^+(.*N/gd)ŒԌ 񟱒@:mCySn~c+ǯx`q_8ΩZAd -ёTaiEfFajŜ¾#E%/KiG $I;8-88 w>\/~e*=3rpAO^?۶[cK8!)LjFU^Y\Q/?//[>_LRZbٛ$8I rԫ⾶oý؁{q{nvh䘽a^k]gOhV0qјĴdL^M0TWõO5Ki1oxU+q@6\ET)I}QFˣ{*޴!ݖ17ݶa LjiO$3#>$Mr,ȅ#q)MU2Cu6d7m\N ;~8I jTDfl\o,jo: {FeDzS{“zbb#; WקjHjNgv̦_ -8$t%diF4;$ݑFSh䑎T^Ŗ86_p& ׄ!q쁊D$߁!c`V761=/{5JqP)^^ >;JBf6gdtmB᱃F՜ACƾ,ǴPOhޝ6wBš(whñԉ9mb%~cPI _}8-ۤН /οQKrk{5.T@%uR=w1щXE_R^K>KC  />/iE%FoZgaAլ HkU-ɫV"WCW9FHՔ}B[Z~Z/9})gOrveNfB82GuRbE| |#5lYwT [`wi} ӣ}x={ɏPcMeC0cy^a[I2ކP_B-7:=P&\hΆ80dWPh' bpN't/}hc{6m@]Ĭ Pet7||ϔA$י T%OX黀 >]4.#`\5ƨ LϬ_9,P-R ,?S@5"Ib-adVp'EBP>0O]QM^[TzYuQ Ɛ9! I@ @ A@(rUZPE -*Ȱw;9]笇ظm@X  o#D/a$vla,|Fk~`We0;xۜ{!p .B i+n hkrx[6?nǹQ'q-[FÖ8āh=н0 D Aq1O#pR=%~h@m 3@ ҵ@ez$|e[IZ؀ ws*CfV=zG%v?&W0e 'waO"w6x   Æ9~/ϸnl$;C8 T7[ځ|فa䮛 N!F#{4i!U!_ٔ>oA%W;9-<þ$`ODfhl<%n|TQG wN::>r~u.;Zm`.W&<AȍԄȾD WA3JJb;D|IoKIyO%^H>J3*dS -4p1:?,y7s_pyP]yW~1GyFԦЎ'XGE6&Z,L( %;Hi{I 2+F,,jB&)&:Wn*J.eW{;i4IsȒVIT|(0g-$Z.UíN2 +TU% iC -nE>rSiT՟՟y z ߾C7u27ۉZ5/[|ٲ[WjʙN[QM*jmU-M-4cR<7U>42_%di> -35LA,ty ]lrxOgwۂ_;CO=aCAֶ2fS<֨ThX%B}0/D=$קUR:U)?RHӎ1thkuNf Gkf,ںkyWS:s-=hUR*S Y#0deIU+*"0$N%ǘq5@:g6t1f_Km^t?p#`:Ἇ7R4ԲwVW e2raS(hr+F$!ˬeHP<3Sd[X15@ cbfq݉Ř]K0W4t QrFVXY_aS֗pRK qb\&-O+=JA]4_8̗-,qi O -1P= f<^g̍Kv;φڻp;iSe #ݓ &ZJMr`:/2O_^*yNi7.*a^\W<ƴH=;af~.al̽s0W0}Wu8/l_t.ҳWl:j~IH (" ;BBB@aG(Vԩ#.uSw -,0EtVEq=c= bʎϼO;~|}&GPnj'Stbo~~́ՍҴ껥? eUe>6 S9Gg?2ɃVEnvK7rk. ^H -/usYq[[ 7sbMFuaӶ9Y k -U E┆u9&NMit tIn3 .3^9w^SǰVՑ|פЭ37\X%XQ"L{~:ܮԶʬymdmC9TeZl7$chI-if+ qA3$MScCV{n Э _}#/zq|εIJŕJ*-A#HOr{kJxBrA7좑ܭt~NZn2I##rߒwYQ۞7<{7ú{cDfknNS2KR2LH.$ &zrX艨Y? -^3C \?bY>.Ԉd#hݐky˰qooLlqd)jMlp-:2{-O)zIH8!J"HX>--t?x9g#0 ee:@i5 !e96̰p\C8hՇ" `Bʂ,R)!*Bk ;[s@\#/x7){4<ǃ.fqq!cBE1"ST,B%W"=U^jo3v+w)n࿡) ->HmW] f{!և‡ T\cMPCj1de6C!>Q DHhu«)5QʤL @JM 0&jTًH CָCR6 Qrq Zلq鄏 gLQغ|AACil2} fl)2HϠY_8!e+<8!vCKľāIp1\h"G$wH -.HpAFڕLw8(XahS-^dltf',rw6&`FQ4%oL::!Az;׈Qɛo%Cҹ7I{-GK+ D4ȝP*yLa5%B )mP1oL6Ɍ=~iޫԆ&7rGQo1IyCWW/dclLg)BCBt!ő,:飔c}!"LPƷ:̚ bϱMr6s_`kcN8MEqkKXܪ {H @ b B-@"D@D -A^VPֶ^u9ߞ3)1 ŌyD>$ < {>p}n0 1[ n32+$lw} b:XۯQ)GrU`0kq&LxO1 Q f#@O[ a#O}kTm=0}ډ}Bs"oףX}i$6hWѰV0+ -s~1e9XET2K^Q9A~E6fLH@S@V@((C^ -d!o5n3aNKǪ]/wt9Q~uqXN-Q1J8yj^NB03S?*?g$z$ {s羚P?llew]O~{GI=VkȉŔe&tir:NfQJ8Y9$Q)JRR^y:m<$HX|\^ԯsv-K.O}ݪ7rV>VANfgf1ӋjMOu^>/NPJԷ$?IIeH\@\k X -:G.5yionm>KWg(Kɔ4m#%']ƍϬfeiƴ$:7<-3.#%. lb$ zob0[=\&:7;/j;E W_v_U*є(ɧB-K^P•Djω^H;)ʋ*|˔|J d!"5.7 Eg0ri={vUM-ݥ :gv_cĄr5-4uFxb$[ܒ\A?LQGl2d=iv'poٻǩѕ7w6qiD>YP|]>&Q*4qw mWB-PA;?k#t{h5h0phUw-uHy^/;,1 -0idҌ,M6ɍOhoht#$1a-0 pF;0r]m3`fouw)^lw+{/J#E] J ˒B'Q:*(v#-3>xJ÷!a m̝`,߷A ,hrO-i~%s0ɇv9-t9(ax@!p`9 l ,n0aߌ@v;(ݎ[G%];1MοÞOʾ\O%(wƁfdlfGmrޟ~n^BL Ѿ"4 I\dLCpfbc!Ń5RlOh0P¡Ej9Nh8b#MN dBBgCbrDd9CVI;hdFo۸O@p꣞Sc>k ᳐ENCP@^ᆌE gސlB@|<:S!RԨ/Do/G [1|l hf;U:A=*$(j='os背f2N/d~~C]'^OaM)^Rq|m$ y$ rȋACrr]3CS2.TkyP~@ȏA~6dJ -|:a9z[ gBƄ>c8i80 :W=79>Эvc4ۂnAPAu,lȗQ!ѿ)^E*T'* d#d@G4LX( aaV4D{%1K튢|O"Ə y%~Gnwv?DsdڐT ߳`F5}E=z&L`dcn= -'\y0.+\2lZb憘gMOsN=ɪHӞK{*+y&O\0TAtLi/vNuĂ 7ucżsJ>?.\0s}Իym9,?-wnog\?]}oD|$u0R.`ḁS.͇=/4[/[$ɿ(*n)1wƎKNw=Rv<[7E)F$z".5,uL,Xo?T48˽oҐ17*fhiHRRF酒EMs-3ԧL4'L]c;ڣϳۋd)!J~r}EoY{|_y"űo2ksk._YB|Ʋ.͊c/5K9P|>wϹ%VKedZ n)J{4/#/x -b}_`߻FܽR|~vLU k,WTy|zߜ_);qc2i@Vs%dM}Q ỐOsڷYVp3? c> tF}i1\Ci`mrkU{*7iw<狹_(o3~n9h2m:oHXնf>L0?I8XUA)ғ`B(@( =jjA@P((2눸zQ 3{f{vV|>_NnMF*1&8xxot~ |NΌъXQuTY٭9.}|gWF>UVJoO&51/'&Tſ -NZ 4D#/C -+2TVkrRtLgve뢰%1Gz ;ryr)R~1)ܿ>YƪLa&KEļus->TC"{٘p#W7 ưnA:hO6zIw VzE':J 9U*%IeQ'Nt=h/L@ -TP![ ד`]tk 5]Rwҗ]&ok7BۣKq-IM79'LWBȎr0yL, -M1e?0Y~rD#CdUV&z 0_@]=hxVr⸁pD0`ƿޟ:esZdjJh*dAC1b)VO(P T{kn~x{oeFgC5='ݼios~)wC,D쏹k5t$9ǐ27zO17ml36E|blՋ6<ȕ~WNP0- 7HP5 #(^C}lgIqLO΅sd?8{ &`V`ǘ9f32g̠촋#:JR%n+Wq gC5(~/r!Z Ɯ% Ygf~,"/|&x6dtmGUnߣCnO6p`sY9P@ -HUY.B )RyLR7*71[hP),SOeNwen6sew,~^p\O;Cde.|-{2!aYb3V]5+ꊟJJMWӌV}(>o;6kb6ە/a+~*p<@k:> }Kې_|4kC:(r:k!T -5C^pZ>}w <H[_Hh \~:L:IvMQ" -ְD{P9Jڍrw2Iu|u &9+m8)@ g)kHE vȜ` dYTg;Av5&@ $$$6!)67,E*n8RA[EQ}k=ťӊ֭Uq3_ۙx;}srpig0 bɴA$ -ZH2E1ʴ JYec'6PT9I~(“Fp [83ؘǰ' hiCzu%icj&v&ON߃Ѓ`O B Jπ_.xzǂ6p0~b8A\4uxg3O>C|x\-,@0Yï {M;H3)W;=%wÔ/x0ȃ&|<BDd,H:τ(} Xict `)$- ?&^[?i >Th\H>D,Ku YB !2m@½V"i EknٓWB_ a5W\R'H#ݘZXc!F](#,2S֛CMʆd͔dpG#vg&W߉ڏux!px.S lH`8G!ք`gr{Qv4bgمD)t01&*4 _c3fE;v7{^u~%;4 sI\I>{7s~c -RV$4ePͦ24/-!E: >&ϵ|3So"j9O=w60G=/xߑI;vM +3 " D[1`@*&D3>+ߤL_$~YT|?V.z}nFs#b{=bwUywTyߥ7 Q> v&c a$7d;3,!7#ўL {g c NrPչ_Khȹ&v߅s1D <}Ip"Xˀ|ăs8Ép'4!ιtl2K %dsobɬ&?$3K/w)|*Ι=G :Pui㟈&LUw( dx CIb̀B R2 -{ƛ*WI5GneЌeJѢDNgN˽^w05@rp_Rhhɠj_Ȁ꘴?lww}Kop[b RH~6[EFVQlƯuNV+.Y*Ns:v(@Vo?,r=K%};;RC ˷W$VCؓ/M7&wУ(#Qo̩G06NX'3ZȖgaks&%C>_GT䷫I+&gRVRZfQ6Qmv>-"{9צU[^RZ*^Q$ސA.o9wGxS#VD5\j}\?!m,DPSO;!oQzR[3~:7SohUڻR]\Qo+/]%n.Y'k*ڤXUMYW0Y?U)aDm{gՕAP_&(TLf ֦CnAlMU``|wBmMIs2<;?n-_[])XSY/^],+oWԖ~*鏬(9]VrUQ.rՕT iiQohjl5M]_M:4:-p|澮PѶ[ZT.kzlXпBаliTW}" aQ(B @ؑm( ""PYdY(.Pjg: eLm 8ȢTEgǙ3=a>~s>9 -f?pfތ=!)B3&w7- :íT~!3߷1 ײ~\btqjZVWdx,ҬdX]~at,^}w:}.();x{Br%FeTA:']Hx uE:L8˄M|j2$Z%&EŹ;itK+L)(I:Z㟘75Qp|o~ ^BD&}8jh]π4\nAG byэWYoP|&lmM95qU;U.puVnTUy?? Ȫ=us5:r%j8Of.-lHa^ttz/Z}=b]Zfx;:ƞ͒G7(=:}B|BF:V7^:{Nx -۵W:?yO^T]ׇ^+F5);ǃѽAOﺴ4BU\ۘ{>ȔF|rT^^v=#}#mJ6'jEVH]r.;hHO2pl`)0?cWh-KjэcǚD&Zn*Q<ZG+2[2Q)'9O7^F;uߑ.> g_E̟d+?Āp{߆L3Npp~; ơwRL2,X -kmb>E !T*ք!>8^LI dlT,q*+N׶>~LW4Ӡg3s'|:Dx ꩻDa5`ɿ | 9_!(_SW"y֑vuc8aڽ԰ye=c\\tm2YYpO'-Ba -|P&(Pq%HђBW=iS `0F3 1 -Ә+{XY2kY|uS}.?@O߁2``Aa1 -5ZJTP+Л^4#h=up,08XKt0Ļ75LU$;x #0%uj}4d/K P@領F)AWJ) (t" EƖF;R!"Jh`8 AhyǵHsq'*.Zoω7r,ɖXD8(g?E,A660G \ QJ\ Y ?ψrȝ/j:YB(>\E t1QB0MƲ!Y 2Pͺ(EQK  gijoĨ;Kx,(RfBҜ0SD% ݂5%;_t߸Jkx(i ZG>b9 z }z3u< -qz:O|I~`V=.h` t "7N qQTM ֆAH$2Ī3l5c ~[>`-c5Sfe~ #Ɯ}Ƃٽ0L ƋBF cJjH}1@LIJQz#r߇zVM#ٳn'AF}xiۻnzЂL0X^W6!``U8ߐ4|3-5.!q1GQkG,7[޸VNa{rq&ՐΗB;_ y4F=֧#TX`̿6M{W"H7Bd]沼5_g.}ʎ{fk"f|*k1vWZ~޴b.#" t?>1>Kg O.Qx,ŀ^ `38xػ,|+>Iq峜DKm%8opks=}2f43J>}pIʡ^~3G 4@x{;W`2@_VBkcڝ"_!y٦9zw,;XýȒp>IdY>--\!BEݔjoBWhN(Ԏ}ɂWD+,z*2m;xBa"4hR~/C);ªțfc?]Y뫊]H[@Y1BVaߩSGB:j&Z4y/#g$H;$l61ױTW*H}$B%M\(/, - nOY xb - ;jp:DUʎ0\2ckY,ͦ&k\wUEbʊ:NFQ _r*(VP~[|(B0l5g>BU t]*щvutc%߱0Nkc,l 3R5n^Ԑb]P+uHd|&3e#30x=KvHU=ki;m_0ptn.>'n_Zm}W ->H[4@oƤ`GLspf=1Ze_LRs`6`a(nQ5 S≧3JӾU_zk;qolcoLc9a91 5Rk)C:{PWX\(nvW(hdDzs۵ DQ:8[3.O08ĵ}ݶYٶG1ֻ5]ƶD\"ΡKԼ -*/= 0"&\Bֿl!:!#ԑufq:': ;#;?:kXݵ&$F$W1h|~z=}?o ? n3b{˜XFӛ̊VsBs>)Kk ~ׅBy yV?Kv@x D? ŸBG0qLd0%c Mc{ xgu7v+{u+}u+5=Jp_A_F9\w`l7@0JttTa4F1ned8 Y8ebIeI 52@cs'?25P@` EԔP\ 1l1 l01LF>ba c/jwJRC,Hn!? ۞ Fz0EYf&\TOal>/r* QD('UDE -尢O\\DEM‘0Qn ƹ28_BX=&P3] ^"°O,ߨ[VeJڤRUUG/Tgs⧪+⇪n}Sq[{jVwF@/eP`}\zi=u\Ԏc|p<˘VtS~U*~QV@do}v5uv^ͥQw5y2FkHWɕ(az{tGh.R0#3{g$inuD;nݽov%n=N7coK;bOIǶˮޖ]}$7,5ƣSk<:OQ0-mH8&dհd'dXdYIgs3e]~õ5NW7HL4\rA׭][ަm}^9U3~fݐ{S"6&d2:HF'6Q7t̓МЏyTbq~t[]͙i{iBέ\ΥM8t2&}w }q_GRJEϺ4tKGtfmL,+zwNq›NxLdnG*/ZN~I[rMuG5}qIK@H &8L 5! !!!!B-D~AumγMzvqwHEӒԫ|90;Fzx5OռQQBgC$kP|sKMgZ;> .=w$e@q<;eb6tU!ŕFQ=.Q#U"tGr VMN|D#2fo+) ;i Dokvpa2v\wz`Ys:P{qG2ˬ6d:jH343!ZHXi\:!$;D0ut [8?^|WA ,9ai;d8У;^rji&F7Q*Vnbh -CJdCJ,K!).R.>H_RT -DQ!5({AW ie$J/wwT_ZIqae-Q(w=|kXhރk}twKfWvzoUk%ƜyTe7J| EueTam5-hftd֌W RUXz͏eDL5;hϴ`!@[+{} =L۪_9w^ܧHr+pڸVwU9)ECi6P3&4l6m&lbB&fBdk*=[\?ޅ;`@Iv0ʾd Qџ\ЛvPԓ}-bnV>h0-<m[8u{\r#?Z_#3M/eȐoewbp8S-]YrNaUy,"_M|Tn'UԐZ\&MaSހׯT%=Oz{U z9gIO1!E41C׹z. 銌hԯ-iw {=k aC,CRDNئC -GgsYg./1n_nߘ7wϰ_d8"i46[4X\3 嵄 37%_A_Kn^ ,Pisc ?\tN B`hDSEoOu̐&ʡ'Q <8H1&FTȅ?G|&^(=7ت.Dv6ltvS!{lOVԆ!aP~`*ZpӄqtL:R͠LCsR|I_EuqӱKO,^&?u-Ñ#w6N/►*♺2jw*vEME4ʈhn -ҵПƃL##Z3p'S2'2h,ď,Q ;mu\ݾU{w+igaˣlnsۧ.eqhWD>F` }lAmstl6+Bx4sPzd\yʔŊBb5e Λ]7w+w_cmBKI"KPUvv4 t6L睦y 4,B8,pc7p5aFrD{̔=jG|Ŗ6\!uSiz)kN>*ve]]q+aMLtX*b72):F[\B:qP?Y@PU/bT5?F37Y-gUYv|cVtbYk25X2}ۗiӾ-K;4NҌA^6 ru КHW?w|98/#j8g> ̛fM -KeKr?R|Z9E5.fo/u$F!@n ulJA@5@XYE#.`ǕxZjUlF2Z;sng.8g>>&u)1$~*2~DV]Y$ILLl'bopk9@[D;W"!+H^૫QMmG3ӭ>ȵNSWV:u.NLVH:.K$Mr"91{b7P.8EC`{Jm]inTśXVʳmʲ Y咢LSAFK^z:kQ;kiZ2E]\PUnZV=CU`QJV$YNM.)g')v'(ds8]ٚ&3&I*`Fga4g˨}=@W:Eh[+WV(DVerی"ԒJiRqì5Mh'*o{DZŜb'z|K}jpvAH{WALl:O^UniCan4:vfGɖiY6I55تzv++ETuQOL9DU0DR/i?WS (l BԶ۠rW~>oJn -㬖u1D mRUjNaug}Pw5n|5s U3b7%zCt}v=7[*J5PjfqoEz S:BL6ψiO3ڐk6V*Y%]ֺ)s`7΁-Zòf&^l'~L !߽PvK'^e.=ӑ#Af'OЏl=R-4+Y֕mYdbkufM_Osak%[F~mf6zP(hU((H"y5E/_fm7A|oi=3zj,}{=e{g7>=f>yz LwoTy#@D rH!O.tȇe|5r B<x GhfL{'0yZLA8 c< -0~2$o򏁜a?O@ ([@z0F=7 30z ׋z9cQ- 7ö́1j5򄌑+)?W $ïK(ۍ(0` -2=1 |f Lmo08qLJ8 ƍcGg 0H YG6.Yc \#'+q/?dyTSWK¾!/@ Z*,ʾHHXHԸТH݊8mک^ENw~NrOqw¢)^/  (sHlCbh&M+_<8 xKKO>Nctf' HbGZdAC6=|Gwѽ8}|K ߑgcr?F>c!SMA\=iOcOg#SbH5|'.5ԘhZz%BSE]3QDtNSEWG-M1fO u?Ax}aM>nۓO_)S 'zRC*}I d+d[}̛~?$'7$XW<.z60s;pv*f} τp-#k -,IK`btV`Y.u˻w52U&߽6]n|ѳMF<My~ozIfC޿Ʀ76# uAmaG-Ƽ\%9Zy%FjM0=favo}OOՀ)c>׬>~?l\Ȃ aha߿~}'l_o0'([wb+`50@hlFcYqeF9 3Йۨ(֑p< u}ֽK{BVq -nn -+ogY^G-=w[k [?0^c_t-ě:aqFbe=-V؝՜vkNo o ;(61'ǖg;͑ض9 -|/=7SЅt>LCO!l/5ѓc;)մ31bGں=[n[cm\L_&X'\=* 1X쁭! Bwa0$Ѱѡ 1&T,ҨCh-Mfٚޔ$7'V94%6ҫVovZC,nKCQQ}܈.K]¸c]"0Iiq' AR :Jm(sEM^ğPи(Ƭ Ų\+gjm܂RZW#P BUQVW-Su=#*x!Tc' jLޤQ8' •\CT" RFM.P1ha, 1(6-+Id,[MQPSV -;ej׻ȵe=nRq4ew8],X\iP%28Z2 XNvQSK髂X& Z:\Q-Qg9(J+FuiWIcn(YS üIwypB{a4ܷ 7XFU7(oE42 ÍbrkS,klEV^UM*U.$W4 *\ݢXq04k 3!A4(ADqpU֩Z -X+ -8"8KlUk+.D:Zu}_Z?y}r?hL1+'Ǫ$;LZ_3~Pec^:A?iÖ8g~&h;+Wƒ&^I>7AR9{u{d*`}¬4=f15x/j\jѫ;|v G X{EwϔLmn5l%$ ݓWoy?8lJeҁ529ega:__qڋQq=C89NwnXۚ]2xuj8QgCls4و<+al܃?/ b, q=0DGcr504/"~}Ts{re.r,EvܜGD7H}zQI;q-ri9Ѩ }>mmV ⭱5^tWtSYNq̟Xbߢm_6*m管;k莿+gs' v}8 .B- 8Dz 6PF mmmmdۤ}hͮnyc!xP:շn+9 -d ;H΢l@@ѺEA0[TV%=вdKƠE4++JDuIU>%Kſ+ -T Oﳿ&3{_3[_ wE .R.uCKɗ"ۡrAWY E ] ombj/e?fSXhf? rh^U?mwfpػ>pbP٭P؋!vI/3xG@S` -j'hjXU5@¨#񥤆5kxk ^]zB/Hf,d~Љٖ@ˀO&|P:t^;5o -@] a\:$dwXNR]% -RJ:RpUu~ߜ%Hx]/dϦ{̽j  ~O9^D.Ue纍.O<Otc BTPwk`w%襸MtlVGKf#d<3#Kwa5,Ն!]jr}va2v7Ἰ}[S-.R\ -@!nӀ\!Cu~a/ZlEY`<7"{n\$n q͸Ah?J ŀ2EyuŹf)4S6b*B:Ul| 2ۚ0#Zŭ i4UT$wT9Si̴MuڴWUb*PԅJc~ 2W :b\Qq}nE%󖱢2YQjPYl:e^Ɯ>iNs8ar8.hvO820|aj|tmGF8BF"xY;ը&(n1PO|3Bq"zQt8/ǃKGhE2 jĪ}Eb'{c\jl!B!$K@$6Ibر@ 8X$vl'Y&vL=i&I:Mm433{y9^+b?uUL$L(8/~?b.JϤTJ> FI l -|,Ki #ޖ_LAYB e"dDG_ŞvQe sIZOKB/yȝK3kFJ0}n3уL̽{T -rr\9fW 9eAEEr5 z!֜l=+;ŝ\2S cr0GY)kXP!JEBlkdJ'+RRD.0ԓy 5LG aŸ!5Gh@h@DЯ$a!0\̜/񨻍(#AḦ́˚Ͱy 4iӥr)uQL6WlzUDQs=\,+ {xw/\: >ulG<>G~=<𞍄r98 z1iVIEזƌ3SZPJ\ܞx"Y|RԡZoS'&>Ij){|K !hQ< 0*A-3^d hE0cJ!MaCqG_NU{ʭ.m#۩mv4BfFԤYJh,]KNi~ɦ[ڴ߮%'7؇1wޭF0w]żcX[kN&U&VfG TF4nQZjFn5r]FЮk/'Yt6~5F_EO,g50_.|\}DW1y+Zu/iВFEtUG9*=QYhc5T4sm&Z@bbR&# -ӏJ!@xU&K>zpu6͘79o=Flc 61]ASF4[˩ -ʪnX;恸Jqʟp|&]ՇȗIz"CG/_p3u8mx 8لǚyRHG"^mQlFY]gTֵL}qq:46ZGHX#*qe_k%xa>}g6ּ::1wvw ҠMζm֖Ljn=LnhG; --Zeijg[nAYè>_b9Qe5^Rs|^b;Gxa}x&ּ+?1s [ܮhhw~{I6W*֕IvFVvǘ:˩ -zy{-ns[ -[gDdZ$E,,siJg|XHCBr<(ds r뀯hf'07!_R:WƊ2B_}(VM* 6U M Me0?;ϋgnse@@Ӏs%`-TMP^q7W;AT(ĉff>XxkU@c^_ c?\p/0Qz:Ue@+ n:ԤnZpC͐7݀3!/o)ca؉?DZڏCKswُM>0U﵀&Ѓ)yocܤQ}E.>o9G윸x~Q`:ϞWXx}ͼ{~⦆5i`M󞬉"CFQl`.~ <_ @]Q }Fi -ͦIٴ66*TL';1E;w<;A&W E8>UQ1=H?y,NxdJ<2uQ-R.iOeEBvWjz/+/ x=K{+~rK NX2Z*L-!Kel%]ϒ%#/X -|* -})v\UlSl}Mbc#?4esZ 4tU\q/Q]}IEcdOΔﰦ)[+ZW(7[sUͪ #s5oPtU]*60>kt&T Q?wQ=F*Nm %4N)h"/_WfWdkr6hvج o"nYo̠6ABmАc̿B$Q~<)p0EaWHiCxڰܰъ_({NV ^ -]dLk$d>=H(aAha^S}ZO#=vn4ݛjfWpj/s'Ϡ?FJ׀7GbCdr#H91Pf蛤^'Ygi3lz2 h8;8R}J_#6{܎~f췏l:lvژȉ醕1aRVtYFtbaʅ&-jiّ" )+G7Niq4%CrcG ;ғ=FYcP'pFnXoEF|O v"-6Q͠hfLΈIM=ߐe41zWCR[c@a [5{砚}>)8 -|`BV `)-,5!Z>ʔULM7]?1nݗbWq\>r{c ;ғm|/#Y.h=?goÌX<5/e GAkТ!#@ Az@TBt]OZa]-3umn~L _|?~i扫t$))2k89ǹ0ՒJT2k7gk[=LڃYSL^&3iH$%QS{ Krٻ>5`:d1UKkR$iAzc~97⚣[XVu'4i^ԛ4#uNpK J?sYIjeC?14LӱظP\!?kԜsr2\ VAZwmꔌ5I^Z Iz-Y/(bkی8(bq1;¬Ay¤c> xc&;b|G:1SYQ1#:As9|ҩw X=|}鄓2v q~ x́GO4=ˠ5½ -PBEE(z<(O=޷z]ɸ-w "N! t;< Ji7N}7PHI2$9CԿp;7qBƝIS0"@!tIeKo4pe" WX0/#tpL.#?o05w1cbzx;~~ 3 'MJpT,=/^`Q|9Y0y\t$o>r|O~|F!Dϵg/PdcE]cAnArKĂܑlX Y,?`/G|b‡hEE>{F)[6SDϣ̘.c x6o>&w -C}1<%ă=&YEyCp m49q42,&$ Ud=LZțNr qO?/ -z%qx:)$D-"d% d+APg?u1q -xk%w~AE?4tN"|G҉Xy8&>y;uvQ ?uR8ۃo>?pnA+r7Fx@qnT\9C41$[1jlf4h:Ӆ/u<;HT}Pem:X5$p 1$$"B"QmCToB -~ZC j]FҊ6\lU~\_qQBYOU"1J F* !zR}/&4w|kuWa\QƗ#.hVs|يs=1|Nw'#k"uqb  $?-2zp۸%Wb7;>ŹU8ӽz4Þ8ٳ'z^m8k:CO`kO]"_ǘ٧1-O$.E&꟏Q8ÑEOg`f_BfOr2lav lpxixm71Fd7w_AB> ' ]8\Q|L|4h9'6{6`ٮf:S-ڙLv.Yݤ]nnbkwrܺ!g5CV_>T-ÈG<&w銿6ZY=[|0,Ga_pҍ6 [tyجs0t%zmu:vMl*[/m[bJ,ѝ.ݒ6x,m!J? -O?$[FM|@380J]b^q!ވ,fM\2]WʶUqKiB}YHҤ_%o5OW̸񢔐O%RkL!jy{Io$('ClH&$%IfQmNpM$2BZ -P )Ҟs=n#ڌ2tŪh1hNMaf3sRNaC1,36 -K5e0j*` -G11E٫`Sg~+Ofz^b)K29sG1sӐc 7_k*ԘҐi:A/OL_LoWUPQ*L,Ch>rp:>iBzeE6l r1M4侍>'d[PJYҪ2 ܤW(6uy8ƓuE^W(6ҜN`g!XK- 5?OY=1#?ov` UyvΟ-R%(ZBe“LUQݭqZ>8,;9,?y™'ʝQxɷTd8GڳX@~*P`ڢQ3a6=$fb+ -rٲWZPėX}5 + .ka][׫m]NVM_jUTXE gܤ:![G-^]4:u&rDiי; ^Q%k}j_ooUwj,\ub3^wY Gr`C3}Qye1LȸfnowKlE~F/zGn)\)\*ܮ6,x2Js KtNRS*4~$'j+텒x|Q䋃7q2 t7畖Kr!Yw]Q{;TiޣTďQV"_ <3:S P4vNO~%npUFQ9FXҘlRir* J$?IRF*ErVe*IXPT!*E9!{:;)`Tҝui />aB0H1șldBLf(5\ZO N$I2Cp0]<^PU T$ -QSo&7h"i4L#UOs: {\?a0G!=p:c 066)a ~nL>\yTƟ3, -ʦ0 '-Dk$F5O465"eE 8Hpj%&*.TӨ(1> -&y{E -9^٬IθI&9]hBm^]u KY+ǢVwdX'!-'Y00g#YT:Gaf)r -/lV&TƜҘXe\*T%R=PC_7f1&yeVr dia=H>}BR8Ο,$}oɽX{c?&ؾc~RĬvywR@Դ`5GQk׋WI%0PCi4K+MA/@t Cc4b嘆HG;rX/usRغv)XHk}/q ;z8x@Mi3_pz"©G3*ViDhe -B*"r8*Ǣk$T͆U[U}VRS0\$1θلyY&7Vlc<.=c6$z =08WO] Թԩy$&ߓBwp_F;~v[.vB-ӎxJd"%"SB ԩN 5j{q|˿C?N?D_/b"Od -fRg>u p6Q)\s;SU[whWp}+\D ZBӅ9 H^!M?Ө3m&SǎXC56sjnݸX|8%:Uj- @oX ^zXHo2L77Z3X Ȧ -󸮥F5*phf,Nc'Y@*o1zuAS;hvcGbl ^;CQ T6`sQl -n?Jp!! "',Y<8}hHBmcj"G:rĦ lZDB4zT픊51n(T{GUHOic{WT^o}kd4hg7Pih2X8 PbxņiuBT#'Ib9/a2a"axFq-ENcEv:Y=k=ן@|U߶^pĦXcBIXcզQ74QZek!0}$-3-rPe*S,1mU,65*N*$Pf)盅"JCơqs5>}{`%v,iȵ2j/e[&IK-Ŗ兖 y%[a)-%yjťeyޙ{D K] qDpFf`fD -5.Kq-5zXTkĜ4mz5m<96ij4Iۓd1w= |zemA6G#ulI1kLslJFɄU&3-X,VUZMI[a(wcm<+1Vl y+6"SH"?7wg:xuH?6#<MXmE%4X2EZ -S,7{2 -ۼZ[b~^*6o]BeKa?LK^Ze}%s4kahEI٦*t۲mPj+KlbͫqֵRul:lsm/ԬkCzu]˸9Dq-빮l-#QW -eʔ$#JLHY"8Xr]+~)W$/U~Q)ʅpEy'<[!܃Yż1t7|ۊQBRu&T@j:\L5IRԀXݭRYVxO^YՐLܢߗuJ@o/K} J#Pdc:9pHG#KPX&.q5َ,Gjo2;uq.,q3l>P/^0GO4l^\NGV G3 -w><\$丌X]9bCJLWcqҦ6H&gltҥ^եOuo4gH꣰+y|'{X[rzTB^i$1qO➉ŞdyRaX,|!S$.TFO&ͽNkpoRrFIII>KB^ޠgS@-H zdW BVHX+' ; <)XTeʆUVU(ebNL,n{OKqޫ)ڸʐ6'.S8\>84ʕ] \n ~OFo }HYSs >Ź͚پSX[hbBڟ8tf`5 |?` 4HnX< S1?$0o.f0fi8Ycc 1Qu@fύC PD3I&s[1efƌhLoiqڪ3fL ((@Lс<؈ =x*)`|W ~KwQ{s+=o^[6 Q1LLjۢ0m,&MSX<`*30`1FkZن;aX"FԎG=a֐Qe۩BFT'%`^ v>ۣ0} ڣ1}t'i;w,ǠF ؂ h?[?CAzdݛX'$b_f1G Dqrº+RW,] Lz?]  |d8paD8vs 0CK77[7E. ̹3_oI}^3vi=EWGA a:-Dr:0 3G_l]BG>Z{#=`7ԧ__ DQԾI@!j{r aCя =aOpaȞߓ{G{]E Ybj٬5{#|Dc1=GO>g`|C x/y=dO4 rjbE 20*;o!"\>'ug_KH2kDT} ** EZnnhYDQA@B"2bM01rRV&NRV8ff\*5qܢo~T{=缤O~ld!Hu'3enDٍ^ӉYDdd"d3AvtS"oq?xW?" ~ 1 1tKlF3`'5ڨqssg#>mj O9z<&ȿ?eg7N&qdOT@EꬤF5j8s#5P{8g;V!}i_2:2G;C5ķQĝL%_AԌ3sӨCBjF%5jH-_'QB//} Moq~$7 /DÁc 9}r]*|=c\| urQDUԨF-5>V9wd4o鋫˴wi0Z"6;ٙ0eG'\;kBq5JN&gͣp y]U -Dh9YВSG|kwqlCyeÆo$O^17x Ұ,\p9bu,ǙU85|z6S 9G#qGF^Qߠ1] sh!ȓx吻|!+ȍpy~.)DpM1lt-C[ :jtmD6toO$xm}qoc<6WL7OfRߛ70L.Ot%wW􎝆|=^ }`M.Ůk:-ScJ O9Sylv M=D+4xB y4O3 : -]&s6L*gsf2ϴaR4{bW*UY[Q?kP7S+}]s_\uS^})ZO.;v{{bs%}4h1' >VlDoj|P[*BoηUX3P>G\=X6rޏb|Y,yP<\{-]~tS\ `*aRJ=ʔXLAҎ"eBY$,W ˔br+-]--YtI#e?!,CG߈.10vƲ1-Zָ. Uc6C}PUT:(PLy!E_H^X -1cx@k[Hb[fB+:q#1&.헥{Rh2q<3I+s#kvxa>Y=DlvBP&-~,"d%ĞXVjI 5bԴc1ZiCvZ3\o1\r{y{lb>Kz 4&Vq.]#4"!RhX0&>'dӀ~M}̽5G%]3G%>4G%VhdeT>` - 38E<gTJ&;iHbR48%LSh@jT6Q}Ҧ+:mҲ+3m),)tUShShdArCc#˰ Jsz2gکOzguStV_ٱ -ώS -˙МSPE9kS+c͹,2L/RXݲ|އB}0 f8*]A -, V@a Zƨe|,3mIU7"ue<-\GacFgWA+%r:!-;klql}Q3dcMW2UP -#[yL@^RE7_W?7Hq؃R)`+5okTg/S |!adg,@PՑXuw\ xº2s/)kS -܍>iޖloaHa1~R=Ci}_CP o,^Ç<OXI-A GhFoz<^ÒsdwT2GvNI8Eag0?:Ǚg -hrM@-H| -/:'֣?<ŕldllj֟%hMFg&9GEq\#dG(+t|+e`؛=vEHrsh@:st4CjQNFi-9c֋]DNg:ЙCGaoA:N:K(gJm5b>i-mP՝ U|ǴUl';cWC(NzM=~WO2|u{7W ?w1ԄZY?T}40VEq*  zM f*7h+;8WYEYy!GsC+-)%)a_ڸŵ7+x(0fl#Yik͊P- %,@=# ^+eOiJWZxR#2Q>_ -h- ZE%Hy!@$ $BТmN!Zҭ͵{3nu;֞vNZ!~>Ͻ`͊O= S&',V iw$uLs0^5K>[R)G{Z -6g-=Xaڌ -pŸQ ?|mX -o^:"YDX\f!U<ຒX`d?|lΞH)EkӰ:;9:rLh)GCN9u]Q-\ʰQjIY̡TP/*IT80Tf?گ>8b팣E5yhV-O j Q`e<y [v&*w _4#2]Y&H4cO79rZM;렂Ʊ _39j&c6.N:tpQæ_/'EYQZ' ̆wd%["G+ ?Xu ;i& }60(Ӱ9lT4 -a+,ƕ(5`1h(.z^ɊLC2iTVh#HEc[LyB~'Z$[s8ܦIpf -bLa5eXX0QRBq*[`4(0zOd:yc/"ɴfIJG=L+s3Y&Pa0JR si:K0PTf̊ʽЗ?]yX}"z\loTBe(me-rA/{"z`-]c,Mb{,KQhO|+UA[YJ*WC][:l1pYUD+~g9 ۀ}M.G}\fN*KqC0TGB_]"hj!&y5*P9P:+ZlW3 tu WH=*gDjUO!wIBFs/QwZǀ'Ɇ5y0(Ȑ_{8CAGL V;V߈Vx2oR/#{Z$y HDGqKU(=C$ s[*e^ Oo*2}QHGZ"țRڔM*,o6`YUHj"ѿK['?m$4CB$!s;ڹ'Zg[#cR3 Hi@R$bi,i]G[Xܪ ;u Fl 11man% ¼"EB̺1q}~ux@s `3]9 ;v#%L-[Jpmcr60%&^JI$"|HL8x˥^ȩI @@ \*""^b2T@W=j>gmt]36v[NvݦsT|?D~;K NH#H3i#ϑm%1|I1G,Cy|G3y~g_2)ѐ,O"ƯgFCldbOajWL#>[_0o69aOƒ #5 &$dP/:jTι_72~w1N.~vp:kߤ0ڍ>$%qαRgrragaoj^ԓ24jZ}\ q>)tvpgp//^_ğYopjG708=]O͙xԓ3I<87+]Jjbf@FRcn)C\vV{k4Wy? C~9wyD)B8%3/ DQU^jM]c:ut='ye&I-`SGch"x^Qy1H}^Y:9?"56qj66LubTZGKB<kW)hVub]X1eG;Kf ?6I:E1g ~s7ڧmFeV -5f4`Up>V.X6!QҌ%!X50<_EugxG|Lw -d*g> Iǚl)X>#"BTGP\,SEsajTϭCFTmCyTG@Y̋ Qo -O}؂ձ!|u iKd煕Q~X=u1cQl2jPkDe qN̏,Q⟠Hn (D -•>.SL >{Hh%kS'F$ $Ơ\*C4 z$d(OB܉(L\|2dp:F87`O9Ia0x'29gIigk譞>'B>e`H![BIFlp&9H*F^r%K))w"KyUȢ(S`n:ظv``볎>VG-}+Ǣ$t(#R#O -J٪\Rݰ#+F¤QsDyUd _Ads6x:ْ>(}T"O -:X5)hĄLm6KaLAn6tۑ? HcNAF?V'.w/Zd=F.V}0,9ԋa+`ԧ!Ð -}Bk\q=LH5|Q4@A4 " F'Aj?xK1#MQ2gLdL3a Yt -ZL ԙyH!RU2d"9k^>d{|~y0Bc{?wp$(^ J5ři h!Pۢf"Ֆ UlRHqCSyRHoԾsGBl$va3#{/u+9Tq/𹼜<\z1:EPyCYH΋D# -rGd $:m:!qAEk-b]g|A#% 9i?wQ{">9*VK!G%=B$A ;y @TQ `NQ#"7#xŠO!m9B!H@+9Κ~/;9_ -4s]QQXWeueߑE=-fQ(̸ `T 0q8QU bզAlVMM`L6{bCRc4how{ xmcb-fJM`PW`ŘՔhSE(4\ldZR[_yE`oجloCkiYΧ6B}3UXO|)uF(6VvЫ -dά<Ȇ3D$ͭlJabWS2mzAWڄf_0'xni]' )vba'luPC!d|R[Yp156v)40wIwvjQ:jXG .@Z.Z}-Kbna14,ttN_tb\KK34@ o/uahj!j6pENc$\bq'-%r?= kgY,zA&Z@q.IX4iÁ=9]lix3o'3#MF{- ~FK.wuNĐv>Q@$M1p2 u1(}\4׸7qț|m‘3}ldIƿ>_{[4Ү)yWP(]%| [6]?>FC#c61qF./l~ `0)`(bJzآ~d|isY;}/\pedZ AwH0Ŵ}k1˰_}- :55u]|gu N|OCx̹7T} c ضa{.0.S0I v͏C8 Zנ"ZIJa/`߈"ih~1/Ƕ \M?Ч<~b*-a8k7刦NƠABc")}gcfcߪ^N*ȎSD2P-T+nKK_ϡ1L4ʓIg#?EhXrc;YvO^Ö}51%;JUhi#:cFg1v՜\; keҧ.]:6k 8qW:Dy{+ePvw9] ƧimZqGiV9hsV8s\ -eJ]*TNuA2_T=z6k\FXᷔaw͆SUnZ=̣U9R%3EiI *4,\JhX|~C9>5fSvVfspN_FъpuQ7N :sh@ h;3bʹvK| ]9ʎ UVd21fF ֌=Vi)=MSf)kRb5)\c7+9Zh|qݚy};`sآ#;EXojz\kj|MV*%~&unS5Qr5.at_7W=hTbF&6jdѳm/uT@T@S2 0 ]`H䲨1 `y ^K$Zf*hY)=Zֶɶv:k%ִܓ?>y}}˚ƎL}%q4bb\9\0 -_EUG$+7ª\eGڔYQ5ʌ5EJTjl,5?NܛEra #NJ + q-z -)?zrX͎1*#&U,*-ήԸjYR)JNإ2%Șx~)S'FNm[q88GE9^2LTaJ3D)Ր KB JI,Pr\%ͪiV'4˸Q2ː.;?^b911AaaV٦0QG%#dɔ$cRf%*1D ɏȐRfřE[*| w)<@75؇~gא2jSHdd8̓d4*yS~NC@SKoޖt/*zXlȤ,bI&XP,cR4QE -*(M+NTV) -M-Ճ%4CJɧ䔼K>yC6&35‹JzYQ΅Zz-X҉oya+>J+)5I0=hD{&3SV$_VqOjlyʳ<˳:8e ,\~Zʹ4\SHl2y1!P&JOոJ? HyU%ʳ*UcQe{"n[FVKիUF.wZVmhȠF΅ǩzr@LI1Z(7T:B(GFe遺 : ;лX_mQg?ߎI~%g#=Rb|J cʥUÛйM\\k1>$mIgiໝSQ;vMG'$]0P`C@uQN w+ -}|7[ًO -FwJ]#y‘PRa#> eԥ8 t4v71qzjiW|?-/҃ -܏WO1xNA^SIAN$'gR,Yhmy׵u/`ͅ35b%Ұ>Z ҅\Opn!p8>c"5ec,ýKKf+ų`ߐoO!|z-Kp\uCѫ 7RnWosܦHv;; PeP hfh(MEyEևb7:󺮢gKp>5HCax$q`\,?Yu !yaMZ`{!`{9)E h̏Qh;:.iofp^'Ѻ7/}J3G~1`9U~ YKm@k6Ӣ?ڵzGOѺѩlS$8AQn<r_ w_pYX;|r"𓈏4-"el -ֱc X:V;؎t^*ׅ5h 9$ V,a߆};plՐZ| -]ɳ|kyF;lݘ؀m@an_L b -M&kk^5SWUv6ҤjTiViӤݴnUNC}>}}.Wy%z"Y/_{Ob> -ۻ3>wiJ>EOOUE79𓣛}!\+q~F6e;K 0"WҽMyޑ`HRsxx/Yڱ8]c~9Xze TtOҢQB|c29wxz8-RLSγhqyi'Ooi=lff1s c`4!F?јop4Vc:Wy,=|`oŔ>1 D`1*u`6ƎjLza"ΏnhF0pC LÒ{_CI"%M{MlbdK II%LTc,QcI%:0,APڀ!i H;0 EldcdsI^EwUtmDG{+3wLYfV 37C;1"##) /ՆCГք6tw`z?:GϘ2іyYwz ^ω?9B x6`uKlfiq'L) (R‚@v9NgѦUGޜ!4LE4?-xo s@Ïy uQD\, Nݿ{xmtMc[:oCxhdj2q@FV Z' -ܨ-ZKg1TΡRav+(7~@N<_&-7p%~X Rud h,LGz}jTpp2Ԣ؄ -c;E즣(3@y6uX-/>K%"Y=r`wps:T:&9&*Mp a7Qn.CŅRKlm$iXga]GQ"}opKcm*q-$ RG7u2VP֊&E&.wm  li&IX9㡭BS5uv۠T!ӆ^(Fp -Ho"!R䳈=%.p$[;xuwIE덂99r(ݬ«CׂL:|Hu!7 yI$lBr ҖHlI-_Y̷۴?77s, ijڥZ Qn Y8H!ůܟd2pAф;! !3 OqF_|g|AfY㼓#VA}FK=J} io{eD$ - B - -D !L`0!J{e7#bɽ6ɼ0Xa,L|qzJ] PSSm$;8D'!b8 --,FI> d0 1y7ȹf{5"Iq[\9 N98|_%~ / .) ._\Z!,8 ]u'0B(5wN FO3朜>dPg\Ҥ}jCtrt\\ȯkK8D??8{=<<wrx\O &5y vh}q- t=! P Fj0ؔf/TdV [=v]Ku_}K7펝ץc+ XASZQvg+tB-l7?ckncgX>Ntho+|+{n* ^k踂?t\B{lum29wtt"w71pyG\Vx塿ۏa -]x: k %^i optDoq>!;p(cv;i w|-88,~>^rlWaد@Z=ZAXf8Z_m&:-D`kwp~ >szK?"'f)X~vcN^F[4Eт&tl2!'*,42"^q6Q{rר_\1a#bP Gcᨆc2pӜ.E(>;Fvf|¤n 3a#<H#8 --F"t)rkKwմZ)eZTmjKU}*{lVEσF]W=x2undy>уnOun5W&h{j3T힫qCU1Bc=U9^= y.RJxh~Km**qtS>TgYB*ݨ|V {Sy+0w^s6|;:rT -?Oj_J#5ߤb*ꛪa}sTw+?\Co50hv++2*3䞲B#u jv}L$7TA* 2 (F588CB)7P9!_) t21RJq%ڕP -xwۜC^_IqD7g C"De(#<[JԈ -DNRRl٣Zw}L֘Kp(щCp`<9k6d>FBr<̨~J2(-*F)QJ+):C ׀r%j"ǭٴ]&gzWq2}61dZʧyo":*1CБTCd3F*'k\T%b.TLje΁kJJ%QC|`jK0~a||}XzfXd/,`0oJfJj_5 TE -WQ%+@3T.¤Vބ;@_Kڕ|VKH F7D`aLr_hvȣ<ȣ<iFF6wh es-**12eXI3b3Ism2Q"@zcn^NVN$)I&O/\T ypײ ~ h -x Nw 8 ^o7h:9ϚQL3xm\|pZ+>V4X9np 9 -%pb]79E|Fk.=tqߣp_ ~ @z! 8d -%8b&qO, 7G;[s}F7}#8>oDX׏xobE.!}F'W\G8?#} -y 7{//x8xB/?xxd!]ʥ?8 -Jqq`2ϓ9cʚv鷈uXi<^^G~_['228}@-1/i z]@"b#v91::f)d̲%8 )=`A}`7x#vL*%x[fEA>Nlb=Ӊe2į~xVav];aA-63ڧamFnf:iyZG1cW6!~>gbE,C %F3QVXn8ױwl=>t 3mIB6wh=X)p1b8{V e5YЕ(އq#%Y/>`ݍ.F($ p< -G - 68jȣZӴ<G UJ\ #J7á{6h^b{?v[!{8v -!J$D @2-Dʂ xPW`k,@9GY?[ԟ0G^m8rК.5~a_\0A O:YT W*N.gd m$VM{Mn+rޓ+}GXo|/DA]U9fy;kfTW5-hr,lSNCݚ;d\%X mh#aǕ~Iww[~8:ZڲE7*HFyb=41\ T3f(8NYɲGNWfd2"+Y&YMj:.,/>R+цhkknтҖQ|k9T(ƛm,S/My2̣d3[n\F)%fĬШW#^wh 8ӂ%mhY y>̠NSikTF.+_l# 0 3ΰl -(0.D4Dwq;hc9&٬i&VLlkXSi&=iZcܲUt=}kPqQE! 0ԩ,+7lFSNx1WUJ3nUK)rF7r+%.^nrn-d߂Y?=N#_<&0ҧzs+&OQA1#RVg&),3]Y -*հI2dPp<(0C9?(8/<39AM֪lTi&Sy?;pMgq$*rkPHnȐP`~˿̖qHA>+#C *B ܼg9G0s%\*(EYX'btTP%b_qq-OI,WWҍ1 %gحNՠ:iV4x i|U<}/!㤀A+ Р2|ˌ)h`yr\rYX -0嘵rCƠSW.jB豉4/Ɩd%ӛ BM -0wE=\.BD."c'1!Mdb61;-s8KpG`O+yɇK*a@ȡ3$x \ \5\t5\@5д!`u+-M_M;"88Lum6{&P\ U jbibh[6ҋP@/DG=lyC2D-\X:` -XX G} 35ã9p5XᲈXĢ.$ml||<[\ -nm 5ʡ]ֱ@!H]/Y@ & -VxҏVx£%RZq.|j&UL+q4+ZOX9HfF$|6K[w+(݋ < d۹xvzю(8r965]@:r;zgAK2>Ab{婋t} A===uh $߽V3u"o%9KɓFbvI9V#= u̐ǜc@E?eb(Ea.^zCU>_Z>QA\%!Կ_p55AGy1~ [/ g?>q&8Ǣ%Yzq]9@"g 57<Ǽ=f/΀w9Q|P5Xr*.S 8yP  q-M\׸p@E~_).: x~B>G"QUmpA0ҷx̯5c=U K+<.$;?1?R>@k?eέx -^?ni 53|5ezqA#_L -^.{8 3w𗿂8#=C=:n$2y?t,Y?8VrEr?أ8G:rXD^]M2m~A馲.= ݠ&救GZq+YȑLLV8DDRKX%_"6cvv'iP6Դl_+u:~G-rE.9ϢB1į DWc Ğ2 YNy: ߰Z_j%yWx=19v-{E'{Cf$Ilq1 BjrԑdkL76`0`n&&`CbH'@B(HB[Fi.K@%Ye (mfi6AZN]5mӺ}m6MӦM۪}ؤjڥ4G.S =z?y99『w f 8$7el{W('ߡܿ$xqÖ8 1Ua#f<ߦg3q;cX5#Df= MSw)h5졅p$v1iL.x -8K)gYBDim` $]v>NK<n'2LY%u )tY='e*\v/q~J M5+ɢmIښQ{rڒ˵9%M)aES՜USj61m"Z~D XR(j ?R/1~ -b:m:r"8+GS IږVQQU6`(VBZ7֫޸Qu6Mݪ5Ri3)yNU -VUX>T3SKH'bCطEȓ&K"L3TgU\Z_5ZjUm(hک_Qgͪ,mUb{Sg;>S#{Lo&Yg{(C$;I!Qk,ekͭ*[*m媰W^2GJ[SI39OQsWޜw6 %33IcuKZ~vlF9{IW3SNʜ.*8'"WH>涩 Gyv?ʬQ5` Q)-[J&RVj0vƟ^Dw;X҃][K> 'dMSfI,kr@ ʨVz(PRB=J -)1N0uR;HfbM ~f/w_ዾKax e"'q!a$|:xĞqbMG#a{i{sp mx AY2`͐ѐb: }0q8k]A(nbL4n"LvLavL"&i0bK4A<&?åC){1ǎJw ցJ9>c;cܘɋ9.?7FҳB_Hx| -:;_ U:G;0\|Hv,bb,R(2 $y{8G^~;?oسEi㗩WH*_%p p Ǎ' 67%X,e -2X&8ҫ>_{Ŵna"r܄*_a |n]M>gVcB~PW -Iʊt9c/ggTW6\ۏ_ݛ¸oo=^I/G!R6\{tƟ6%inmzK4IIKKEZ.E\1AAȠ ás)`e2q2&sӝYiOs~/<Yz,GG>ۇ;h -{mf5*c?,ks51#ꋚ b԰>_8?@}^Gnx7u6v̀/b@2(CAw6ڦq-gҿu7g8?R<7{{BGeER?.jK?wvT=:uч踂89,C%tz gz@{཮kz _>/߈M_p귪һܷtɜGG8qyqa6WqR6K'Hz0v]_p|ܟ>ݛ,::)tGsc88#8Zܬ}d/ _R@m!B#_y \b3e'"֯MzGek=:Bt5JR=pt±nѽ(\Sݰt*O.r?b̘C"f'Q~mmIG<4vPAo ɠy#ynsmEo - 8,OUB$P]*,Od_ 2\G{?vX-s^tSsd+\x -)c:h_P -~/k$?fOyF>OqmrѺ!.sSc>;\䱧"p᪇pMdptvZf^w@dG\ȝ --a4uAL&cjHָA9ʂ͞*P}LHuª4Z59_'`K0\RE-U$Fp+mw_ղqlI&&cqjHNQ8:CjU`b+4$JUI -$5ȗ4VH%oUqOxBwU`BSDEOƮGm%#P1i(bPɨ!Ft94y4T*Rjܤbs -*HW5r[)\ʱ\zD $#F#ϯw泥8!7#kȍߚ -YJ-*RBK -UX):Jn[rm3/T}RcxGi3-»[1nŌsJnFS'R*U`cO۞<{r%r9|I );AY㕙>MNgҝ+*{^2^5MlkH=Sl-~@ Fy24+iSө̌\P -93*=+4L={l]< K7#L_O̔zvx75RxeXՅ|vꤖz P#6(e3Ǣ49\#L*Yek.{LddHO*sLIs#>|o #c`;3 mcHrSCn|Ĥ* Y|vY -ke,K)EmJ.+x U\Qy|;rȻ chiCG3#t27^RL%VJe,u) % VRYH 2*ۡXxK^n"/˴2K-pg]9]m jF_-CF2֓b$&@>*-JLx_b}^ SH~gCcc ~cUEp>4q*=NsaXFh11+)`bA MhPi`0MA C?Vj)x6{LzӐأVBV7q7 $K%l\xa0t\x ǸcBHuhcC걓zCON0yy@0"dF\1RkRivHMdM4pġӄ&45GoLLk.Khhh k\ni)![ 9<h#;?;: 6+Xy#tp 30hs1 ; 9tG7&4nrхU]Gy,AUEpܳ:^J<a<2h6ƺ gGI'M/uE賏FG.Y'ṿ; 1pa0p{Lߐ {%W@Ca!WқO c *r1@_RqpfLtLRl`ut^o$6hVӐq -8.sfp>rFqخR+_W.0Y āt0Rοgjs;pH}A#GGs"^@ aG>|Tp!X4T |pƲ~kg88K8G<N]zS'u/ >z:=E;N*ңn<7U#` :._ORܠԍp/h=k!G!^7YJgz\hDt*bn 6^ 489x,؋h2GM>:p6Nv4#ԥY EfUR0we mXu8# teDt2!Ue/Z"\B.j(fmV]O{ jȭ7\~t \χc9)2xYŮC-Z@泳R\ ,F}9(48ĵ5xW:EiU5YJϨ.&j$ -n1 BxS(fYjC(i>'{ogG;k}+l$n9C5rxxK;\p%'/\p k4\5hr#{#PN. -idgqedY1@3zMaL$?r2C&X5>ȡ1A.%jTPFgiD a!w+'tCV:7)5C)O( 1|!OwDt.Xm)1PANO!ǁ紆*dT^Ur .eGxGRZySbdSd{< =ZQ1]!2YQ,jLN\r,rNi 9~LSeLȄ*W*,qB=9  -NUHO]pwL,Xߕ|VLl)f9#'CNPEyLxl2{2yeLJUD0(-U3Ui0v|:ɮL1v -ͥB+tr)D]᥊y -ݸ~0)\*ָɽû{Xfmհ2V|ߵ=růĔTMT۩jEZWj^vqq*B˄ΆKZ[µo5c[_U`8,G bK^2ٓ:hh5i|1/jZVXA>ך_,N7Ѧ _\[=_iu`xD@yy_2%ʹx>r؏{Թr`jf>+Te$9 `cU: I ~%ٱ/袁/h _s)qqlK3[j ML_>7\;ֲc4QkTT((kx[w -ሕKk4U@{.J1P╢4 ŗqE`ƎUn\ɼEi]l'${.yǵ1Ja} !Ϛ:mfG3m4I3]4E35q^'$;i츎[u r@ -1T<ȸѹm a-߉MKvǀz(j-|BL9~3p.Q3 xԭGn߶dN;|ܛ}6'Ѷ$3'qR<%&4S|qJ~DzR>ދx/9f |ʸ'yj= kâٱ ]0!,ڣp~ӳq0rN<Qٗc;ޥ`|<\^\e>PF<?WOcq|xiorM_a{ u| =&RK忚6W$dv}*1?X߶i{#_\Y3Nmc} 6>|d)];__/9Գ -3%OlOI' 3d,mB=E;bW8{; -,g_^U*IltBtl x( $/g :{'iv6l`gv;8hûCQO)͠s'I=. \x)9)#+yJ9ۉxs'5ۆ Tx>)3tSI/ WB)t~-vk~ƻFvNZMsEp]z>Dk;ddI8,ybi|ENbWVf{crVրco5(Xe1/sSG j+GYvꎣ7b%8pTȊ*J3LJY–ٲ_h9 -ukTz.?.7i<%oD,!`R8\)`. .jȥHB@H1%폎@TXb/&f:.cK4#1wsb=8|LfҖxxCCxt $2N(mt 5&j0T?CpmG2aEh9K(U/0q&{@AkX = =Y&zfͺ uЭ>HV^iPfPwމЋlxH9,4ٲ5f` ,x808!qM٠)]I l"10BTI##P$Ccba܍2Sc5#&F&G;Τ-gҖH#D >[3F5b( Ab${izз9&l^}p"F;b2!{asE -D&x#8j$,byb!p,dLY]ّ!1CHlp\q .U%NLH-Rdch -^@D3Hvgxq|Dp*жhcHu}67jʌ -?R#3I3< PS,> ؞Eq\=-R'6;9IAzɆٜI6|XdA,@W־+Y?[ړC iBF-(ӊ -A[(oq@j ȡ^s8j$,AE$h~?Xhڊ>ǁ-•a0|!St+R)5D@*zmahCFnlV7qm͐pnyQњ+{O#Ok R>5y]Nbs0 ;P^84~EJcil)%dtUY#Wq€rFtGz](9dj_8`]భKJ7HKwsؗ1TT..(rۮѵ}4f>z{ϟࣵLAϻsƌzfzkfL(քC ~h?j}CJ3E%/c_TVJ*pT_xEy\_^Hڨ;Wi YA"ҭ[l!Iv^يR9$Vd2nqy>=/<y;s+Nw $ ӟmWy0\*c<0gלuN@B! +G[Yu?R|^rrH/坑,~$K]Kn`l=Z5[7q|gUnr"~F8ߛ-cY đ\ೖ-K1Es)`[>zyH]PF(볫ܤ;dqFV Lk-zPߔJK{wWy~P'C8d,ߴ. :J@7 dzqF@` V" 6X ##  ZeWŔԃN~a~qfu#E".lйy.?Xϊ ;m HK=`(tu4G!gn_:^!B@zhCLZ8l$@ -+ @ e!OAx C8~ⷎNs]=/I֣3ѡM*{q6ljK~!}9Ym!!_7Hlް(Qppj`0GXs,D`+/xGF@ҚSШ -s=t##URuMT?|zq+[:sMnִ䂹33o\P7.B *OEtO1o,N4GO\ٞ~pc݌)GR0XQAl(f4 M)h@<׹L"]NJYsr,'%hݹv - ݆/U)|JnPW -x kFEQ`0|=t[ 1x}fpc3A&ŽpJ ~ 7%1,۰PRND,^HU0uf>7웻ñ]zQZVq6 S d`0XA#GVJ[(9 -RWvHo^0x3 bx -p`+gQ(^1ױ>9ږ騬*^x#qb ,Y2aHwcVMOb/f=-ȁ/} - `=瀾}k) -4`" C!)p3:mu@XoQv ngn3w:s+*qBV- M$NreO{}v R` 83JyMO4)XZGyQj{DM {_πY ̸Ӻ|)weUefᨈ.A]]dciI~\w<8/t Pg+e >*7E`S# 3\GHpχHn aKS[K 5uk;mɶcVރ iEHD_+߾U\'9GVXJ¬9M<~̨փI+qijL9%A0pcF"((`77Q#'q h[:-H,n#*Z_YXO -=Vy!pLYzY*K;x2}{"w7er"Iw:GSy\V[<6'Rչn%:溬'5mDtbZL\&$ -ܾ~vן{}߻<%E&gINDHJ"NƄdD] Q!c@ -d *>7 8PW% \ h`3^l:93cM|;egA :܂8XJ[7XI|0|N7w[{EkvcJȬi%J-Q#u|FBѵ<~ԠVTw|_JvV{J,͓ɯ)l/` R|Vxfm 96pL1c3Y0ߜ,/NP[@Qt+eKTe9ۏ-p -Ȯ|BpW$ %IHO޿y:~0?_(gD,rE}KcШ+)J_*=I,?!4l=Å[Pծ=Ğ [ }g OZO$o!xL=5dbBC) Oմ>RIr\r"#;@V2[kclzi5a#*Xm?;62.#:ĉ֙Li_8L+ endstream endobj 21 0 obj <> endobj 26 0 obj <> endobj 27 0 obj [1.0 1.0 1.0 1.0] endobj 28 0 obj <>/ProcSet[/PDF/ImageB]/XObject<>>>/Subtype/Form>>stream -q -/GS0 gs -129.1199951 0 0 133.1999969 134.8740234 237.2716827 cm -/Im0 Do -Q - endstream endobj 29 0 obj <> endobj 31 0 obj <>/Filter/FlateDecode/Height 555/Intent/RelativeColorimetric/Length 42467/Name/X/Subtype/Image/Type/XObject/Width 538>>stream -H[WlFM;HoA4 AEPD,U׿m)w1r̼ywsBFFFFFFFFFFF拿kd6>kF6e[&ZH(aHs>BW|k/'k#>eBVV"ucI FVa/+BoBz̍:lYK1-kԈ:e֊ cF j徱uXˬ ,T m"ܦ)xuHx](, -B|}Ľo5&#ks1j, -BP4|>w*!DPxX!m|1ugPM )?߮0рp#' Q B"ر㟔MB |0ag"cׅBQA( 'd'iN|(0!C!&!m|J1wˆPA&k׮/Jn|̔0":j{juCbR+ aϞ={݇7ڇB@! סaXt!ꂳ@Y` -BLml8p9dOlllH #A)jqmX mάuACXPYp&@86 ڒ"!@{`݁p(!mX;FTSl(,(D}膓>tp"P<;PQ̆apUCD`*hpuussww8=pՕB>8\R(6Skڏjs ..4 - #^^>>>ooo//OO0Ap -E,9TjkŨ0ABsDBcp!D*7H7 -g<Ca66M, u T4D 3A$CBBCÏ"jp&LUPx0F8l4ya Q]A]`8pe삮 -`&B!2**::&&666N bcb"##$,@!Pt8mP&aX`.0F. G<} OHLLJ2 , II `E@΃tPullcۆ( - k?P up "` -  -f"!1ɐ|,%%5--==###S RSR@Ő!q!t:XsGq(6m"ilTT_P_![{'gW"0]A*⹉4x/Pܜ쬬̌T1A:8vTd3E NCΓ $ - ԅ`q42TQDn^~AaQqqIiiiYYY\BBhC8\8`fΟPz46*&ny\8 `,c"=3 &DIiYyʪꚚZ-555UU+*JK #1tDbrXFYm(e[y"Kcc C$/8]GE"+'/HTUԞohhllj:r455664՝Gz鈏8 -~(f 6LiXHB Ω6; # H/,.)8^UHégϞmiiE(3gN:Pρ#ǒGxhp jۆ YC$T4H '"(cQU5'AlKk[{{GgWWwwOO9%x -#|Gy@u$FG6o4hV YCL0h`hbh -B=_rewbo`A@<Tly{Phj4?ӘLŽ$ja`pb}/,);Pq+WGxnxhhpp`HHB:*+Phcpu=Ys$Q {gEbr*EqiE%c]@C#'&&''vmtdxh\HWG;t:qeg7b"‚| u2O4ݓ`I ' o?EEum=c h -B12:6>1yͩ陙[͛7>._@:ZO7GUEYqa^vFjrR| j#[طh 'ƺP"vO1ItwQ^YSp c C@S3n߹{ܽ{w޹ %3SdDžs̩ڪeY)XN(|!ɺ`' -#hTlAbuX\I iYZZZ\\Xx}(w@fn^zՁhEqFEiQ>Ն]A>OXit]czZeX1$#(Ș$읊g:{.\$ơbf(`ңǏ<]^^YYys{Ǐ--Biqm8zuƺₜLA#őRfڏ3 e`?`k #x,-3i:e`x,bnG {W_իW/_x,C| b`hA$'>,H'+ϞC߼۷޽oʇ߽{3*3s{vnF͚XbԠ&k4ѨkĈؐt:^ 0gh(1ɮ7}49}0ϿV=\/]p#ڶ }BcT:ON$ԯQ'dN(c';s"w|=":&>,rHEQqI)LTUWW -@!'FtDXHq=;_ИAu#!4d7¥Er%7 cqs#_^H*EUUu 8k4Zm^Vha>*ey!HKNERc(4֭E͞9}$64*'7ibM&Yv!000]=䴌Z[HsBDC8*ܬTЈ B=j[7+ %(:J=gXr dEVN@Vfxwt>:;;;:@|PFvFjb\tDhUESvT';nxѣ$]8J= ?(4<:6((R)UH:T0 A>񀎛-rCBiih0 5EM``blJMr*F@pXdL|rZ&wQBhPA(DOO=G==$xthV+P(4bC}=ݜ14 ROH߯1mOZ:fw" IRjsQ[ih$AA(=?|@xV*0?'35)N~z<3;DaK0& 0=܄qq4 N -(*:h-Ƣ ޏ~c?]d1ޝ6d6d ! Pb䲻7NȘ켂R@Z@X@@[b9 !2Ĥ׆_`kF>vߝ6XF0v -0\zT,lrUuM" - #tX %]u0x{Ɩ>|2FneH2=Wc\EV!VlcpqIC`M"T4xd+OvjD7l|0a#엇SWGgggwXvfh@5QHo` EP# " Pz&4!"Xfݝdg's}~1ϟp Ax zUi¤i3V٠Ո×Oy|.(~pҢkWĸȰ ?/{@SBA+ΖHd fɘqʓΜ5WmR;<EIK*(Y2\e `4i(-̿{."4㠫m&Jsh0>c$CE,:u`t wsB`ɂ, ;$b\2`PAgG{+ j59!lĩ'8meaEkU$`- ՉSf@.Xuf܎xZQB)Y>~,=/(i%vh_jj|9]Җ ̒o-f0ˆcVMT<#1 q._Ə4]P-M= C=U֭^l_B|cȀ!1 %c^]l10ݾɠ3!WяQYs6 jI%{"ň; qR pO]Ν5sڔTH`(A'%ꋖZq =s>>TI5j`ID*%bDaÀ(Uc10C"c'}B^E`oqp9t'04[F\E U%b#8|`.KbP2~ܘF#%0|9 -٧2'۫V{8}!)=+:O =šG ϟ(׆ܳzn^9ݹP{ÄS"mɠ/2d U#s=;{x`\W65eE=b"pqܷjQ_ъŧ(JFPɷO%s,^ݱnQ/._z ̒d0`jvOPB D1;;@Zo%Xђ0dp!D@2@$kL^ݽ~]L|ZqyUݭF08B=YUQRܓR o$V%K2dZ{ -Dc'"|\SGOߠKW <00z{d`ub재b:qc-hI&((rO Edsɶ6W¢b -|0dC0~^;q2pTAX1qbuXU \S 'WϜOH/D`ii(.`AEɳn%-M{b`Ċaj$Vɒ|)b(Kd U%`t^ <WXZY[5CV(,-Du:$+U#*ViXYI1x$l>qxx^-(vO0Z(0 JJi=rCcd҅DR ŝL~4 },A{? 0l Lq+U!Vɒ]KKl G2>ڧaOpҫ׋MGO@`/3Z_{㞁'8PAjK"};DSO%ljjmEx3`0Z(QV`Pyf%R :VLTV]Iu}K OM]C>},^_* &J{{{Zo75Ċc^1KM2'#S̒Ɩ·O_`@6Z%Qº+6-ZL’@ -DRO?XE$f}dn>Q,a{͏o} 0^tB 0.%]8O  c}MT*+.P=%0w1/Y8 BI^OF7 %i#C{:{ZmdUfIY Ŝ`K$CK -ۧO`XTLb*/K?0^}_0VZPb0pDp6vObHK2' VQb`٧w@h$'7K^C|`&`tGP[Ej5%G#{jad>~nɈC• S$zDD_H/,DЫjk=5{jъAŪHHN4XG-KN[GOҳ}6 bd`<0nl(bs@1FҒ(/ #: -m ud -ML(AYҌON06ݮ`\NOƵD1r{~U!-bON~1$c,d = -Q\'%?ɵwJr:VQms 9?dHwb[BIl¥+p%Tdt|rfN~Q>_{q0ʥ ёaVIjS&V%Qn aKT5@2ko%$BRFv^a)> c!#Vi0p09mDŐdH%[B+ M]#sF2^L-اh7{0[7KEV9Q:vOb0K" VvK X`ݤ#" w3KFEiA^Nfj"q*U :J!J8I)Cv- gKpdlجg•$U}>ygF, c=MV{sCbr-!%뵶[X8e$FݭwH{tCL(7|cHOvKFo)tjn1d%# -$2 6nEQ' \(J(QVSR!>- [D dF}Sv=QHFd7) 1 /.,͌jo$D<C'wK&L!H2ʪo""~`D1`joXKjG=%l %qIYy7jdǒ/=; =reȑTRҬyPI*)4hyh@%HP)`*ʐ"qsZaޝu~Y}h~} -ٓG8F! FxH-.VZ⵪!AnOfb]V`M>z<7J=edL#!i VQ=!s %jK;`9{{8G ߑA 'N0Zq0)`X#0TZfb]\kRZ3E02aPuy*sTZ% *K%f֫`>]p;02 T00>h C_[cb%V\a8Kd/PV`5_.!5BIYe-42G{`Xa`,k% t*r2XX #K `xmpt63R_`@p% u!dF7`d<yd   {CPY Ze&8OXK6oP !`!R$ %d .<28'/{q0Z\B  Ie$jObjƉ3犐K`Ƞ˧"aj!;WV( p"%!qK -.=2 jIՔ10fJM#%b<.1&\F\_D8Ǝ<7ZeDl'Kl0"\RXTZQs3X `bQu KS0`L`*ÅxNdc.I']҄\ -_ :0>׷HQ`dnX.9t$+셒kK8{d|c+7]Dc ?0$vrIRZIpՊn% -l0X`4UgO ϳ0$~lMu|^g\]։Dp_}n7#0 c ;A;NU. -VN`|>Q]^ZHOILc* Tq :)KKZ0` "0޽y*F*(`CGK~$\%~KN#T7mz%% UųnFS}u a`C=?O.&ůK4޾I%#W100iYSqHMJ!p"_I0F ?%cqDCtI$\Gq+Khl0:p0`% O:.`s -O鹂\r%n0X`~wjn\+Xp/!gЃDG&\\bDd' $pI+ld|VJ_ Ȓ/{mF퍲Ɖ̴{cC}0 G4?),10%CB\BGԫ_!0:4V]t8|p_lDH&A`ad.CuJK%d%$z{vu>rʥsgNf>/."4`&Vf`C"q\rVtz;xQuؑC"Cm0 0F.5 zKR,Lj_3ƺҢ¼cGG -ܾŝOq/wdplZ"Kj;Q_]^Z|>/'H];{8FzZByY:dy /]kdNd7% 7v~UF띦Wϟ=u4;wxovqZec`/Q0J3`̱@dpD\\bj\ .8q%_^}FMŵ gOOOI;hͮ,0f2`q&.U\bli. =N[cA]M@PsNOML n6CcH%cqL>kD\bhnz$GO,^sCme3Rb‚|<6`h1C,`s -|0?ԵWZK<|%KD_YZ[ ZpDF>ׯ0Yt"n0cq})Hur}K.^^%KxbgI UȒ[uUƹ3'3~J 238/CRvɨјK9GFD sɶ Kݨ%%l2p0AԫUEy'>/."4m -c|9c$e.J%&Vk]<|C"KK$Wr$ @w2U%) #pa,L\2O~bp  -wH֩|%.ޫW&}Zk3C]MƤ&2`HX`sx%2rK4t ̬W+*!)-dypI.Y,^{xzJ➨;{8!04*)01C=?OHDVAIu%dV)٧%57>x PG%Kؽ{*s#=jϳg0`Q]2wTYsɣkddΰK.]wB]BOVȒiI{c‚z[ `kS0F2e+*40vpr ړ\RX\.~j-A.ZX`WZk/QY(/{ OYE4-ݽ'-TZ^ .i뤸 O,9?."$m;K3O cٯﯨ4'd5b٘ -c/أ ,`AIEHSQ XhDz`]k;s̝e&'_0gsCՏ^~&[hu,l#bJ?WPR^U [rn+%[@>+QL^l -?0F *ЖP~љ[g` [b/95+\qYemӕ%[Fٖ0V'd UKs3gϘ<^g aM< =A~h?yƜށqIG2N.-i-[G[ Ϻr'%^Ыfث?Ϟ1eMUzP %df_bm۝E\JkwLbJZv\k-|)ْl "3۠ϺK%yϤ?I^;c09}D_G?~.1Zmjnet4"g#n da}2x賾4>/;+&K -_< u<~/IEVdaP'n 'B~ - Y}Bx[K^CO}}MQu(?6;£%f+-rN'gK?ANׯY2qd W{^COy\vpph橳EeWnܾ[l;-Qm7W>ϰt475gWa|) -H>?]C"b>v"Zk;n3fK>(yd>Q5}>@%,~P~!^'C1zk6l~펡kQPV%TzU< U=n -i%'ݩt,QSq\~zٻP[픟pWK N8z0!:sL-Is --;â)?MN ?+'n A+.2 22Sbgէ''KP'd?GiG )%>go%)?u8t3/y?̖|P%z ͦ3)ѧث{9YɩYKϛm/T%`}kc-"#W W{-f&ƀYe)fz`*gaŚs2 -U֮X3d)z?#|2 e ~b~[ B@Q 22 /+O1Kdp$!Ӓ3ml0?+ rC AF c/>gNdh"2 O0Td>}-[O=O'O] ? |~+Ș7kA}\XyKi-g2~|KՀ32Ȱ`1C Q}r-6AR~?)?0X~*ݒ.ɖ޸XslNVZJ\d 6goQjyJM;3N ?9["얰`M Qj|*O(V3_R󁔟ْw-a!S}OXOXO+g`XT ?pN?y[ųǏܿ1wy: {!d'W'cĨ1 ?X-l~V ?[qid2 8"Cԧbx䧵}ɩ9KuP<~dK-iu^d1^?_2P*V'iPv.!q.~vg[R}?Xm\R)2P|2Uw)isU[RV|.'W-Y0G"c ? -b]ljeOYTVY'CpK:A6@#joenf@oC!jbHm_XꆖShK[4TW9IV@tDȀ'Thb]jcgXtBrjVιb!~~lNnL"2F3niaGy;SnK^p<%!tK X,;)"+V,VGwߠ})iٹ灟̈́POޖٓGr$$*6Xi3*"COX5b~.2~ZڹzFƓb-b??O$`[i( `ыEx*~ʋu-N~X;)?_+'gK oI%$XW`Edk@~"2,Vs+{7ReꚐ'uB~r ^dI-qsbfb: >wd|>^~(V}RΞXO(V'SdKIDْ[?;; ÓnH7"piҥ_b -PAQ1Y *D,kf75h6^ -:s3%%0X1Kx,u{oU(5o&eB՛b| Q|WG0K*0KIL p`t[CX*^+qb<|+S?{^YBAֵ+O,ٵdI*/Kb(^2zMC b dkA&vvzc~v R?gOEYrhoJ̒t6KHkU}{p_nXAbt,b}ޅ2x~}e%vRD%Ƙ%`PkHtH?d *XCqR,V~˳,i;shӁ=me$*K,, Vk($nǗ.V5\ \^[~bet20K~No>dY. VTy3o6Xe,!Pe0Տ,ְؤ nzqa%ʒW/w,, {Q(3 -h|+Ū,\!QJ'(/S䟐%wYRV%6&~cg/]98EʀŊʰePKc3qVbeAɣ%7YBeIiaNF`HFAT8X'Ƃ݇p<>8: 2eHQd.NH[_Rbe,9V:K֮^'jJ%".T4 僥CG߾%XAj Cs+[G7/XK#VЋŪ~.=y %i \RY2eȜ -m fCCU -7SPkp>e!'d jk Kr3b!KYb8K <,(*h$T>s6h0eb%0X9yb]fFj;y׋ ~<6]p@^ U,sǂ 7D|p0G߱HP?e؀2&Ϝ8,&1#nc+((~b,i,i>[KW'D.7sw"K\к T(pèSUU ç'bC24Fh2ܽP!ye[p6bt^.VR?!K~,iܿb}QnfrlNvD_yD  ǂ%ҡBсp+6)뒰ؤܢwՓb_=,;_l,i:@d]^Vj<ɒI.T2fIwsAt!6NwSMMG(CR,VTO@9 CӲ˷2p^n+~>%.s[ '=1& dx'/Kd(PA19w"!G4NrlYB9R!#M\]as5,VPٶ2z\\|OX_z+WD,[0{{%P?Y,`{'  @$F9r(r'F=4988xl6+C!eN1wQhtbzNAʚb X~ރy~nX&+%.$8gb8Kc(ðp2X zȄh83r1<dc q+ 041Be2EĥdYq˶]{6;y(kwfɓG|h۷JO99u,Q]" > -037H,,YᧅDb#<8 )_)ehjb:x2$fVT2[AW2?q*GYU(7396|YS'CY2DiBa T Z*O1G<=]'dvHywf1U Eƥf+۲ kS.\X_/eG,3=8wtue!0p0\. Dh,1pw H88" 08tQ4T}]m1xȰkd4ya1I+wn=qXPsςĘ0Ȓ)e%%%T T,х1BbTc]\] qEh-Xpҹ$q _q 8ё ` @F"+ეeJ*ZF' eVӕZ3?6#ObKйs㐩Io?R0sNN,r DbDV*/+S(S(RYTR$*da8؉-N֖G6EA54PƮ=+eceU7u97p qo22~лX!?AټV~7U%A49Zg7O NOXbښ -EYX* 82Rj6}}ߐ?oFퟝw>;/QBhkIiC[(;mU -AexDes+2)ex;IS-VOXOI~z:Z]=91$2=i3+뮞 EF+_X\Rry|~yy@<.SZRT$/';3-%ah`# 3=r`&@cHyTdFela22^z%=L~.R'CI.^B)cO7bi`E%"9yP **+jxBB >8Ņl0وxab{1̓4`y1v=2(c +EeP1`,Vϐ/+F`Nϩ2>&Tee@d]N(pU5"X,H$bH$D> l sNu{kFzNׂ46h̗|OfO(eEepF+\Ue(/V Y,X|ݮۚc~\|)Yꘟ̠ɲkش*(nO(%#;\ZXRW_/6PO*Jb#1.&*,$7ԆE󧴵D٬^)ј2N)QZ6VF2+krbLx,Vk8?gMWs vsP]#ӫpKoߍOJ`rUP4777566HGY)a#=%)Ath+:Сh yr!l9**#!93ʐ2zTSbb qfcfy`OXꚟ6(Ɍ._'2=B#b'eJKb:&Pc@QL SRa| SF#Qư*{|Q,ւ܌ aX^6źgud%ucc 19QƯֈֶή߮ΎvBGQl2SACnzݰ2dxFpeMص_(BNrH/bm_.W.B~bz:Z4KVEO݋N %iYZ1~Gޞ 870.&mow'ksd~6U2>Fᨨ:U1b>.bO5Sƴd|5HfY=c3kGϛF=xbqx| X o``<V!Iae&E<._YْTh̤Z?S-V/g+S b]?I~2.?g ɗY!3mzG%fpU5` rP ? !@٨,m=NbqoX(tN20AwPƛ7_1Fe(-Vs=\pb~.T -GO78ï!]=012y###p]بI).ˆ :!*cychmn -wpl@vw4CBleQhz89X]1;v@ -J~seCb&ru%XQjyK;\w?rΞ1ӡ@&ϞR`,Ps6tEV uw4{lW5pO([SӕAP[9eP_C-_&3/V5%r~@q /_s!x/.)-]TAyNA@(chkA-рvх30P 56SC{ 1(2Am !(#/+%16"P!X?TJg+'BN)Yy~Um]C p 5@,Kt86z D!6p Iqnm-.>)#jA(x5ƺJ^I> u'획oi2q] >PR*BmXhD%@"Q*%DPR!sdjS[%jG٦lY\u>9gz^? ʆ~pi<)nM/sMP&3M ~(<%׮^ʃ?y7@`Shj ©ajl/h`4 O n%`gXwDXJjӰ`(v_0fj:gI! bi-,Pz,f;/ְC)5@^+T&ǗѠZ@v#Q(d -xpvy)#vF_Ȱyc?H:avJv<բwN`h~;w|?\HA؀F;EcSvEo ] :M(ۼMb0lyd\K6W/d惱f%Xb =\L3`u¼c > H l 6dѠqS9Y驉((B.5с@'Dm%d<}\ȸ8ƚ3rK:,ދ&+;4I}!$|l@4h>W(LEuM F6_ǑI*n\ddHu5EQ`5XQh;V*liJM>GPD!$+M45L'lVF -zdln2>0d|`M~+=cbڱ+;jD4VDƧe:|rh|hb4h=q8VhVp fMsIμO0t c~1i2XV}EϝcKo_q4 Aڢ Neаnғ\ ߾+)`3 orx/qߥ<H )OV*+- -k;>Dz>A ['M@X2n2&n :+f8rLCV B_ì9MocHDdϰO!Ky4hDyF/~%\i{qj_ >C'n=!/1ȘkBG 2 H% kj% XFČ⍕e@='i5pp?Θ[iY>a}BR ٨ QM -pWZ85b"B6JĈ F$ c !c2n6 xge(xf+;* =7٫_i:OD),h  xTCJ+FRORjQ4  d2fwm62IS Z -;{gWTXU^,#u"2Z o -t>h@604Jc_#ddD]E CoȡVMn3F'\QsOFku{W܂q{=3 dTV',1k2822\$d@N[`TG"%=ښ`(h+}~315=E'Ojqj4'2E$zrɑ D'.PF  6؀Vo-v _AZB? yspl ްvRhZ=`Fj2T!OGZ7 xktܞ}O oVT}VdH!Y05 -Q]y,Y8g9OX4BM@Fh3/1S~q0^%r˦u+=/jŚpDh(F<O8}%^m -ݶ#>@QW~>/k}6Bij>A?XLEQd}¢վAW2xo2$w""!ѓ @|Y2:DDVJ#Ths^}4dĨ1'ϜǦFk:WW;!Pvaj8rHK])F]m8::z%f=z1*CF"e: #!62tF=i ޣ` u}~6é0k;ǩ kꇠO!LJRWO"*.AUķ -}Sc=]]]>ƃJW#߾~S])c"B|}Χ~%>164ܩ!QA " Į dz&Ģ%x'tO!C#';> W׾zԹu>y'B6TOp,&9P F# 18 g@ނyK /K@s !* I`4:qyL]@]*^1}L'stJFC3 OG2G'dR{TsQ8C!ࢵ#㛯i -G}u7d覩4UIQ]KJ+E/k++YA+}SȰL*Gj̜y -d6 Ti'ܜgc8Ѭh% h0uA:`0LKFC2u}y}"Y{'7K-HFBà--ͺw5А 3!Å q!'g!rQ+ f`0V#:4?jG 32u"kޥE\]ruUƛ׬O2/; ܃P'} &Fx Ȱ#2Vȸ\X,"U@j=IS'* }F#تOO3cDpZ5fj8N|>Q89c4 vA*HQ\`iҋKg.i(P娱DJPQ$QktrN&羟laK>,?3~ץLPln@%PaK!cGIjT Pt=rKOFUO>{JARBʷ43h,7oy /30Mɒ cFAGDfM۪"~T_F)24~Roʫj}H+s'|p+ gK(Phւ .Zl*öNn> 2*)d2^j!9"㧑{ 2ofG@˱45]Ș)EF-VW5q):zSMزJb<jyV: %vDcEڋc0VCCAd%ew!2piN~^%͍V,њȘFՠ*5]OCRBMAESB]mH4V,[ 55'5Z(<ȸ? _pEua# D9IJTXUޙ}mogOa_.VQ%4 |biflrr8+t kyP%}m]H.D=@C*LG'ҪmMO'2%k.rEOn<+Ϙ,241*gzP),">|Ɛb(k0%ܩ xlajlh```hdrFMh )2 2~3Hх)dEoBdd,e11/8 -IF2]Oj}BACOz=6rJ|Ѱ4_cjbllbjj=*jQ8d22M;JVQH!prMG$n4(U+Xקj)O%J(\ƣO<]!VfpZXC0ݼ#DI,2zwŽwQAƼfb*] EU㟓?NW]fL OA/VMGA|U IOr}F }IP%4=1&"8cvu !Bz!2z2n"x(d՗ 2K%b gukW.]81G6J+ORбB\oݸ<]Pj<~AƏ42~S Rz 2z:4n)IOdx2,&!wV vp}F\<'k[n(蓸0wptrq`GǧK*eYdW!2d?GEMi896"aXȘȘ"E5U T URd>Ϗ'-#z^XB'IPFWw/_A$#bɳ#RY5̀œ -2  N:L90]W)ɕC ow%yVz}sc]e$+-Q*pwss  -EWp - uMdQi''Ol|oP} y~ƣ߼wԱCvSDaA~>^^>B#E b`qȸ+Cm32"2B7m,M t!2fN C( '}3. ~-W|"rh=M̔0,X  8L(JHT7A"1 2!2U 2~NFz+h͕" dH"KOw=x(C p}2>41˵PWYZш l,c02r -J+[)d\3WA !:݀[ 2 sS)dL,VQ2]'e}SJ[w>KqJ(Y4F 4r=rh[+J$haDX(XFb]lYdМ?F1NӶʒ$@/Κ1m 2&\BtUxCI }䩪B5 zq[u9DC+_T^U|Z@~=42b7d2Sp ->#>IL)">i' ֿ]%Tx d:FnVFZJRbBBbRrj8GRTVU\r.Q"+@ DFj)2&? G'?p }O,KׇG6:%Ty&4OݵzKIA^8#=-55--C+).ݾs~R?d~!bS\RWF/*(#%.*q@3gL@PSϓqÌeYPS͓:y4̓PBm<161 Jh/>̓@ 7+=5%)!N,D$IZFv DCmxQA?2f1y2x*y2 Hc:P,~{&8(yP [eFyIQ~nvfFZjD"IMK. - P2tT -ruG)c =O9O:J4( OV߲mGOx Jha Y'P5ڪܜLlࢨj2LI`_w'Kg5*CX:7OFQy2ɓ'k6jjae-5L ռ\'=FS}]ue9Q\TT`.Ve +ÌV%PS&O@)-AZ/O&b` ]xuW+(.#,ח% F().*+*.)+660]Bz(/H(xeiʓ'KV-b(\04ʊR -䢡Ivc~jVnTF(# Fq(m1cA4Fy,-]-J2rO }rB6hVZh#g YBM|ʐ+(ǭ֮\h?)K͓A` ]TB}"Di\r,UEIuWhjJj (  ,yU 1a\QXbSe}TP3 t MP,~Ah4؀,zSUaP8RUKieUP<7~Z 561?c}YZ4<3?r+*˵+4~U! }]w;v50V2+(CSN;8z%[GwH,²jF?* .0^AFA_ggW7p₡Z?uS 2 Keyy6;} w:yBip^t "s=r@z(c20Ke{#ЅZgpz˵,f**4A x \RuYPB<k\ePB{IC `7<|1ڕ{JO?'u .W#-/\kjVA)Y(!"g4^d 8@.?.P<`*(܈%]p:GArA mPp,{4r5`.+\c%Y\A}( \ϟk {2qK(ƻ4fΞ\^ut %re1x' ^40RCqႚ|`2O%]f/ۻxFK2J*@dJ `2\`訌32^)$O&]&g9%4\{AO4HC p 0U(ct<%tɜ -{ugO̼%4# U4(64Ņ*#*#_Q5{׽Z,ZQL e(SfBU^54,]C#Sҳs !%Ag68F%g|h0}1AC -Y.W3'w𘤴̜ť54!zhuP" iɐEt4u1ʓ54-Zz&WOȸG ^ x@Eai`00GG\ -.W SGwD7xx* m !UkotОoh;0,:qy'Μ"!|*dGƿ1p71Qm\KW[9o3yOC4hh񃾌 "6D 1ji&ۣG\4މCCpTŘ EL+r -.W.Dm]K!6$/WFA -FUhh,_cbykTFDhg$m _$3.Xro]{4*I4^p& &Ā\곴46Zo -O>ZPthH&sEG{. Op8@#бSw#4ޓh- ILQ/?{B1r$u Mh$D#q 4> LFKr .WǩNXbOPxlBtQ@#L@h,Zz&WOF -> c" oLbFC@c|#D#&) QZY{UhP02\0r ~ 444 ȸtF!D:DK!GLdXc2rEh"Phhs -IL;Y\ZQh".Ldi~N!#!/WB4&8p$,FBwLFIFLݘx㧖VА8Lc@7BC 1WoU, q. LFEcب*74Z N 8Ѹ!+4a`" -1rr塡8Bid܂d 2 ֦ vD'BcI4,!Q4 -CC4DqAbp2ht1|8 ->OUJdd#Pd(=Cc(&+FтsuR!2V/]`%hLXb D#<6iO֡c7!/^}h0qRYㇲ@2|+מ`%hL@h,Z и(42 2^qPOWHƲE D@C@c+4}8hhCh& 0 #a`74 䢡`Փ>Eb4GSg2B0' 6DCNG Iƽ[I2!cLK@Cm&D#D%Fw!2>Eq΍+5  KD& c҈ ".WhxSи4 ,IƎ`'3.Fddɐgh јC( Ld%ɸZSAȰ%cHDFcɐc4nj5D#,&1-Q!4d|mmd4C2nd޿ X1L|Cyh̕b(=s2/g^k->Kd5qhQ8Y\ZQhGFՅG;fW.Y#c&1B@cU }DCyd<&"2`2؏ 2ylm}C 7 Bƛ爌:{wL1gUL18Bid܂d2 A"`+f4 Qd\.GddEyY 2?uTqsqFQc `twwwwwR -H -؁RsZ{ay<;=\_XAFAC^hˍƘȈEdؘi 2v1dЩѐB#93;ШnB㟡h !@F9AFT*&cF 5иF 7AF?XddĆd(1dЯhF+/48""K}- E=sg3dТa?g#Ok*JsϤĆx:ۘk(?׎WB@C物''ڞxEJ 7&GDFGǭ!nF:G0dа1|hh$Xpwh"SLF)&#.<ƌEƖ C`7h %$nLF"Ý"C -Eql46rLQhGh FdQӈx hE ?~JFfRt.C  4,|_zB9ad2Z2DFZ|D5#,c\ؔ3E%5)4> C$;IFAFC5IFL:"C$CpY tk hY:Ph7>~7_hd `2="(@dDy"2T0 m46laaf(Eh<|~+Z/bl2zO5T..L p2Nd1dж4(4}C30wxvDF]eٍ"|\, TN*Fd^ȐAϸјFc%W`x\*F-| o #+9&DOCo^/bby l3.P1rp N8[F |Op' -joa dމs "c&"c2Ct>~B" @CY+0">+K+kZ cd"#6TO70d,N4BcݦmR{=kl}Z; < & -ddD!2TI2"20dж!h IYZ.Ai2Ƴo8 EƚذO' C Ő1= Иd{mO^@ -~pȨ*yĨ`_7{K#mUE=;n$ɘ͐AFFc -a(4Z=kGh 4} &ӇҢ3)q6JGd&cȠmј5{ 4qZ]? +,Ah<4vRhAwbenLFFbTl22dйQ-+RE LH?wFYU]s+_ia ~‹Ը@/g3}MLƎ-B 4oD4m.бz&NaYFk_h2na2C|,uR@(C7 EVa4NhZػGh@D$ @FD- d 2VrAx!0Opb7hh8yǦdT4g b$H2]Lsw2N9Wju˗.Bd$YBc2Bc&Bcɟ֊KDhjYػG%tfShi@_`2!2Zj+gS#]l 4Od >m ǁصq% }Sg3EOI44ppK8h&Ɉ p2U;u M>&aޟ' 0soHtbBF|K8(CdGyؙha2$lXɘd1?ih<4V߼cǕ4l#R^)*m O n { շ 3cB=Mt$ "h̞4j:Ɩ~Iٗs0o 2*n\<jgnd얔gȘH C@c [vʚf.ށig/\^hh=i|}O0%y9Y1aN&zꧏ܇#ȘŐ1 ƢEm&SjV~1ə {`.> -d0s^9z<(JPP R*HZ $@HL(^JAt+zSܵw -/~y_ONF - av(A󆫸@CUC;0".9#A? w|2Z*(YDL:D$J"!# a)Uu]Ó<4>~ރǿa"c&â )qA>n6j(!h\,%{m.Aq)"O_}ɨbg1adHd )8!gliIg2[GhU@1>}%e4>dъɄ` C]M i@J04qt O%鬇0sKk޼ (۝>Ռ҂l|bLXD CEQ*JrL?xhH0w IgP46=/7ɠQ6J -rBd@G4j ਄Tbn1X?bdeA@ ;=)&<垕ʍk0gQ2C*CECKꞋW@xlR:ʬ~ jg _>hzȦSB|ۚȸrؙӧNd ("hhxEZNA adn{7$:!-3]ET@vA16זg&Lj)=)6<19?Q4NYk m}Sw`DlrY1<137ov^mo,N(%%DdܽsK@8&  W10\!4nݹkdaṂWrј]\^띝P1-2BRz2 a -PdHIG@^xm4Aq9exhL=_Ydkscu |?Ga dg '2@?e|1t|OFFrlDm՛edE@bU - W:fn>Aq)"V766V-x,  -a (qh3?(ƹ $eյ-=0|J@gpdbzv -(f>qAf(/P14qt dtTcqiyyyPw[G@:D$?P2 -+@Ca -Ih/dqixJ)pѰqp -$ddSS]bbegb1aޮd@'+#hHAhhh4}#bRZŮd4P !-16"ޒG>'P2>*hT\A &QNR@1P/sC;Q2Lh}7d<[PLh2* D''`}qɸy%EАqܓ<1_X\BPJ D| -6&"&L$JƑ!h\,@?$2#dr -@rIY\ -662XO e<\Ϝc`jeMNK'frdr) - vs25ф%q#*@Cu|5bRqbff&KMJ - ZZ*<2ĸd@@tDC$m}K[GOߠȘTǧ&c1aLȢ8Zm&5 ƸduCfQ1(QGh*{릛Ś{(|SInI&&xDè.J"-Vjpb*,X1sj2)b 4"뛹b[qquOχ\\+$â>L>MSh /';{yպҼL'S3ruzhQޯTkG'z xP2ģ_4=Y@5%pZjae\*2(F%l~N)75R\?8KVz#bxXL3 %C,z#Hl|a JB>ԉ'%C(DF(Ftu-JgBmeө" ~K(/pO`jr('t&dқd">m.[Bh>}bG5r07֓)d#^DA?1gn %CD`H4_K kUBy_@ Jh25̠ƂɁP8]!+H8{K\ !рL ҲP 8Pn %C0h -Ƭ_t^_,>Dž+bhJ`tFM Tf n.n5+b'%C,hO P7eh00fPo !J4=aS՘Ƌb;9n`L11F1(B"uE (&j;sya2I!$]PԘn̂VjpI\ >I]445F& ȡZ1&A{P2DN5ƙ `2h1ndP2DDUCscr -P@-`h#!& jlp7&`LjZp/%QĠ[",^ pclǧ"!*j(n|;8B$hfp5x6L# c@UP;^P 3DK 1̜0  cT5``nP` 1j6 C|zAf UE -$H]hˡ}$@ I^/H p-}w^^q c Ph3D^ *=nhz?K$@"cŠK/jA^ ""JKːːAAAAAAA> 0x endstream endobj 30 0 obj <> endobj 5 0 obj <> endobj 32 0 obj [/View/Design] endobj 33 0 obj <>>> endobj 12 0 obj <> endobj 13 0 obj <> endobj 10 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <>stream -%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.2 %%For: (Matt Cherrill) () %%Title: (deltacheck.ai) %%CreationDate: 11/07/2012 12:42 %%Canvassize: 16383 %%BoundingBox: -129 -203 462 -68 %%HiResBoundingBox: -128.7456 -202.0967 461.8418 -68.8965 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: -263.6196 -439.3682 578.2705 155.9077 %AI3_TemplateBox: 157.5 -142.5 157.5 -142.5 %AI3_TileBox: -245.6748 -421.2305 537.3252 137.7695 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 1 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -432 193 1 1081 688 18 0 0 54 208 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:-149 -538 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 36 0 obj <>stream -%%BoundingBox: -129 -203 462 -68 %%HiResBoundingBox: -128.7456 -202.0967 461.8418 -68.8965 %AI7_Thumbnail: 128 32 8 %%BeginData: 5280 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD92FFA8A884FD04A8FD74FF537EFFFFA8A8A8FFA8FFA87DA8FD72 %FF7E2806A9A87DA8FFFFFFA87D52A8A8FD6EFF7D585253062953A8FD04FF %A87D76A8A8FD6DFF7D270504525329062FA8FFFFFFA85252A8FD6DFFA851 %0427042D53582929067EFFFFA8777DA8FD20FFA87D52FD19FFA8FD07FF7D %7DFD19FFA9527DFD0BFFA82D040504052E530453062828FFA82752A8FD08 %FFA87D527DFD06527DFD0DFFA852F827FD15FFA8FD0452287DA8FFFFFF52 %2727FD18FFA8272752FD0BFF520427042D277E2705522F06297E52F852FD %0AFFA8522752A8FFA8A8522727A8FD0CFFA85227FD14FF7D277DA8FFA8A8 %522752A8FFFF2752FD19FFA8207DFD0AFF580427040504525305047D5328 %06530404047DFD0AFF7D277DFD05FFA827F8A8FD0CFF5227FD13FF5227FD %07FF7D2752FFFF5227FD1AFF2752FD0AFF0527042D042D5952042DA8A906 %292852040527FD0AFFA8F8A8FD06FFA82727FD0CFF5227FD12FF7DF8FD09 %FF5252FFFF5228FD1AFF277DFD09FF5205040504052E5304057DFFFF5306 %29292D040558FD09FF7D277DFD07FF7D2752FD0BFF2E27A8FFFFFFA8FD0D %FFF87DFD0AFF28FFFF5227FD19FFA82752FD09FF522D520427057E270527 %FFFFFF7E29065352270458FD09FFA820A8FD08FF2727A8FD0AFF5227FFFF %FF7D7DFD0CFF7D27A8FD0DFF5252FD1AFF277DFD08FFA87DFFFFA8262E53 %27047DFFFFA87D28290653040527FD09FF7D277DFD08FF53F8A8FFFFFFA8 %7DA8FD04FF5227FFFFA82752FFA8A8FFFFA8847DFD04FF52F8FD0EFF5227 %FFA8A8A8FD07FFA87DA8FD06FFA87DA8FFFFA82752FFFFFFA8FFA8FFFF7D %7DFFFFFFA82E2E042DA8FFFFA8F853062929520427A8FD08FFA827A8FD08 %FF7D277DFFFF7D7D7D277DFFFFFF5227FFFF52FD042752FF7D53522727AF %FFFF2727FD0EFF5252A852272752FD05FF527D7D2753FFFFFFA87D5252F8 %52FFFFF87DFFFF7D52287DA8FFA704A8FFFFFF7DF80552FFFFFF2704272F %06292E0504FD09FF7D277DFD08FFA8F87DFF7D52FFFFA8F87DFFFF2727FF %FF7D27277D7DFF7D27A8FFA82727FFFF27F8FD0EFF52F852A8FF7D2752FF %FFFF5253FFFF7D277DFFFF5259FFFF7EF8A8FF2752FFFFA8F87DFFFFFFA8 %052DFFFFFFA82727FFFFFF7D04042D532906532D27AEFD08FFA827A8FD08 %FFA8277DFF20A8FFFFFF5327FFFF5227FFFFFF277DFFFFFF7D20FFFFFF52 %27FFFF2727A8FD0DFF5227A8FFFFFF5227A8FFA827A8FFFFFF2827FF7D27 %FD04FFA8FFFF207DFFA827A8FD05FF04047DFFFFFF5252FFFFFF51F80504 %522829065204FD09FF7D277DFD08FFA8F8A87D27527D52522727A8FF2827 %FFFFA82752FFFFFFA87DA8FFFF7DF8FFFF52F8A8FD0DFF5227FD04FF84F8 %A8FF7DF852527D525227FF277DFD06FFA82752A927A8FD06FF52042DA8FF %FFA8A8FFFFA8F827042727530629287DFD09FFA8F8A8FD08FF7D27A97D27 %A8A8FFA8FFA8FFFF5227FFFFFFF87DFD06FFA8A85227FFFF52277DFD0DFF %5252FD04FF7D27A8FF5227A8FFA8FFA8FFA8277DFD07FF2752F852FD07FF %5205047EFD06FF2704042704052E2906297EFD09FF7D277DFD08FF5227FF %5227A8FD07FF2E27A8FFA82752FD04FFA8527D7D52F8FFFF84F852FD0AFF %A8A8FF5227FD04FF7DF8A8FF52F8FD06FFA8F87DFD06FFA8272752F87EFD %06FFCF055352FD05FFA827042D042704522F29067EFD09FFA820A8FD08FF %27A8FF7D27A8FD07FF5227FFFFFF207DFD04FF277DFFFF5927FFFFFF5220 %A8FD09FF59A8FF5252FD04FF7D27A8FF5227A8FD05FFA82752FD07FF277D %FF2727FD07FF7D532D7DFD04FF520427042D27272659062906AFFD08FF7D %277DFD07FF277DFFFFA82727FD05FFA8FF5227FFFFA82752FFFFFF5227FF %FFFF7DF8FFFFFF7E27F8A8FD07FF7DF8FFFF5227FD04FF7DF8A8FFA8F852 %FD04FFA8FF27277DFD05FFA82752FF7D2727FD06FF7E292853A8FFFFA827 %28292F2953292F2829282953FD08FF7D2752A8FD04FFA8277DFD04FF5227 %52FFFFA852A8FF5227FFFFFF2752FFFFA87DF8AFFFFF2727A8FFFFFF7D27 %F8A8FD04FFA852F8A8FFFF2727FD04FF7D277DFFFF52F87DA8FF7D52A9A8 %F827A8FFFF7D7DFFF852FFFF7D2752FD05FF5A282F28A8FFFF5228282F28 %2F282F282F29532953A8FD06FF7DF827F827527D525252A8FD06FF5227F8 %2727A8FF7DF82727A8FF7DF852527DA827F87D7D7D27277DFFFFFFA852F8 %52527D522852A8FFFF7D27F87DA8FFA852F8287DFFA852F82727527DFFFF %7DF827275252FF5227277DA8FF5227287DFD05FFA82D27FFA827042D272D %272D0452A8FD0BFFA8A8A8FFA8A87DA8A8FD09FFA87D7DFFFFFFA8FFFFFF %A8FFFFA87DA8FFFFA8847DFFFFFFA8FD07FF7D7D7DA8A8FD04FFA8A8FFA8 %A8FFFFA8FFA8A8FFFFFFA87DA8FD06FF7D7DA8FFFFA8FFFFA8FFFFFFA8A8 %A8FD06FFA852A87DF804040504050458AEFD75FF7D522D2D5127527CCFFD %77FF7D7DA8FD7EFFA8FDFCFFFDF9FFFF %%EndData endstream endobj 37 0 obj <>stream -/حkr[gb*+j\f+ 'ԴɾYT6*Q&ƚOHbo-WVD Ҏ0/[38hZA\/q%mlهxŸ@O{r z<͑Uա#9C4[vl]NJdtճZeA OOi 3n )/H_L Qu+U7 ڽE">EOj7@5QkLZTIک8 m S9wZ-R#\/$- ՠfMVVK57a,&P0u5_T(1m QMRZ'o,A}>e\/e8uyaSq RcgVIЖ{`M7̓z!!cׇbQ:Q QrYQb(۠叒։`^k$(2`Pfg)@z%mͳv_|z184@Fi'u((J%!Jn`zK( =.F!>sua%n|ƽ"Qe=ٚ꿈k;ֿovoO:xS-DibtV;Fx}Z0-΍ O9f5h]ʑRO049pA*/@#m$1ۥ| ~ yC&z7܄>1Tnxbۀo5ըcXzrWC{q/ */O,qZ=N>jy4_>iLJf#M (7yq႘FݾB~$EHlΆފΧU'P3ł<KLusGCFs֘GcIƆ6#(1ZQhAb_cD/#{?Ƌ{2$^bmRxtӿ_X -k疍`2097{z?W2NEH}@x%@c & Nvm^Wkf|K0㻯˧׸G-f01KkeVuyaVtĹMGF"3w7~q;=噹ifv2#Rl@9 . b<DW?Fԃb03{>q󍛒6{g};/}ktzst☭Tƣ)m_;A7+:l" ࿈|yb8p1H xq'ulwѦZS.SݹmSd;5{@´fq) -M?,I_/ /bt1?l;Rrdلwĥ @/;1vџG;QOrO>+Ld -#rˬP-.uazYa 0*z@֗ĨMCPwӺ?1YC -hgHo'n_˓fL7ˣ:,.'(з9zo¨XqnH漖l֏znn{" _4@Qn>SwXpP>_^xF;OWa3>9uv<j56kl̓!sW6K_X#\ -VVz۱Qj=9;0S:9~%E/gLLy6;]jsjα'{t赶*aVbmf ?<Y23 N_bk%wըNrՆW}TP]-sT- X.>1k~7,!]>^ԆZkLb>b?^^+z]2o}eC^lt7I+?Zf>EssDJX)>Uyx9HK?<ܝH{?NΓej׭o!!k\23[tlW#s'ƷD>EsT˖JY}7.:Df);{1R`x~y"[ݏeS[.XZan)X5盚2oK؃vo|ežHsC*` _\@*MDGKCcW'Dkd,IO̻dWךxyϕqi kuctVv{2.E/e V=Kپ˗zʧ*M8n{_+Xt֟{0Kji>շz%sf^_x6b ˧cm.kL,@&ל}0j-??˽%/ܤ*-8| ){,)=I^|^E|wS;O޴紲7 c4oTWVD۶';t66oV3ZAq6ojx{\fJn2WOr"q3n.̢F\6e]6 K ->;sWa__xJa>?8s3٭ncdj -B4L8:8T?.hG}qAe>ByHunRhgʽjf2LUuD8s~0IV -k:sMh⬲"͹b؝wdz٭/fVl_LblG06^=3P+? -l2>w׫C6q;+h+暥,5*#%FZ~Y{nRD-U%L{ff?:w~ ~M3|jiGhd2%n5 -7dU>_v(so]s7,Ki# d=b*4(ZrHЮᕊ\QN{yvdslJl)|:{vgG7*q빽3Wm5#7a{׊PK l6ڕV+׈\5B-yޫ7Ru&96R\)+H/ky3`TTg"҇G)DG)/ڍ=]h;ز1hw6j:$q_4gw6>5m* ȵ¸+Q_UFGz.?Q\!2c Ns_=6Et^>N~{t`6kMSpQ[OT{:HYT nos 9߸rߕZy8f)]Ɋ[ jZ?XUS\- |XgvдkҴs7$TTR_Hrg#R#4kXZ5l WV`Ku*Gq"u@9 Do%Ǵ0;xV9L&NHu=ÝG/bL% 'io_ ;Gl͆_j^Ms|D䶍0l|˖`:> ,`I#G5JA$kC 3nӃ ] )B}m C?l?նJX]}sb>onv<+&FdjRǓ PjW^ć]1IFeCfX L V؃u5qC|ZvpeV'FϨ".u() ,sȀ[49Ɵ`TSRBBv]+&<2 -Țr"d{2`?rP8qj֊Iڏ?+T lm_Pʴ/tF|h SSɟƃ| |E xتףoQjo3 yFqC`gRWldpV\(r -^,NG qg- d~G~-Jpb4;3[QzF`K޹(> ]@8syN(h5rO,g>&m/bH@xF!A ZOU%V(\QbRrptD8KAF O 0g1J-&}0-Z Kz_x|+IH?+uO -(üc~4NkQj3B'?Tնa P1DɈHTsB7Z|n5 NZot38,OOD 9EI+ _9\?Og0S6~x[8囕j*k"]CW;Hs%͉slg('47vR)b)0w o=}7)4ԁX}Ļ7"8^ݷZD=e܇DGVR3c;}ƕ # -b4_icsètͽ\NT{ n]u~n}vA-VpeY՚Cs ?/ՖJEYk?/i5ID[+C9. jqn@9m]J0;WEef7#_㭍ܴpIa>m]ozb>.-.# _Ŏ J_ȵ}/r>;,q.H: {^S|« ɵp^&^u "_*>џ<7Xg}86gisX"ڇvY^SXN}ͥ:oXӼ:H C֔=_Tpնv߬,u9Yޣ@W-"wqϹ9(`v a?>oLvjA@KpӌvЄx㗃1>?uˢSlս<׵صsζ v׻V}OóNvt?56PBܰA13gg5vn>ʄG;ҿܪ;ɢ爾3椁䤮f'o=-,lKۧig:sw@S} Xݟ}wogkYwq gJ{)*79LEtSSȣBiA^i×ݗPiiʃr\KsPNJ&"A$/vX G̮2V໊C˶NUpTݠ8=ݑ4M3'+wY< dyx'dz!(6_FߞI`ncQ "饣 #c1{y8߳ D/,VUʤi8 6UhcrZIp?g-Rݰ-棥"r'R-)|)ȳ3ޅ+,#5M8Y= ~nم\x)3pE0|Y -@PeԖ`<(9NRT,B`aZWռt Ek#OA/'b dJ&:tˢJK0O NI~d1J=nۢa!mGBzmp͞4*sd;قUƭ5=EeG|ޮΗ]}y>:K2? 9{yW03A*A>Xm.x=_Kr& 6ksMXe X8+А4-|+{L]~ǀƼlG<ݳ1~Ӏ_La͈26dz3K+yVn::K<q*TXUUw)B~@F 0kA#nY(wB) }4V_F q}YqOxu~X3ߨ~r(fhH_S`S<1^עtxqR%Ǫ@RbױAX ᝢ>"\YSbчdw'dE噙emi&2^l@eCeЊ`q5UOhmFO?"󙯄|CG/쵝e'Q8ʆ2>\)c$3Eb3[D -qi1wV#*/.Zȿ˙M'P8_wj2#̉41=͉y57mҪ21^r߰}}`FkDTfyjT*$[:DHNGH!wb%ֶ Kl4qm)o,mcq& Sk1z9KE'Dkdd Rۭ`,m7" EW -܇آ|? vn0z3 ڗ 1/w~?s9שG_:b|j5a'YN\1%Ӷ|6FK~PLd}LToSB zGu$p#S)FGpܫ%Aig.D 4ta{M\X2_ߒea!7ä8>Ŋ"5 W0Y :WLk]lFkP\RO罎G7Rޘծڼv5Ǵsz[lf +:ڸ'MaCm2f ~$୴ 8 S}U|ki@{Ut`Aj詣F{_3P'Hf,۶۶ÙC趱 ϭ-VqT\Zi7phEi=7Fw:Ͳ{X+DKӝ;C -OzQA쮹l:}vni\kMv)xkiipn=߸ėY Nǫ"*R" 'Bjq(wNo}U[BL[z@6HiVޢ]tOQo)2;+A~/=DW (uT '_ EM+ZA󬟖[rr.9ފ|.Uwq@i֢ɅSi;/L8|O~.ꖦ9O/dHt2F8.b3~#eO8 " hZQG#TTgP¹?):[VΪJ]5Va=ȌD ie6z=3u4'6X`n###Xq(2dJU08.#C;Ύ(7ki7I9ףb_ t=~a>{jjP{w8=Z\T|vF8fs{G !G= bcnY98!+N!JߥҊ;rGnVƲqGζ}|,ζldck̈#m0TmD~.AN$$3MjT8I2vd4"^$ 2cH3Jx I¢\3{2q% v'ٻlid&,P'i&%)擴4dfzx&*og`YX]3$Ձ ,={|k'o0R$MaIBj#[fwkga3$^Oƿx2&ˣ_?`t4^T_Q;OR)@lMh{䳑5 |- Dn%bnf}?KK}IrZMj lOVrkƂ%%cGzhw}AY҆Ǐ˛Ek`W& [3V) K^ -XC -LZq -\ Zv3R|)/>d^/򸲉!NvǞ{﷟n0jԯexŨ:swi_mbZwOqߵ+E]6i 264G?) '|E$Σr/wc^n3I\5.PDcu)홡ijOX)&S|"xd;ϖ!>wlMA> ̱Y&iNHȜ$ 4Wi3,/xQՊ7AoU˕7rgkk[mϋܺya<6〧0k>6#FoY!;`XsꌝB|tn[O.ٖ[M=g|[8aY~ }]ZFwZԺT@D 97/;{uU'dc1V"oPgKk麯7v\pm`Bwekb} ?|NJKOntC5BTκvs|gs?AJ%g` -1ڐ"Ӝeכ9ycgs}|7fǩZʺ,Rc -ſHs89}9oSKS>)[[a MU)(z[GK29…0;Mg]A|3Ϧqg;7'Y.tpt`Ro ҂z]b T?5Wb-w4vAqs?t@1?n)ـ5VZvLsjC\TvFF+v4\n0Y,̔5EZhYzWoɃl*Rmsİ:6歾Z |2nlŷvjGmu? ղ|%%C*MyNέq -9Y{\Y?-?|Ƽ = 墴V#rc Yu5Zԏɬ[f<=n:59HN(.uړɷ31A[|&NQ(a]%6$},Ӿ$lc{sO2/J!yI ^ܲ73?>ULmM_ƈÕZ-v<+>tN"E~ޮ滨,81O0GPd;mS}:WEӺ҂6%Nw9RdneGⒹqg, /־)WxV` +VyؙBvJDMk.\q%o;Bf򒌟-ΌSvT\l-g\>VE]/ L\Ŗ C÷?b?;Wү^dQD۸Ev,do_j% hɻ/czZeυa"qt ycoQY~BK~O'N&hWj`0f%HQG;Q^H?l̓9=!MfUwO12lyN_$~8 /b-#u+ĩ:'i5YËdӖ4"ϱPnb>jyS#鋔w)dpDxVcxd[TwT\a-D AE`z Tr6;XS s5pP^Z6љxnݤ/[^hQ[%"m-OӐVK+;{Fo^2kyk'G_?~@-arTYok6F%mp2JXD빕TjN -2&8] B`cY:۫75,7;,;~DuLvqہI\TJt%Bc3G,,nEtZ20o)w~1Hֶ -7D+ːӡ`@m[:[FO[ GEs;ЂuzDv4>mL[#Zle\\QR2 "n,uԤ&MkVpyդ8/bFN PemC;uwu)j=6lE<,[u,(BSykq8RPMnk6ٺRMp??+Crs4pWwLZנF/ rv^}>OUkSֺ;^|U9: xbSfuTe̤ܒޥOW}\zTr Lc`AGj׃< =6m..u$WqgoO=. hUpS :c1"s2leH,(;Z{aѕ¸ xOs?-l̟ԁTh_jʩ6պL9fs^eЂ}pcSvO \3ȟy以9wF -)4RXX[یm -io5K "{\tX5 V 7o -NL+ɣFPb_ .^ۗV@u n秠M?7H0C9F">^rgh|@{@'6{@%u&:{08/ -IzNMڻ~QeA/zrO:uu58D: ,`_װ`M5+f<NP ^wnmv.Qe!垄ZhAVwE\ q"mo}`C&/֓%53R3LދJ9N it3?Lކg'x{B$I_FY6c' 3~qLW 2{3;I.!321r +%#Oe6kٰS:YKlzED8yzufrx͹ۜ#EF) |Փ4В -nwĜX}&o(*%ʵ mAx) S+wM>r]ܠ^]BxE؍% hTN`[ΞwzallZ,"|i`K -ȸ֠TK?d_^>(=F9Tk=m^޻i;M3ݱF -̒gVNSuwzZ5-L͇h2k5҂M44}2|yHm-T;l6\?3TܐmķaBS-j};[ {Y7(j nS(㵿`cwK[#\Z\yp HXB^#"ӜcXi.7 -{`q7յzryk-:6[&AP.VRN#jai.}~¹nP M1Ԃ]N}2 F$|'<{ДuK5w̉*~f!/"tIBZ6jjw>mьżv萃zXǦ2.kG$ܔoW[u^/,VX]DM9F\;Ɇu$4iwdБacj:Dv nmcKY^"r>پo >-*m.dv4u˖eZݐ4Xdozu</lڒv@-ʢZA /~c|Ӹ6/gljK,Bh2*w˫kaW_.m6~[3[ZA -̈'\ cNQ+`o29`<Ù/nJC&Oھ+C2u߫ eG?@]ںrXobOa=ƽ-1a˔_vO q韕֘U֋2t|}8aQJ"a!In E3s%ʧIt645*:}nQd1. eA߉*Low6>.@l,ϩkҔ[#ևp5fEj 5FNّ~tS6Rg81BJKNʖ2D5(>B1w Fl=||{Wio>C1g =h+{h#MUJWKT`Af-{B{S-F[z1a(2SAIJ|$ Ig Nz`{af}@/btBpL;w?<>%BF@! noPlS~-iS~it0daV 4DiSP=\y<ԧFn YtX+dV;& l`Y -Xz7^(M`?/6*';0mNE="W;sN] dtޤR0%9F}Z[R$bLQH9{0}hY"F#Ju q #7G3c^X9-%M̌9띓1rjepc͍Qۥ!$6c'*둌wM -^X!D ug\JHӄ e :כm]i![TVzG^=C=DySBZ,6!}C};ek5U 1ngqh @mǵaZ^RiN6fkdx[Tѡr =={5_-Q>Y9<<ױrZM}N^ȉskWÑ&VVC{y۟zo/Po??n%iqǼ5?mWVtrߒV M}.+ -0w:)d^rfr5o`<x:]dxv/~ν8twtvtwtqut^ wҷ*{>"%%$df|AVA†~wBE>, K+0- 7Tzk+v'34={-JA-0-|l[5?=l~F.~Y.tqE kÔbp)*F$tS׽mWTC|:VC*wӲ~)XnظR9䮍5\ѯo9VvPsJ]8,zV3`i#)mfl3?< (%(aNwvc8' -C,{zMzrCaXԵóQIN)@FfjCd^OO?`ŘʰW(W*&1ںI5?A+>)ZYr܁k*F!6 ?`zH-ʯ7XFQzs[ %"K96rSI+w8Gup #MYdof >C 8 -!jU)7V Jeֹܣ~R -p ]cPbQsJ F>(E ;]+ԣ.:>?`]`L.I`kj`6Xl*0g-L!̔v8V%_VjSO u[$$:hP{6byߪ~/C63跞 mPsGUɻגt$I( IBpRH$:`ƼAJ(\[L%$ -nbWc+I}- Ʒ8O'i^2 5e,F[ 723?*uKkY~W^4~C=7}+->vwyy~_$'oUw-fʱY8egao?upW ~ɋQo0'},MgmŠ,DQ_ tҋ`; ?uVVɧNY$Wt,d6m[y$"^'vm-ы7nղ 6pMvmFk̍Wx.v#x?x&&rp*.Jڑ{?Î  To߭٬ms\r'AIzR$UO9 WWl\vҿ9L!y\( 'FL5u~wWкz*ؚ0(ocqn|,ڵ"fFɇuM# seF4.~_z!ڭ(~Y8}y -}vѳ@n!ĆPQ26qmrJPĈ_sJ}X,ЛH誤p}܍&Ex4S8h:~:pN9XKm,Z<7ϏfPzh>蕼Iޥ^׈=KXӎcCC{#o#0gEݐҹ!hqW~vFBMgD5FMgXpg|.?"(g *| Vf ['-{/WriԨpjnçZκWA>tMi] -flh/!z9uQ~i9x_`wwX:x.,`T˳&nZYvF812W/*zW{K+DsO=`5$C979$^$a[5kzaOо^8;OD,AϷh2;YN~V]?9@ -' -zTSE흌[;J|֣i5CJ_c#qsi_mzY ⟺Ϫ%lN~Z͎zs`LN}ޘMY}F5KjZuS%GHEyrI YvqY}&՗X^r`6u|%TV6sӡuuR Yv8iZEUbٚڜ=ikr 0Zh;\$6O" /NA+/֍i4)'T`ma0:bqoOxbԩ62g^|rܥx9/ޜcY&Z$>iˮ:KV8ܘVi3ssH)9$g -ò.^RUf]j%'oJb2 vҀ+ݐ.t'ыTAǕȝgaJ ,?ElS?弳[iW.3EZ yi]I^&/xPD'N_/qU6Uu0hyQqێerik]|Tt9-v4=#]{(lJ)-k4w%ry(|(<A(l{fјjCtFIj UYp-:˱t'_= 鰶hNgPb^-QY@ڂBP, Q.qݎ6% oAIf(ͱHZ*\zRԥYxXc9]Y;RzZX6W(SdgDyStF NT neVw^ 6$Q.=ɩ>"/44e+5X,Lv'd\i6TPSWTwj bq(vcA)QW΄5[R)m/P>nsƁ5KL:d8&C&.^:Gdz(kgzX5΄ڣEGeg9DE輼Ay{LM.%WXRcHgxnRbz'b4`ANobfr :8)5`%b kxm -H3F:Xd}`|J:4飷JN 7F7kW:[l5Rɵbvl5V -=*Iߜ^7l ,cbbSM@@so I ӹdrg <ɥdy2rEY.V`[EFrVG3hd 8Ԥ{ʺ⃽S:uWOՓ˶JcHf d5m2_wWA}8p_$I^5$ò$ՂBn*Uw$ -_DgR w1I4_ҿD[LKf;z;jڏ!f{[A$/$~jC=OlgVp(ذqͶR8ijYO$Z9_V̶ǟ8ȸJT=GXюA~oL+yEZxtDNmTM>"y^~<Nj{?2`hEZj w^vwWޗ.qpNywv{Ϸ`|G}WZکOA$OlָyhyGzg:u zS j,խew;q59y  IvUuPO¯:{|CAQk!yFni -l:RtV-I-ѭQdpOV=.~1N}RNa.oqmʇ GeD`*ȼ57Ɩ=\ LrTVX#WyWXÁU)Z.UP%ӑ-kvàWvͼKۭ@M ׍⵳*וo>*ǵemj kAf\^% wuG)㎖ #OC|Ի -m5w6tsO2 nwq --uvי\a򰳬 -~97AIdC=nSx-z;]q1Gvy_ig?u\=g+CN/$g|V |&l -gXn+V~n+ `~R78b=CYG;B-kAbJ/.`l;df =Yo~ږ7\t^\ =t8͗Ë)gʜ@s1~KΖ ōS+Z-\`ZOOj֙ؒSk|UwW2hJ}\@4a*k|c'm풪K{*c2UfׄtҨ+ӑ:K 4bahU75OL#>U6OJuJvLx;}~| Y}R>ciJrӐ:ac#1{Vs%Yl/4ɍh}tZ$:u%[Q=&6a>30)TJ>@oUxm+‮lbQS:[,GH R.R޷g?l]͸~J~4>ydyR]m.`"S&ZߵTΛjp*uR~W"' (Z(^ -^]@We_il0L?p&U}ղ,p&_`ы̷V;_P'ǿӢ}T'lB5YK2Nwt1w -1EoX6P{(YbʫPSF/7nYjٻxxV! y(_4/~ dqWzkL;lVaBY][ah[; -EPn jH/0+z+(,v׏T$T08hynXQ$K8ڈhlw!zSJh@0VJoya IN*_" -3V^mo⯏y{"yPHa {1JQՏ3 |Mf!r32OQ2W?e\}=sgzo-Un}S0\+d#jt]2* 'f=K+kI9V!eYJٝ?dM}YϢp7$m6p>8!tЄZLI&}@SЅ=L:;*: QN[?DLVN y2G]dKІV2@֑9fD\s -7E4`.`8$=@i`,%M">f<fR35IC1bzer( -ĊP|h'^M4"H 0Q8Ȁ*`أl4R5p\{E#Ruzfp* h&~g*VƫHk56n t>l޾oZ c4nvrdM%v oNo~</ 3-I=)a^)nNnsE2pE@v -F 3+ ĤcLgʨ$4'IYpyPG8?z)NoK-{[@E HNF{JR@)- g{j!'gr! v/.?/\~ĿD+toYH)V5o[N|`?O/`xΚVj53`~RYf_k#ha#3Xh-9:y,^!Y== >MoȸMh=ǽQ~z_I/A?Ge -,5ylzҊM6T|j!h[J8|Upw1yʖ!,q1*qѴ05Gs? -LǷse> ~yi]eR3礽gkɾ\wSک7-&m]KifIrn]-lN_HjgEXέN>th}73z(QYfkg&jpC~(+ IVsЀPPXo_ ?2+* ;|&֧;'zvh<ʾ?ЎZFMuI7z򘝻ʎwRO`]9Aaqfzc#]{ -wj2\$ĩ8z/;7+q}anW~_vL=C9>>a~{IJnrAc֮!:l-ި S۽+rr1\ΜEcZn!Ct*ӺXM&%]/3ZNft*H>Ǹx -+쮪7_|\FgՇ+s¶iM۝f܁[ Q: uTJg5 $r;z >1Y@z3 +޻6~vylAd!1]ٟr\`crQ?r5) +jFmkR.Xd"̇X>s2PKO? -~M|vʽkNg1v|/k;k4Q/N7AoVCdcBt#>\C@=F*}dTLGroËf8 dEw]iL)31Ga؊ţ5uFWN(Gm>,؋aƘϨ7O2lxr؃|惼{%%VHĴlSj3f^S5"滫.~7WHc1_{ =w6s7VWà2/3v_.~"4rYc^jзzO@6m{(j߾^ڏ6^xle[doɕq.\hZgF_nۤBXRkN$5|_D?`~.a-vhxŽI{0Tufپ eF m%պ%?9z}6jP6c 1 -ZߙBˡ"պdߨPpNFͮsGmd5s5zgu v"mrSv97YC}׷@~~V;jA6'YkUT xvgcac%%~Y=u/tA{j\71tTq8)/%P*A:[:.1Ep~j >_Qyҷ=JTnuLqEά4jwl'^Z{ͲKa*w谋ez_C 7ΎNY~k(Gך O&sZN tz񕚯ṆR5W%Cv||%/wks%]Jl&av_,bqb;Ll8$E~}6gCKKHNՇe]6m^lN'_~SJ~9ȭ /Fꠄ*aO&\`dJJjN?l4[}–r \~`S6]Z ƭlGbRgFU\WL\oVuׯiU -ٹ+oꁣt"wcqoi؟IV:|T\~g^\"?\BO^Ļh\_SZ{xcz}=2vvXF{ROdT=JVujKWݑ0^pD ⣡c\2-N~SBk0&bi?, UByf8xSǂGZT!fyu-kTkV݄L%@=J5_ޖř{Y)X:L(Z1H7yN?4OA@\)7'%oH2Ci`fFE :J3;.Hĸ\i@ -"qUˋz\~Ϲ*(-9׎=ԩSAK7vBjW->a C1[}sv6 X0: _[n/Qƺn9B)S>C;y+钭%T;6R?*q5C5/1XWGs<NJ`L›gXй#[])acGpwơXo!]9EEz{ltIt8TͶ)\,H{ - bxĎq\<-0 6b&u6W_gnD* v:ܵ헙gs@aTa\'!nLc!8AQ2b3^%*aa|E$hZ.TFwAz^E RQڥ"Z,54F["Q_=z}4CRk@s}K|-]+w4u234r(m>^ -gZ*Ǡxc}ҟEzw8]+ h3<=yaQ< m|9=-a4/%ܰD!Jӗu`PpNFZLy$ۊZBIDPɳeq"5L7ؑ,,= -xTRuJ%(;z2?7?^>/&\hUer}*Gv*Yl{p޹s+q2a"򎝪ѥ=u/m)+Z"=@} W c L˝Ug=ŧ\-WGs gW20kfWO~ _=OpWs*LLuDf~ ^78(SR<RzGdҿٍ9F#Ei2RRV%F1lY'ciPPq=ڀ~Of~M}?n4wՓFd@~:0ɿM~k#j_1[9o47WuS?UmHv7)ɿϩ/_?%oX2/ֈW!r -_ãu܎|ƙp^'q{XCgNقeM-Z4hW/N2"K=L6'hό5x#06_-PwqW !.UD6Cg?Ц<|ww 4~"<3G-#پ6zo?ҧ \UeSއNrW5VV=v˔t:r}6@[&?7`}N/Gs5yDz41 Ɛל9N&)2ޗ N^\9 /)蚥>9vR?yHm:ۡ79|>oM׾%VwUfeZ5wkPj)נVͭX0wü;˟8|5N0@jaXWu:n{V3l -t}i]eKʧL5 uƲw: ͢Kٯ_v_ ng-[d.WxIl,&6>L.lˮiݒZG9?ނfF6 |7Է7/RxL&G|W'YZ.B^?13aS~EyᏸEMmBx%]~)53 kmA65h~r)KklhHxk* OyS乒~VPIA/^~oz2ei9y)^ZNV. lICU1hVjGlh?b_ԷT'b$6PQxYzRVՂZ70|]v,'z~qFu3sm}^꫺qǴ@Cƽv7b4/ k宸>vەT5 -K U$ɮ\RaC֦jٟ+WjVѐXJz%s%<Őb(TG)F{/:|Fd:ߪlZ V3 Rzx~!Eurl1,QUWߨkR;? -2;r3;J{}z5#{Ptˤ z.0bf:#0@f]G҂_v3ѰMYO{ɴêLSWѸ7QBUM[rE ].ލ据ֶ 3I? - -rsɜ4:34^3bMORSdZse+7q~: ôyڀe~d+y>(%yMHmKUީld_We ÏvwZ -y$=gۃa#κZz}Nl2 8،uEp…XjvպHί5ek ^{|:brb3\6#XZQfpPri* 1~R/ LMmXmI/1:&?u⼖sqy4á˔~|UicJa'b&D,fsdںzHcmaf-e1z2Ŀk) ]RPeCv Lvc< yl* E}6X&ÏYA:'-SV(}QJ~^gcU!F`cZ$0iӍ΢xPЪq&M5%.HBVQVv\sPxyax*P4C74#>j1"6S"& zI:w7:8:76ڝG}.@Sn2NWg3VF.}0]1${^Y"]KCS87q~Ѱg?fmo[jE(|Cz̕*-ӸՒXڰJ+r`;MM6H+lQ:ݚ뽷=q44 "c7a&O}_2w!4*V >hj~M>\pĞ*_QYz3@J+>ƶɚ0LjTӖ"`GE-ICG,ߚ W# M5S35}^ Wi[*ԃ|…AAۺD\4֍Gg^g| Ⱁ٬ UU6w:¬ - @J6P!t -T*>*׾*5IV^h"W[TYԭդX?i vJImœW'n[k7 z ~|j -ж}}J]׿2mb]j9$qa5A$O"aFz|*'1B_d>sͻg;wy.Ml]ݬ?\w~%/A_}9MM`QPJV -*EXWٖ׏Q|[G)[&ם(d_.G/Ȕ~,Y8h:>n솝$r=]vpfoZCO+}&"hWh~hs#Dن]am߻A?_ 檻È(Pt#ܙbCڸG#.5Zۼ HQ[j -Mz=BfLndb<4x_A^LQgH/υ -'û#VzuaGV۵P]=?,Z:Z<X4;(956Y:&ŹCxk n* ':TnۇkπP0t̜Q]?$<{[{2k~>)mS{LR"ٔiv ;\v -C!zsT=dcJAv)஝W+Su:/?g+PtKN 9E^h#/>Gjd/\+ύauEJy7fG=6Xۨ!pڳ! yfZn;ut{ٻ}9ϡ3`z[r=taȴ壻,Rrk"Ts6sԛC0~Eeyg8k'ޮFiPhx71-[O7ܶiy*p &ەBfz87fCԨ "n^epg ,!Rq9%zv67)Vc j[SPx.9i}> MԇRjJl`;\ZxzUi}lu-|;1Iyzq}ngaInc?%li;jQdX׊CiGڒo(W-9#D446B7@W:.Sz*UflS%1Ve -eet*)*NeX@3lDWVWڟN[D)fP\ձYxA+UQW\/^ZD7=z.wXܕiu"/Kjzj\,})8(7x7a万n1 v=Ymvd=[tZQ|jsdG'L;>RCxkFjE-Ԧ髓jcDlJ6w -[Y)`ґwc*h'nŴM;gVJ]Lfwk.˺ӢZ:Yݵ'0RlE/:YYz|>|,%kb:Ҟ;a]A$Zbw{N~c[9RWk 3vڳ|*2i - -Nl$ʻ.2l8ڒWK[+r^ո(0X 7!R*NU.IP)lI=/Z cٰES3KgnX͠Qa`GޝfLqL&WF"wQf(MOFUZAS=asA5ڥx,< n\r|IseҐc|j+#QcYFAޛ+ -=.3{f+ڒ([M{}Q') -wE=v,f1:|:Enkr_`X<'h'2'|8mM*NТ]gGc&v&?p`=vfFv6gReZFj/hM[Yv9]v!S[)q&C,kf E/Y"c *ݛcR}̺KpyFTc( &yF|)S!#m&H3&)y2> [rsEjg -C"+6[w5/ ^ ;`4HUu| ꤏ$fD.t~sxX -x|1ʯ*RW3@7N(#v$NI7 L›KWZRk0']:NV+sGr#o8%>I@FJ_Z f"ꀕ7VZ>c[|>3zY7J~zaNt%AaRjɪ -qw1?iDOukl">}b,W:M9_)oм%}_.#z( S8؋tZ]\pf_T>UJ\*φw.u@HR?y /]ЀH*oMN: -zsa}Kdy0ZTaNW$K5IC=E冯2Q~|8wՙEzNI~*IW^DLP#wfD8I2 ?t{|*5桭}5Ի{#s[s3fNu']W2|/"{0u~jA$xDM\Ij"i3DKܶ,OtpT&]1B㻾Y6a"ЅгSdza~'>αxЎc?ev8o*nvee>ǒW>1$f ̵W 'AQ _E_HVЈt{FEEU{UGեR?ۢ N,~ax|#񅵗[ۺ+W*ϐnX`c6ls΍~Pe_{Bǣ W];G::t\BfKoBMmEْ˦jɛf ]|S;P5chr;?p#ATCgS,7_-!ڜ.W1v/'~x`*|zs=R(Zw]qj]:!h6ƭ@3^3n_%hL4uxD;f`Zw_'<"od-յX 7]_h+L(};J2כ4ߞ{:5 -멅W9ZFczF"{nu%w _ -;'l/٭|xcu\vl}W -vYZj -R)6R6߿WN7] fw-Bbgj) 8[]BHAjٴ:mz{tշ xZM׭Y>KePJŒU*\m2djX(/!~"'?r"*~mN8_F&W։X&=F|+DUuΜ @Re>훕,5w 5~`n=:irsY )#)ә(c/>7m-V6ݺ6߹Z~|tQ; T~Z轭~x}kN3}a|kV'?Z2%Z!Z1{,gp~;jᣎB-ueKebCF1>`Y4onЌn2=~S]2K^qehm=^ˬ|%gBaPj0H/ҤXjy_)XX ou1#;H --5s-Qxjb["c= uY4 -c^wE^`J[#tHCk۰M5sG|4ŏSj -ZNF\I!/+ [FН dz95xĕܧl998h ԂpN";-5JE\,^ـ Vo9\wt'uByHċB,E~ -^gCJy>ōJĕb|qо5` b,6UGI>~R\a29z$[[ҨgebQcgzwK(B~`mm_J,hd«j}N#W7. v:p8xLu(֘5<=Q{F6P)jJ6+T(Tj ߩu6JZȈ7țܵ=®uA>LqBvoz[XJKsΜ5,ə>oO&7@ zSst=04}2\lxB,Lr%w@tz!VL\O/)}}~XD7IbB`vLSdm{#>;]#}zȎzUZuxhB#IčOds}5ʤ24nDlȇ x_8w-% [TapmmsSNG2B~J@U -k~i 6ۡE<Ɵs;dĭ\O}{ -q aX4'&;p*=.7? vU_4"7_wtc#rtà S-j.;o@6/إBwF%_* -fi5jz熵U{\ߚY8|APW67-ȫ8~p~nqJr;uv -NY{~dq}Pe +a ;̐kE#L ºU{1GIE)#~"`OUÐ͉PggMIDܿdke6I??0L6D!J[uʜ? 71 DQAw5.N.*5h +)vʦ,"PPi8V+zh9\aZ/I.x$T(b kANϾa6؍^^*51 1\0Tj )s^:Lnc\2Bc .?\єo*O{?kBMmaIB-N]>Z}0癫KtRs cg1z;+t*Fx3]?0 EhWwl ۙ2ۤosCP%Z`$V*T).816s@\6pcP@\e.F($nd -]}lc_hD\OYpP~3Wg+jbC%Ov%R1BT|xLgR5])Pbu#8X\cHePzq1X(N_J*]'VZIDuG޺rnSՅ::>Xk(XȒhH -_<{01|`t#x|*; -T:һsg@LKqrp`Fֶr57Fz'՘f6'*f2ڭ, xxd k3?qв-uI[Tf{s{)ػeQI{J]1[ޠcJ[«hVּ0CY{nhP6Dx:P% 8n)p V/+weik@ u-d`e y8EU+)SW;~`6׋|=jpR#NG&"kwFM 6Hc$Ҵiֿ>dxr2gi5[ g9T7>_]X{N|nLӂMfTIONTozݶW|om7ﶱ:s^/ - (ycJc @ PX('H? Mze%F$r ]_3w2FOYm>rsG;,Zf)vzI -Bĺh7S.IF905MܶI[$ap~en=rw}&l+%\xúyڕ ErTk 3n >&DM'_%4ocrT~@U~GY?6u'dc!=_4xg|w}Hݬ44{Ge }[;r7;ie_3spt)"ӶNs-j޿yrf`f'kjbgu91+Fu|nIk]U>dm>ʡSc6;2na)`WC},ZZd!;׮41;](4YPexW!*crEX##ťC$Wn}*ޥ?Rϩ@[A5 kg}6_=pio8k~\PNsG׺J;Ul;Ա jfӇޡrOC?pTHgzS-G) .u5Vb[d3ݼKإa%ZWY}8~AT11wLRNeac5|D?h7UV(Awm.g}Lڗv!jQ -d Ԕ]4bZl #r܅Sl\pH0uge|[ Eʘġ_eCJfz뮍;f}hmjZîz:iL+nܑ݅ǦF=9YAITXԩəZov^ oFu]5tW']'o]R5'\jMQ-#á^9VrۄKgƖZ˚Y,juxZ릏&w*"i]jrBU&򙾝]isZMqW-VK+.2V0LF?rNY|PJbRmK/Zx~I j\SSZL&G]=d;b\mǭGk ȴa D}l*|4Dgm4XJ6~nԺA$e(*1ou_2yelE_VE~4+y-dl$c[KƮo'nOm:zrniBkXSV,ʒ:ywKդmqX RZ -m/ )9BUEBpWTԔPg| ۲i*;AW1Z?LFxa}~2Qu|~Q؜7~IIuE@<_՚9JF8 -yjYș^EKrfJzmT<&BߢpQM ē ->W|YUT YMYy90fY3YMѫ:O[ј=]ɣ\ˑ*̚& ^Mv#jHR{&.ʽ9 i6ȁ2,Eِ<KSҳ bqOQ0!T\k.n*jEʏ93k'סvu2I;z5kS1*0iԂUnUe5xer@GS| y2O;6)U!?v|V:Y{UJ&ȡ0g{;\"ry͂]>}ћ8U uylWH4X4EG/+nMtPV R5=+V,{-͗F͞;W hl\y[Z7B4F#s(O(11hVE~v^\zlM{-× -46WpFx kV}Q:[˛y(̡r7wFY+IJ:% -9de  "gH{zows(k_Jev{^/q^<47vS&5uzҵ6cp0$KĠm7#9Zz G{ȥ!NclɧhTᠴ/2Qre=}n -gvi :geү`~7Z=4÷W~Yq7%F]>3:OvFjS#1^ƪƘ4`ү6:J*FpJ+B*LރڀAnAnR45K[Mzw_\%.`ZETۯC'*Đ*u!cm&: )QXbboݦ!vR}?Z>ޠ7*⇷,Nee 6S#&#ƅ\ - (1je|5Xˀ v~L7~Yhz {aʚcG %( mtUf~s{amYBYOrA1cBKA Bc,d@.y yrúsLoH )C6}+dVw+9;[}X,gXmã! SrrӍ -e8& ^ ]B6X_.MWUpngp!nQW f6c[ #o = -jM~[[nխdԦ r3d_? 6Jo*pO}@TH*s@*u 26 =b g_6}׭풖{A\ Q9]g?ɠ7+'wy ȃmlrзUP8j]Px'y(L=s4u&g&No<"$_2Bt9:IQ!LUxhj7(Vߠ62kͺ/./JRNzQ%?ZğImNz'OeZ>er׷GdmJ HNԉ+יQqo/qg[#~=@ @~dpr'u5ǧlw}-/okګ_m/Ewtyt|VSWS?c zGέeZyQZdxzPn|>2KHB&_94ۚ>sZAPǛ#6FMj zѭ뺳vU;jk˷=m_YբTܫfEǂY"fe  SF1$(/COHܛCKَqk~)&E܅8:6~]=~.1k Q \j,.S3KOńXb|XxZhdnkyb鲫vtk%%Ͻ%co:]QxG9&і,hNۃ֥k@-MaY]#SޙХ++`?Ŭ2rt/3Lǵ ]CwL/qĶցFxn=zaDRSWDoJ*,EM~u2A -՜3 6']Ƶcf9lK>Wo^ni98ri\|bA'HMV;ZE5.-y9R*ݭJ9vd+-<Mf}2M'J:n*s9N6ךJͯ@DZs+և: 5ոm :)(Kܧ`Lq٢缜J#DCi"Z~`=qB{W{Ř0_Ajwl -Ngq3ZGSÕ/YSMEK_FB?lLdk#9(M<@d(#\(/KRpazs*z:겟#TNOØ;t&!RWekU6oJufJ>0e.uUSNK;ݵ$r7E/tD - -y⃪rOO.r?0Dْji`f1 4<4&"PI11 $5*Jڊeo-ۧ$za$c`w8mip/Fr: *E䜑(`Ds9hϵyQJj`XݕdO6G Oq"Z0OPb5]F\y6'>{L '6IVqB/NvZzR?ۤ4s.J㞾/8iK~_0mN;7ooDOܩ|Tda4ֹ(-Kȱ iW:cM+bP -35gzGѝS͒PoSkc%$?A(Ƚ>zQ'̬:WA5].Pb;u@e{,\ ؗmp{22X82WbWd4'G ]h4ޚ+=a -rgr$,PV+S Lo͞ĬE4}5eZ_%'dSXu -=y_L,6,510UڂMzoyjP{-,jtGx I.,I vif_:eOs00bՓuŶb3#CE3VN{E?e*:2168cOk=vzQVIHeb 24s5ŶjJ;&e DwWiEL9oʌ@eXGX5m?3iPQgL}Z75XoYy &@٭*x9Z1ӱ, %H)54X1*;a;I/|Ve8#U;]sP_Z+`c<2 hQ[b+Br -B+|U7tbm*)3b5DŻW6\ΡDA&hy -?lwf!l5+<=fUL+إ ZW0ݷdvR/qgf lGgW`8M , ʢg(9[Ԡ=KئkB;̧0x)$!Nt88A_$%ܒ҃ $U@2ejzx+ZV\z3*nv;05ZE -)O]ӗY`5Ȳɚp2)\ -@1 'Sb0A~` @lP&n N ϻ: /3 7$A ^[ȩlA4Ƴ:6 ' د-X,l'DNƇtڂɩb3A h2'lhU^14;K?s-@/@0PKo7\·7ï c,=Muܞh> 7];_cMI1l^RdoZ̨&U?`cNA$-'#mSɲ6`T bA6Z&?wiݘYѻ.(Pa%"Zjv - sVpx擰P,"n(v -7X}4X:%$ 4B ^3Xۛ,ya⒛^w8|56N(HAq}oB;Y%ɘFM6-k`OC#>I .MOsO9<iw`SoOoWsٗ6 mǁx( -l{H 0.,. =hw"f>nǿt2fr@lt Ne/ ęb\m w} [/[A?Y[3ەj[ƎԎM&_"-x~ ds/9+G`u 7y ϙF0zy3N@!GyLavOMe\vV( ܊$ig߿['ULo_nVu~K eVR` k*r6 =l]N76,Ӣf?ϛ@w '7IN_XY"0̙ 7#hBl` }` +0O)WF%z}d_m~pZ=hwڷ1*ʿ[J4A|Y*R2б:WWnio'?!4xbl)7'k7 -61MQo(' |$nŜ!wcQUvolB&^Tɤ#↯鲁S](icLK#K"5y* -7ܮkmM|a4O_J\U+}tVpϞ;Fh:RX csP?i1:g=IE}ݗ\1q9Zn׳ݒ;%rW5Ry#bpsYϼ2{z+gL፭ٚCzz2nL7d`}^^M1 ]cZ`ʘzyjp@_ gꎍֵC>B%*Q}x_󏰝_M;n2VwpM$VBeANYVqC6oiQգ4cTH+VTҳ][l\WY5yO״rB628/^qpZҡdյ^ֿdwYIq/'AJ3y&T94vʧ&ϚPOLGHTw!'^w$0Tꇶ eե-Jr|q1Q&N_/xiq_mYK1u6Xx]vp0̶yfAo({*ĕ늦ȶg\tJ鍵R6%Hގ,X?Bpo*<͏MuNUKm~ŲsM٭JupT$i#-x1L_{1|zJ̿x\i jL k[D0#E@ _6S-Y3Z)=lxALG'&yb`X_]ýUmҏ,v|TUQFH؈nz=Tcv%\ڔC\6fHI~pl3RXվ2it3hQIԐ\/H8fZ`pa=3s@ָIÀzHԁ( θ1S~mSm5~]VN`ecB-{ºg9C F-J ! ꛱*m;2;ne ʥE1.(X]੧ -6JЙIX~ -&W=JFv|^ ku'||q28k92Vl w!@}@MORՕp>LNq<&`||nWȮ eJ:ln2DZ2{i##6>ʩZ4TyMK|jL8P%@/O}@F7@<q=kaeQmJU-o)CkĖKìȧ{0JNy~roQTAt MP&ÇlαՒ˚սԟ`khup`^b+l7;lQBe߭( -J-3'M>#pڹx_'w <$XϩX x^+[ǀk;_=7a?E;dj^:& YX39B#2T2?=m(7|UݹvIPy0CB DEb5"߀ Btwj}#>3gy@QnB13y:!4jԺ? _/~#>6ܯ w[s,Ǖ Ȫf9-@+y8? -b-oUK-Wl^;Dse+ȘTU4?{#fߏN?O_k|SzO4qՈ%pANzsydC ͔cmd&Mvx4OTGWf6|UpۿJSLOc[+~"` S+CoS`m/z}կOqS/V^uxRwɸVn9<5@<ˣ2)m &~5_W __yG9KwK\|5?\nސT&-޸MkVUq j.BzBH~eo7WU.O&KKM+xz)p\TٛRXm,]sDmDs~hm+黉N+u4A{MGJArIȃ?MM5 }?F$G'4_/Ukg"D:_=#x<3Ę2M~XҐwA{Ƣλf׼;0sj;H]bbX_aOz7$ɡ,N4&pk4 !zN]Yu@=vRg- dlmed qm\;pho{ڛk=ݛЪZkp-ǃ6k:.WR'k]tNr;٦- fR*Pf1:Ud~2WQ,o9̝s55SB5*]å̏tŁav)NOgjY)k_YA>&Fܥqm3vjkU%mySYg*ř*VS1v1_|B8pZZ%Y}VAtj4m:"W57{?$[B"3{ٰJ /\G\xx4ӛ|W+K =塶ǐۨ09?.y_"w㈽\42&ӡmcmZnj{6D$3q|v"ܯmgDM4^V8V).v sbYqJ`b= 꽬 -G?H&.gשּׂ,^Au4mzU+9xf&ͤŧA2Mdv3Jw6Ŧ^}ӿa\THGĹ2k;]=;iuKmL97 f84гݴ׉{ufc,tտ~u i(ʙxp-c|;NpZ}1Pj%slS~\[!s¾igx ZI3emf igQ*EW^6V񶧴 0o f#=*==XaAJռ8\B-a*KarW`/nQj]t5{&*WTʜN8RC05P$Č/~Wʼܺ}sZptAغFX5zbJw:dUt%lF"6Z#PXT(7Z=˯̵>K'G쭡ՔX8O.ͭ6 [] qUDcyNRTW-)uwgy& KP~FxKp 4I0Lq6|vR훅Ղ04'(K|q\tz!Ŋ'ƄI1ʚObXkشyc5nP?Vijr-u?7 *T(gչ&^2dOyz &0Dέؔ[ V++eGZ!*d$oT(s 6G$(jVJ -}ǩU^ݗ:jX}Z:S5vΈQ0WKs.5 -_6FoZ:”HL'=fk I gO|QO_'8> *g. 6D|nFV٣M~\a'ZKRFd<7k5:k}=khpTajlLʧ; (m% .@ (,%.>zK| i7\Sb7 1lF#lmѴs}L9;DJ)7%f=]_|48Y@, F=@geh%E$h'P2i|j -Z_?i_0x^SХraCsF}Qxm3 DTo\di=$8 ^-lnnV{l0 Kӝ&4`B>L:濹^]xO4SC"'b|~`Sg rI 8\!Wk_vF+sm'Ӏ+q~psvY(i8<X} uoj#ςB>7ԯڵPKEUtGZs?:ҹ1Oz`_\@;2U7O5M-y?Yx a)y3,b8IϯbE*TGUO+8OS9oozp7 _Z"`sR3@jWuH2rv74 (=)⚅G-`7|㿩_? _ @9PY5}Ԁ͓uҬ -3b?t[5wRNg(^"sCC>f_6u"g/Є^=5]i@]Qz\(O^g/߲^he#Ӈ9mH߮3.y%M\~\+/ ۘhdmxnTr SR|Qh.]v/cl63_/iM7 -N7V(m řyD#~LUJ ~ʡSżq%*kc~ڽ=ܢژUaـme>iޚuy̞0^2?xf1ј򽑏n{zWNɄ>VǕ3:|YW?+k#5Oh-!^B{lofjsyXEAr+]\vŮy[jkl٥FJt-46T/\ɯu&b^{I%Xf{ vy [h m{ѲF>Q^6~iⶅCrP<֞Pk!9ݨX慰PyuVUW{esӨʸu V_=[k -CZWhFDK'[U(P N[vѱ - mymUDoUCvAeY'U9: *> }y6brM" ӃY90׹KƗF{i춳*6[db[MXZŷ,, t{~)5Y=;Fnmwp:,-~C&˥\ՊIV{rqy$/۴^w}_@Ӑ=dU:gSdtBKY]?ǙMx/nvot׉SҖ#>Ũ|99~ֻ<݌s_)c<ɨϲ\/qw;e8bFb^MsPMh?rl\[Ϛofbj.S iu+a UOl$ߞZdH><ߙ}npC.jC͆)XD0h9@O9]5і\^_bz/lP㠥6aO9EkaLDRԂ.c6̭hIAg%fly$|Ȃk; 3ˎ>iV -]1VGHmcMO&U Y%t AQIQ%|Nz"'YNxL"lgںs)E9x2"\/8fsNu:\ -VipH[;ءC?Q${z T9)V)JhǴû/fPg]9~ަ=|unw9퍽Qǘ#췫?}a)3z.ZE?FvKm䍊-7(N>_kŃ/YnMxx)؍"?B!'(m qk \yN"EV5V6wPG$(^x -;ӏḻ,ݙ&AH,t=SA(ukdYX yçN/.N('l;:>=nkMsVזͺ^ de5Lj 7$- -wkR;WYFaVZ[~)S]u"Bf ,MBBA&F^z /nSW{h9t?ZW*&k#f30ldt,`G8waybe^=_Pɔ #6@=j59 9r2#saOAᖠYH.8ز+b5BO";g#:B$}BJC_zKyY OF>?8ewwk{_SkK,k@yPV%G^Za} ǚEsJCR.9(q凈4s"BVaݢ@ի][t -.Wŝ'WUFxi*0hV^p -qrήmz@姯'rp0H]#9*A=dda4z*S~_ +  gof`4Y\1ߔRX63^B-S -r 2XĴ169ٜ~ru/$[+ӭ& k2fzJ*stS߀ܣHOW҇ K Y 2j3o冸)dtAt%w%Y2F oefj9(a;'+>21]~R7GѲ"eO$4TKjzdK61 rװU҇zY - jZ. -l&SWҚ4HS x!{%}sI$(V7$ g1O)^GG.F@ zFr ʵ@/@<讖?(:w-NXjCB>jLczH_ۚLlTT!PYu_ptrq~XI:;AK0EbC`={l)Z`- !YW%-9WYW9kz p)t;A1׽-r$ҷ"͖Ƙw~di)mAPj||U~|oEP)xԲ eWv'e/<8.+y8'-k[=*N\A5Gkl| j -]miRXbW>9/H yr~bѣ2 kt |s{DΓ+ -HZ?J/HGAY8\5-Fe5}=0iQq\D]&4R]2tFي@#A^4 7+54د-gT ind)e23 TX HuLTlV" Kl+gy8FqŜ1B]·!޳mVU@:U?n6,Ԋ'55*%ӹq8ǽ#nΝU| 5-yNZoibN`3&$ aL2m*`*Ze?+b41U˗4͹N#: /LX0P a{!`d:7;0NVmV_F++Zs\gcŅpw= - ]_O1z<)C kSx=t[ȲՉ}YYղ{8]a9`iwioK7&:83;Wm8^+~v? ӕٲ^ E -gghm_c:T\PSQVZ @lm.ntǒWa <.=%T  ~}|H6S{?q}VB+ ti=S-p5X7ᛖ7ᯒW-ʵM8X)AJ-J@@ P?kQqeW]8lM19g0ƀ`g6tFwsֽ{yxPP.U$YRT|[`Mv|f#ӥZ -ۍe@= & # ®+-  Sv} Tu2C_+#jkr;BĜ1I}eo!;?KlX.1%݈]Y_e"li6=-j}j*:o??mKBvKn2|^bc&K -؊+[LFwB'C7Nub*f~m5YG@%'P,0A̘QHzRW~Y'f3d%io#~W7hx羸Pڽ!qgx1 pdċs VsH2 ;&r4DrI" M^T̷ϜƮ\RG$e;nɢW -vcYodfUDKT;FΉ[_J)Qq}%m^z\PMJH] |ybsBZqWIR T{{Dm}hf㕒A3LO&/lQ֝c`.Syp6ZK^ZO\t!yX*GEE1Pc<ڽijpwn!j  -M䅰h>!e[NąOKO 9Oz"-px)nzD5枊%Z4y_abxRQ7n$!#ꝶ5[DI\):y|FijN܊x-;dWs#W'K 2SDǫJAF-gT+eDdQXQfD"wސ&+eϋ3y/ۚU¥$6X `T}x=j -2r…SH!4r|,42q|K/OQ<xmk#2so6r-t(&H37/l (BZ`%!T&ĝAVKL\2t-Ɲ<˭>stream -.ӐbzPz@}v! ܩ!0&w1@S4fkuKASU-&w#QXI˕͟Ck5ൿO -eVkӛݑvD&IHF&iiZ%5@G{@VBb~ tN T=5?|Z+6Fa yѼ]vfR|NGۭQ8Ym&8BO`n@,B!2I(1`,pY@m*ȁإ@hdclǓ *"B,tM/i6J4 -E3;p9zF+͓E@nX@N 6weqZ) ʀbb} WbsЋPR䥮wϥ%@ݢC$bfTepWHnoe62$h>7j?r 7897o`v,`VW]AݎZ$51&>-a sH¹ b (=a~sۇ,`~!G ux2x8,#<.g(`WZ38S\MݖC!2KT1!xixp+`Gu횼7pK-/h l[_jt||Yb"ŽKK,Nl̂k+xQZ/za5G[ ;Qbs{+5 勝|[KمwIpD#r/KFحyf4Cx(cRK;H|N[5Wl7aɭqy6dX~hVQ-wv=؁T+)#?纬eevYxL1E]=h3&FiDςP&@/*&{]%DsI]1|MsնviNiɢxotX(⢷^dHdzP:Rc",Y3i_gigVXz⎍v\;R'vtViԾE}g5'LېShSWǓ9P7\gf]7$2R#i(#brϔϵn۴;βq[ iBYdTӋmVfCv/ vu[5\*^ -J˃{!%A[6jNU'uu=vQV,@H OS%Itlb*T!S@¶µJʎ+J7,+kR24KTwPB݉Nj*/e8XC+AYգj&kslЙCvd@O]Ii]mU4WF뙥qpgK&oWl5|% v:Ѭٷ T̼!#Ko6He|B74wRmEI_$\A\igZ.y;kw+Ӝe'nz׫ W[l+b4hRߤ. cJr˗j@R$b;y~I,[>Wx%zNnj `Q52R8$:y߁Sa_X{J¯YFByzP].sT A*TLI4"K.'u=l Wbv =Ji~9Fyx"&q>̄=Q[fX>^D77嬶$Ȥ5j,yY„Z {Ŭ)I4KL,f8^ uñ$EA,tTA1.!=R +R#Np֐_O׵7D3C%OI"'2_fqZP zZߢ:4^kS'š(}]Z^z+$'H,0aheBx> KHǨ)P\|=T9S\OzuoQ_\lJ0b:M: oU "6MGW]}U>LV0`O!Ѯ>]1@|\+ߵo -nj[ jj̈́wܺfRWj3VwOr~ 1rQ ҭέoBK"\z%~̺ ؓpVl>3.n}(LIkC2wC$ r=**J,I4B(SwF +]'6r k2u}CxAm@hqxfeΪ*n1rf)b0؍-:X:0]=#e`݅wA-SX<"R zfnM8ē< -0bdz㨂zdnFs ٷ5L^j,e\#$&g7V~7EK}i*Z2t\_B,L c>cKoE@;'A 2_$0PD1iY3 k`2g w1jǓ}m?~Be@k|,JRX6M@v*1^܎sRF:>d]9#|:4oSZb3qAiPxl MZȒ7,P4Ib h) &dp;4R";BblvL'SI+07Nq֮` F-Od苰HSG͟@o1Uz'Osqоls h=X`hÛh3h*q'FvIr(|-Xcg/c6{G ɋ%-|s4WpJ"h24zZop06֞oV M҄~eq r^,& ) `, [s9b}7]duA"t*6(c&e{Yt{eQ@zp oϮ9Q p=loE9. kFpM_7rmk┞g[p,4W䦓pbYҟ+VLVqŲ.A1e,sQx,b7 (zB}M N,{vn  Z d[#,6YI〬2j2{h22d5L"D2ΩvLl^x z%h6:6+->(wx:]}z%@D ײqhW(b-JjjZ[,b,Ȝ˴GXX|B - $Ў[H m KMWs)|-` Z1}t'K;׼1ѾcahEYrWJQ%rXz&PY733L\B?dOݖba\DggcW.ms7|ྫྷ |/P  h Ѐ1V;֯S>&*`-O3g>n»_5lp~r^W,0E1+*Q;is.ƈJb oӖmكY\'$%C'WAfwn^{k6|:AAҮ}_@\#Sw5bKpypr.x*tg`JoT$sh_B06ߟ~Hvs׻nٶ+58yk6k6TCvm٭ޭYkq -2t 9C6 A At0/Kpul%E[8a  0K3a[𠓶8b Q)a5\Rs,$e,yJǛLNR8b0%۔zdحsmk,pyB'l8zCz~(2hh0R!*pse VeBq밀tS&Jcw!O W8?]7ސ/^L7T{C^M7O)P Sn?ҍUu]1SX#Y0K6t;{wzSE^UsvSFEN -:B"oHR'TS/dfJnµ'6YP7B,PagEٱ^S:܀7i VG$[ڥ''loM(!1'ݞ-%dU\6Z*  egt2jo|UGjܳƉ9EKYN|fF ygHlf霚DrDh{{!$O?DRѾKoc1*ўhjG _x?/ ȋͲz_vԈd"^,:`[#edn:Jՠ /<[ ds[ϓѲ+WխX`Gl|8:|]w+$ڞdpUcWzCM%y4PCos 21ZxWT7>_FƊ=]tH"{mRvc (#n2ˌjssgs0 "P%͑y.P=lYdLo4:`l:ͭ߬s0ȱ |C,**r\F2,i fA)vqWv:64fhW:|I4,~y- #CL,FďS1J1:t)tYQ YQ-~\'Ӿg1eIax(Bs,iW=Bv ͩc,O^M7TxTkDǿm'O,X>|b'O,X>|b_ҙm]S,*>|b4v-Q]6z8]: g1rl}'O,X>|ܶERLX<-_T7Ƹi'`\]AFmrIBfU;z7\[0eM >=Uh~ ՀGX;VLYu\bm`u {E)XLX\WY }&5nӅVԫC|حiv W# -}bbh޾/lgFB]8`7lٕ=:`kjis'?lKw̱_㸚w`4-.bn$ [PiV+xIHA XW @Ǩ ?p̼ -XO\ݏ&ž!l}IWMajz Hm}eGؾbhӲQ?~a&ǗHD*z9#j74quˋ&bZ)U;Z^ -ȅJ- 5gRQS|9@Wmn0Y&G% d?h: 6%`5Ȃ33!»eb֘Ŕ/r\+Y]i -:l$9WV;Z&kGJ]7ĭSYLx^[ p;"lB>.Vc9a-x'G@/D/m!X#Bv~lƽ;Wڔ6;z!kݺQ-Qv2BNz%sYRn -s9&\{WN#Mʯ~w -zZ]ط$Wɝ6:]<=Ndpa#ugDԾڦY  idI*`.XXپunS%ϕνYG|biXu}QK6schItY*ےl._"{J"5AJbϊmEA!{reͶʋ)oʱc졁sٻcwbI{Q8ԃ]4u]˿^;b.l<d9uiZ ɍe/)PpQ^OO,YXE&cx_7$Yn4>hI{D;cKS@%DŽu M&ܱj7#cR`* }Crymxx(!}GaTfmiM)g.#ԉ]a><ڏzvnlތ M6ԣ}D]9/8!eo\ETFq6CmԈb&׊#}mw'_ g'YBst;e5K; *%ݚol?Z '^W]亄_/ ⦍x]]Sl9ͧP kĐm%E4 K>zoiGD.\Ǜ9F=8#ٻ]$JBe5 ΍Ӧ5wh+󡇷F*33+ُr^pNzǨ}+l% \c8AP؟\B](}_dFr߆^ʳU5q?\Q;L7r*}`ǖ0)Ownmj,`߾1=3]]mw%P?uaz'"S#snqmox~u!kvU)%wNo)Nr[ -Exע/6骣^FwNӣ|ӕ[ڋu؋N /զ=_>ΜH'ly^D۱C ܍y -Y4܎#W_r(5]ʍS_n%ux%=L8W.lT?x`MuKl= ZsJGE|:7rB΋+wqa {d7~87lB<ݤ|SAy -"63~kGCڋ@S~d^?qRbreb&{0zmf<)/=f[F($Ld~@JXvW_: a+_~t'Ov|eɁfte#7DG/]2n2\e7[3J o |g>)c*=yJB/{MsREBs:~03*"SLj4ku =Oۥm*鍮'K㣨xmb>74;^tʪoOGЖi{6U}ꦚ#/rӐR P>Pa\ur"Esf):.G;^>Xj$:2&` H:֭2RUG\o]{ 'x>j -g~jf9$wv=>}ch JX?W7hu~?yx=iE4BT1Gʞ#y8s^:Ng<=`|B5.]1WT{L]t,Haߵ\794GzG+޴=xP[}6sp1X?A[m\|Tz>.ڮ;EOJp)-g>P}njݴ#t“'W 1\Ӹ [ߡjY~[^ Sz;(wG`: B7/EHGnFѨ#P| -gʷyHn_,m2׭ԍGlU@Kwz0n2Ge{fRazQ8kA~JƐ3G}7T4D'o9m}o\RxCUrYDJĞoe\'%ÎI}D׶<{iM;B_7-{>u_Pac\2M8ay U$&ӾuIB?ZGKutTRnDVw>-.Oibe;=#YS%_O44W-rmЇ>.7՛&syNʸ1XܕZbڎdδO$J||Z?L^5mۛu}꓉ʝ‡ -$KQGx'QxJ*n*p7cɇǠ78K[5̣=bϺ -!.^{兘+$S䔿 kfsrB 0rϼ`զNȶ_ v4IN_g o|ɲƻ`Pԗ]/ϫDQ !Qr_^D0j~\ o͟XXݡl7dջ"sLi-˥`TOt8˳:xH}(~=*rᵢ1'nG7nM{uAʱ_=L{Lfy&u"ݦP!2'п4kzޱ%:N>Kj/Ft=/(?Lp RXr Kcirt{oHPhF 4*ũ_<}@Ɩ9 3?m7MO)[ t/R=׋LfZQWbD8rpw%;Jyw_rX&I>?h>2xp}IR[ߠz/Wy|{h]Oم'[@4J58h1;_'O,&,p |>3jNJ )S.?&:H =SM3n/'d.xJ DZi]NzvjC+s\̋Ӎ=s9;K>LsސԖvTϼQ-Q7L]R?*NO,XB z3B1,IS,DM5Nr}gᅆ[G86xJ榼u}^nN@OB4mAҡ -ʵRvWw/qRΰ7;xÖw -(}<>gډ`"g.u/Mzs'O,,tK-ѰhTdivoH?Eb,3zNh9:>`o -d ?o'eos;h8z:̄{= -mAeҳ6J0g¯jaY6r!x.)zvخmawQϼ?蘟cݾO,X9XZXw}[dSn7nmk vCN;oӈ;nϿú|b[c9"s-jÙ?7d' ^ߗ^wv2}I+B[M_X4/xM;zK˝ŴW&lZ6__B_؊yihyZh g|X0rR.KZ[*W2& y9U.֧Y4 鴢` vk}jV/nX}n4NempG)0O)sw8i5,Y/nO_=;)5;;Za/jbNY;TO\Y+<_N_"Z's~ȟRZ_șDeocn/hem_w_46~wfu3Fp8hߓ_`s>YXO.6}4=ɲ7i/9Ẉ\ݸ8[;$^/%em1R&O2-&U~-eb<>R/ l y#m5LдEIo~w]$w[j㮙U~GaZJ/<8S/RrYk_wKmtkmM?eSɅ9׾Xx,Eϝ˼~9C[h7砾uR'D ٷGs|vE؍/bvbi7;_յ=_} bݫ5:s~ro? 3r]C[~J+zQjd_Lfw)Kmj_KQ]|j~y߿ -D<T6Z/o+`˥%Yai6-c<.ucw5})K6wjܞ4lN/:M{j^B9!^Yݩ|ּ 7M_7`ibZUaen -ZG) \5մ1/'KxAӬEʮ67敵j/Qq~KQ܌7gl͑mjAk7|Ǯ;O z|LieЭY[ßd%!hkmN%k-KȾol7;L -"l`J@;sRe/~Co?Կ}r~㭋^s<Ԉ*饧6k@Qf6 n4[2{u?Zu IȜx p  /}Cn:zo.U5$wUY{Kڒ+o/O-c:;>ҹ.m8^;y]ucߒO/<{Ixgiv;c{}5Yɗs|^ZwIʱ/^^\ܹ|C'Yxd,;m$Wvl]>66ՙ'h\ޥcWoWedi2v;?ۿٓ~nܕGrOinmIlOť9u疵26rzbI3G'uf?ߵ魭ONmjbϯݼ{5֞<(871yz1{NNJ:7'ޱfnguR*_6*iǵ9K'uΚҮtI]II֧=MIy֤{O?*:NmjҞ!^q*/gW'tί\EiO[,~[NZ2>~*WobzIoOm<>IkS|5p?:LC܋wV'k8imhѢwG'&o[Na<rkG+~^<{{w“>9~my'e_4^ʻ[Ov7;{ҷӧ O_IkS~xS{[?ǷNJ>OpUAnsFIn~zcI?>?yNKYlp g'W֟YN^\}9 'xx| 'tD5K {Z~8>:wJ/o>a||n.5=u<>MId[ykʇOkO}X~;z*$67/;vnݻԟ;}6voqyk)<~}r'3ey+'ӟW/~zZ{|ڔcOV?.ǖ_cS_O-ۏ>~O[o9pܚ])162s7//G/mʝ7&;«ll6~=?~֡]3&>8<7m/lZ?QzM|Gbwq˚b1ɴV'i64cku&eӭ͋3Vm*9ԛ~: -OJO=W)<)Wy_N-}R5S NIU~R!{__k gT NJAjJNJ׏' tOʭ“֦#^/twMm?sZnS<0>[ f}]{>M~7O[~[=xy&[[q1UU̞׿gN] J ڿsqv{Swv67`;5OwQ'ᣆJ_i*OF*TYNߵ'2.^wsozjS? -`^j&7;^IfU~g|E*99sP*=ά;m\ejd]?NwQI:筵w|gV6OYcZE:뗜ݧMnD\p= (;X/tEVqvp[Kbop]dPڽx7:Ay'LαËX֣y]qESjqfg'3XԠͭ-XoWo*'(ئ'C&IpO9uj޶l̝z20ip -1^w.$*͗ TegySU]oO%s{_Rm#.m A-6L(0G͎T;5ѡzK6WSrU(qU7]!k2=Vhhl'xP^+sv2sr;>}r*?3*ˬ&^Q$ʬ6MXƗ_"Mu=HZyW|lGq@}zo29J9N^sa{hv|pZ.f-u5M'# jimVn`Rc|9W)+SϕL-.OfJc8%A k3QT^k S =+A(«|K49ظ~w Ul?3foގdkSnYc~ҙx' zj Nvнbx9g4Aj]s"[Rb7T!=?UYEamO[,󼺰ZZ!ZM>uz]y-Ӿ;JZpay]<̇kL]x8NB='wVTWf&O%0 -%$ FU7`Zwrl|uIUjOR#gTKFO)?.D*Lk4Ɗi}iZZ_ZԯP1Yk -\+ӕYղq}l[:XM=SuB=̞d1&FD~Kb`~P:VZtQKDqE56ݤ>[u,gV]~_<}y8C[At-ּaPkW)܃5t'U+ -W |]z`7kWeZ}]+_AY-t%ZCWNEk^CdtEkWЩX5t %+^Lo嵶jRr#'nU[/Gx2Fc7'8P5~#[۷Rc|6:Χ;s]+Zg,;yU%;WvyXOZ| f{_RK[2֢Jfa*H2TWmMr'6. -7u:X(ZnٙUIgmX/v+Z-v+8fdL״U7da5Cfpe`#6_V+:*_)m-1UkߞV\uغoO7]o5ZO? a^F ]R-u`-]m5u|_Z/)oJ7ρc-,~~-WEm;}~UĔt>XV^$#E+S9X_=Dz.s27xr,v&֖m6w*?nY^8sdK{BgkSWt9իQ9ߪQm\GOh]M|%n Ґ4J+55r˜+ y*I{UҎ\l?qU!S##E%;9]%Rj@ʭ;h=j~>j/S̮qQEЕ(.mFd2YLe(1JɩzI[/+ww,5^DdLԨ%""2E+R[sڊWXSmq/#k]M:lcIX.7^lߛP$yқ \V^N&C2bNoehN`O/8򱦩 [k{l U|uu%+[A*eeT"崊]hbRoYz1|=\d=\,{um+ڻp+y#h"efCſzq^-=Y0ղp}a^zz6'YԶ2'Y9l&Ht]gӯx*H -c8=X4d|kƋbS{x>k<}A4aagm7f,>JE5C:XkeXj%q7oVi| -#qzrcZ-8ů.xV4W|Z6*م/>+Su}Ey "{.zpG+RnVXDZqEѭ/UB-dEެHbE*~E*+R8^%Λק mO2]~ 6Æp~K|=X -\ze6m+X -~Z w-Kr -װ.?>VvP0L>(ZRnQՉ^|(RDBVf60M/nwp!NyɕUyYZ/nn\Wu8Cͩ]=ۿnL|N>fi[ojwxo-}]__[~›WGSEkVq/)ZxYrExei::cnY -;`l۷“[oVM=QI?t{1Lq-e* -׸[ߙa حnRI]Λ8~(ZlvgooE'=.ߏ~I7.SaI_fWةJݵ[TW*~NtT!Mn#:/si>{I[5Ve -Sn-S}/1z ƨI>V.)5.VrQ&{I.oۑwT.$VF)-VCwB1&r_޾o6zixZU⍈ -V݀lٓGemt6w\Ulw]rO*U]Jj *M(ltݳ>Se(UmZF9c.jÔot6wz[ y҃\X}œMQ$irwnlY8Ե{ت|2:XO62Ve3K]Y&/+[Z6zyHW5Ϡ.͡><ʔoI4jov0^W6.}׺nN裔l8=xK>NUFUX|RE{u`9+:ݷV>vaqAvNy-U)D7Ò뒪d^ywVs^I3Zya-u3VlamG/|\t7zΧFXmXTk ]^;1GEA,4LQ!VqSEL>5DSXeLٛl;MU.˘Vf*P/׭reUVY4}ﴱirELE3xĒ~74 s^ⷊqQeeq{w:Po=ZzPj0_.NyqMg}FUϭ׸%U^\57xν7'6&MmܟyB͚^+MŎe=*}zۃPUvf]bf}r2XE{zI^U6IΪѠ.f'NV6/IjZUxI\/jҦ .{3 -ؙj$i|jUkڙN)he<Ū34u.ժAvuwmXpIMr5K6UvQq.eab4e,Y'^ǦSdjȥ$c^=?ezͺ1N&_SO>]}d̜/}DZ/~w[@~?Ss>,tWؓV4Mxmw׷oq8tW{z$?zfʘzSkSﶎ_{ֿz|uow‰>sƜ?y)wv/]=}%9|~k.n{pzN F8Ҟx]fΨ1JԪ_r8?-(31M JX_i= ;߭G̅7?./9nYh٤ͧ>8\W?Z_L>ݏ?xT?ߓ'3,s{Q=m>rmb8V N[G3W6w]c{Ѿ7NŃ m-p.Qx- w//s:/L{2}E|o/?}}boϯNܝOK$[>'gL[X>{{MgGꂽ2ݴY!]zAkS]6^O;8x[ly'o|nwU] 7;ѧqpp^N~{w|W0z 7 $;^/O5*6W s|0C;2}QOjܫ;[O>? -#먎_ff]k^O>?ko=ṁs7vOo=z>w+|m㉝߻o* Qsn -Y/=gĎKtK?? Ւf8_;ܻ9`1USlEuX|v*fBxo{2v(x{<ɯ_N'ztAjSycy Svɕ{Iw)׹ɸgɥ^I vS)5~k$UiW3Cȼm/9;ߦN7..e{|]|ZoX3ၝeudjmr#>鶕:r2H"XLj,7-Jgǥ[?,XZZ/+\\1 b!~tjנb͇Pn6T+6d7vncoe(Jn)|_YluGw%2t<\nFŬ -T6_Mmj}lͭ|qr~t8e;y<]uC|z`t[ƦtWƌق2V - H&e쓿PPzj6DWs{o˿OKYנ|ߝ_v ؙ|\~)ŭl]Wvc;_Y-buL;!|}~>KŅ"obW%⽥$+S޲EM>k-t?ϝ|PuTz#uآ(x}E&(x}E-RB[Ա ]آαY)eg--֦$ף[T+Yjk-֦b~lp[(Oh懇3tOEԽ83k{i^;~-f*3䮢'qp}*g޼h}{WKjVU(˞8oɕ4o(T Խ%y/qpqow?~s^m}jlelGwٷ=F7=ڜ[umnLwq**1;KS ޏ/X[gv.: XfMl_ݜƓGFo˗.<|d3UٗO H__\XWg>s86'78[ϝO\ipׅۚ"UnEř5|PR0+cfiYGƩg;\dnT2f<{׉U\d$Fvm2&Z@yRW'SԀھ֟g^_Hը~m9?˟T+l2}UeLyMI;jԂ*{n9p<~+-^-C<=$z4$yw}Hr/gT9x>{xQ4rDr[5fԬ7Kfԣ(<7Hzy޻md4ecz8޳p ݋ >tHX="_f8P]gzD#q&} [',猉6dLG70]L;o>,>jIMףaiiS=WJ^ݺ,rG绹0нtxr;E4rOc&Wes,.Li͢8j 3IfF0L5${kTOI6v6^oYq0ABo^&9v$찀$>2FURk((c]ZL::,c'z T_XLӇhYSr 5RNMI[|X! -,it8(]7ge\"@۽ u~+y@;

ǧ7>`]~  "'RY~ *j!)z˹*^)oekCrZy$ؓ+Otor5N1"֦/D9x]qtINIIԯ6u}qxGL#&QD=|=q$X8bT޸8b2N"|(bfqĪ|/#VEM#ީMpPamjR^__p׎w{?5sLgV0f./1svl6uj㿯W] -'?{12{H߽36CWcҷY<+<cIK^3+87טi={ƿ~U%30EӴ<' ~ӌ% 1\Lߢ_rmXrBCKAc_kfd{iΒ8ޘ/9pd5f:KX:0,g/p}L:N1B+4Q?\DEsԿG 7v9-p@K@gAxYڶJZ\)?-2Ën;fK㘙k%74ltݔ?fKk:{̡A*kͥ=/9KFYoWi| =!±X -쐟Thv*2%%i仦KE}ɡN F}lrN,vȗt(n z t7j!ݏ*gFJ[%+$#5$ͥj'ٔw.Y5QӕRQ鲇vUp9'2#!CFhTz<'D?&|!=!: 5PWyz膔I!Nr|WdG-|K'e}T*|ߌ'7ٷI}*6}jWSn>Snk ,<?+lBՈ0!k,pTnBU=_vѨb-Y&ruGv)&eqĹG -G9MTl.کrL]*n6W.eϘ67uu\R3cʳpb$UBq/J˴:jZTǙN2O#á֎R_t"Wo^:QUa*Sf۶tVT^,)*I <'܁pGըtfe˖黺zSIntJ34S}:NRh>긩[rM]$w@OQ;uVBumPRnxnxQw%IeMS0LDjQ  m!C{ -uULM!]I-TCGra$qu\{IN'OOD?Y{Q,l))Yi&;u-2{ TSp݇'DN͍觖gEꀛF| -S.$Yo̸q#f`h O~l{'bܠV-IƔG(a/Ǫ̐q¨HD9E:P:,RöU2շnƉ5?()Tql˰u}T =dVWC:@׫z$PR\NdrCW檔 i XŰ8d;Q鱇LCwzZuNh"^=8]>'ŝ JoK5.e'y9$Ink} '|ѣ:tmh|,%.5e,$m$s㚈vߋ'1E@dD XkJSVL{̤0;>}àZX:g\`SD8MۏFbL; ܯshhJ}*!FbR38)0(z:>S,gj)jZ_c<Nb6kxjKB{fT1h#NLxX:>W,#`\1[bl#TGsŸ|dK抱?A=t3d,T=xԧr.;aB!qAMv)5un r}C0b.㣩B٦.chѨ~jקGXQ br1w@'7h6Z`3m3TaU9'6zⰅW]J':z:[Ժc&.|}Q8ϋ:>BR$Q[MhPqtWyǓ梠+N -lR\SIQ|ctv>gTID)|\OUw<朗4~N`FSn(t[Fx<盓L=_Ó<;sqNL[YGՠEs{H8v}Fw%=nЏfDQigJML%rGSzU\U9x{m}F*2x:ʚna[[|# -5V$?PS Ӊ<4L/#|NrR)KGG_. rm*=mÇt#@>[`"]A% -`0tПHOpI<#h%] ]@%搮sѕtp}H1ĒE^M6 Â?~m #!%m0HoI) "ҾEҶ -A !!h$m0(HkDI- ҾUҶ A@?#!h%mЯHgI2~I OaM@ !h%m/Hc6I4 Qv /am4~Y H`vI8H!!h$m QFAvH^I:4~FI7υQM"s!h$mpH\5I<FM67QM k!h%mpHZUI>ҾFQv7Qu#g!hT%mpHYeI?\7~FUҶ׍Qu"c!h%mpHXuIN},i A,iDB(K:4pHXeI?\'>FY׉Qu#g!h%mpHZEI=ҾFQv7QM"s!h$mpH\%I;$>FEҶH^I9H {!h$m a$>Y H`VI6H#!hX%mHbFI5ҾM6 ?aM@ !h$mOHdI2~EҶ _a-@?"!h%mЯHgtI0+]6 A@?#!hP%mHiTI. ҾQv A !h$m0HHl$I+ >I -߆A!!h$m0HnI) *]6 -~"!%m0HpgI' 2>UҶ ~m0 !%m0 HrGI% M6 Â?~M !$m0LHt'I# >EҶÆ_~-0"!$m0HvI! #ҾAvÊ iI +%m0Hx0#!HRÌ II ;~$m{0Hy( !HBv IҾ$$mw0*H{iI -nZ6(!!&%mo0JH|II~nJҶ߇!!$mk0H~ I"ҾnBv떴"!%mc0HnI2u] F:St=A%iQG뒴m#]@uIڶ -pJڮh굤m -F>됴]HkIt}AMHkI:z)i{ ]'@P/%mOH +I,JҶz%i[E^^Iږd W%H  Ifꅤ@>u+itAJچ#]G@P!Hԭm@>u+iPtAH~#]G@P7Hԍ@1uu#iPt=AJv#]O@PHԩm@k -TҶz:ht]AHnFN$m7!]_@PՐ/ I jHԮm2` jW6U/ ]I ' >ڕ0H7p!] ]I ÆMo 7ڑ0,H۲T~ԎAGچUe~ԎADnIjؐ~ԎACf]o~TUҶ m(g:H?7*i[`Q34UH(JJVWm*C@PI ]BU~TUҶ@?!m|VU/H"yAU$m'vt~CPI H ) AU$m'H"m|[H/U As"J6Ҷut~BPI H ZIFimIn?j%i঑9$]&A: mDޠtlVni{C -$m$ kZIFI I \I"$m$ q#Aeni{Ge9F: LM"moIL3yAE niS(/I঑7hr AYIRH4BD$6P!HmimLCiFO((&?A)ihu~D44J@#mw2 z]PQP/9AP,ihի !m{34ꅿ`=j34lꕿ`?j24l`=*34,`?*24. mJ( }8 BP C3vAU nڟЏH!#]hP$' h%m h%m h$' h$' h$7m hX$G hX$Wm h%g h%g h%wm hP% hP% h%$ h$$ h$4m hP$D hP$Tm %d %d % _% %m $6m $6m _$Vm $Vm HZ~aG!$%vm HJQA!nZ~QB!nZ~QC!nJQD!nJQE![~QG@]}, .IWF@Z~E'@R>E'@Jo ꅤ})iAԍ}(iAԍ}(5~ IN@uAPAU%/#7 J_:Cw@t *i_A'i (-i-> (-i=~ % ׇ E@4G@4A@4z{E@4ZyG@4u}A/i?EA4ܒqyA)iA'iA%i?A#iO}A/i?_A/i?o}A+i }A)i }A'i,}A%i<A!i_L}A!i_\A-i|A)iA'iA%i>A#iN}ACEڿA$/i?n}Ar?@A$#i }A7/i-}A7+i-}A7'iM}A7#i_]A+im} A+i~둴oiAP%W FB[IhD/B;IC7BԽ!!^~ʐu.iu.iU/i 3!jO>AgBT]:AwBTMҾ:AwBZ~AڇBT.i"G!ʗo^ K!ʗo^!O!J'@/e%HU}\ 3ËQFB(Kຑ4})-n -i A&iFB(I঑4*uBB(H@i A,ii0 i_ A(i~AC0Jڮ4Lg72 i A i;~E?C0Hڎ4Ȓ_}5 mi_ A(iABgCРIf`4HWD}7 mi A i;AFڇCPKF`ϒO}9mi_A(iaBڧCPI&`OGF};miA i;aFCmi?AR=}=II`TtӒ9%}>ݴmF iA7%i[QDCMI`Ttݒ1enI:]m Jڶh굤m -@tAM"]/@P$mK W%Hԭm@1uu+iPt=AJvFN%m;!]_@PՑ3 ]I 7 m@HY@u\{i__)iR:q33333ȶ,3ell;v.|{9g]]ْ}kMboy bXg߿#,zvC;bX~b_~>|߿,JO'X,pۧc?~NX,+a_GX,۷ᣎ߿ ,J_wbX'} >~X,+q巟|X,+巯|X,+i䷿̳X,+̳X,+iᓼ߼X,+i|߬X,$7,G~>fbXo?'|X,_mb 7,z1?zoY,bo'/abOyX,Ŗ~{fbX/W|^7,z9䷿eX,%+b^./Qbۏy|X,~ϋ{fbX!%bBC~3>/Kbi|^7, --XoY,z۷y1X,+4b9dX,VoLJc bs|8`X,Vho_LJc bw|8`X,VoLJc b{|8`X,VoLJc b|8`X,VoLJc b|8`X,Vo?ȇc b|8`X,Voȇ b|8`X,Voȇ b|8`X,Vhoȇ b|8`X,Vho?ɇ b|8`X,Vhoɇ b|8`X,Vhoɇ b|8`X,Vhoɇ b|8`X,Voʇ b|8`X,Vo?ʇc b|8`X,Voʇc b|8`X,Voʇc b|8`X,Voʇc b|8`X,Vho?ߟ=bX-}mr>~,b%X,T&gbX,VRo܎ߟ7bXI)nr:~,b%YX,X,X,á|lY,[~P>~,b3eX,E>9ߟ)bX/ˡt,Y,zogbX,֋(s(?Cb^TCX,"o?2?;b^t_gbX, xX,zY~ߟbX//bX,e߾e:~V,b ψbXU~X,2o?"?b^v_gbX,V(o"?bBE~yX,j۷HςbXPE8~,b~,bgX,sbX,Vho1bX+}?,b8`X,+w<1bX-/X, -}'pbX,V bB_~_X,;bX,b[[X,J*qlbX,+1bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,b^ $rW@ d2$HF1 ` < -ůP|zyQa~H^0/ܨpko0M`傅΄`:^6 -oΈw:/g + ~+;|x>C.XX0 ~/]꛿7)1`8^pYPXH*$Utjb,T($? -K~&ɇÍfE  E ԟ£01|xX`6^H\Hsab!L"_gI 0 -B"A< z ]ͷ!Cz "  -d ywA)0~g00`6^paABPAP C)@)o}$H}b~ ?)!D3 < a0:~sN@c!@( @qH*uiҤIk|/MԩRBJ Dx AL 2߲̆v.ȉ(, -pX DxHO?M>(.FL(AD h@$xa! h'\Nr!Ă( hȔ)s,Yd͚5.:+|;sL@D|< t(!|lJ^rq$?H.L, * -`eϞ#GΜrm|'gΜ9rd"@CAEX 7+b0#A?qabȈL 98Uj02* -bpC%K*]L2eMWeJ.Ud%$E - `CLʆ0ʣ0I&W4Ȗ=gL CAh^(FRZ/ p#J.}LV e &jש[^4h|Q~zu֩]VիUB*I>HGtiSBÁlO1͆@IILD`.fX+QT!Q~7iҴYf͛7oah֬i&M7j԰Az@R(/o\G h8 )o fCGFR͓h#GO\ʓ(U\@EZ Di-Zj:<ՑreK,^ ȗ 2m`r GywF\)XԨU^FM6k:vޣGϞzAzճgݻwڥs Ѡ>QJ lOGf l4]$h0Б . -"sNh Tjݦ]={ӷo 8hРJAп}z#hWvM0ʕ)U` i6kĕ=Đ4H ?\.Z5kX`)}:l#F=z4{#6ldAt4kҸ!`8|yrf'0h Q9B I(%1  {Qt -`.jmШiPѽG>}4x(,;n&DDDL?EL0~qc)#G GB0 `>tYJ%dhyldʠOh$`(Ob42dʚBh/*TIVPo@bH@b&M2uiӧOahiӦN2eI#&GBF`@ tt"h֤a:(SxrȖYې  &#{"II<w\Gs޶aKPL4e3fΚ={Μs͛Z@7oܹs̞5k >F>ӫGGMկSZJȟ'DOx%%2D39r+sѢUt',&D9s_pѢK,\ZF\dE.JΙL<1b1`<F8wԡm͛4WFJ˘h Eh;I4[EdQX2+I.Zݏ;6&ȥ˖XrUQQѦVZrˀ% sgϚ1}Iǎ7`#Yh6$kOcOhp g{< $`(]"ĝn=4t8b1y*P1w@LX**z5kcb֭[~ BĬ]fU@x̝tLp6x ѩ}V-4[K/w,?EÝ 19P!V=S(UO3fTU!qnF1 T,\|h@pذqӦ[lݺ5 _mٲyMu1@|YE ϝ=sIƌ6ұmMC4J(R0'h;I\'%ILn&[ .s1aRzM̺۶m߱c]vC޵kΝ;vlPnٌQ+W,\ p̚1urx`c>=vjFhT*Wxa'h;IlbOIPJBIf-۴ܽW?bx"+VE6n Hܵ{޽C_߿o޽ɮVcW̛=sqطW7FݚU+-Y`\مHr'd$C I-Pxrh06 oש[Oi3_qXطpȑG;vsaHdžu1kV.\ l̘:qCC4ZhҠN 'h`r'h$lUĞbdɞ+_%@IǮ= L\Lb56l[,@HO8yԩӧO1_:u@#<ضu˦ 1kE ̜6i˜FϮ۶j azh0 -w0dR1D p$6jbQcw"> -L<8=w.\h -;w 9`=E8˖,;kdBo.hTF#7 # 4Q 0dI9I+Бc&L\,YFbm;ww@(Ξ=wpt+WI៯\rKyǏӱoϮ[6_jْX FݚU*)Q@Y2R!VNNG$5BNFMci3kocȱ'O!(.!׮]~͛7oi/oܸq:@r:vlܾUh5fN8n0a4Z4Oɀ@ Aer?&&1`,zFG2ns/^\l"s!8}("wܹ{=M;o#"7p9} {wm h\xO׫k6aMԆLF *CRg|bTV }_|ѣ/4>DDMbDc֍h̞6iܨt@(fp`9b4lW?HU`_l꘍[ .N> N7VCO@_j¯?FBqʥ NDƚK̞>y<@F;I7$ (UNz2r0H6lE?B\\x5CPO~|S$lܹfO=|`m72da. ` L =5s>I %A -Ɏ݊KE%, [w |EZ"H`.QFƎ={bq>'`8"I22 0(0*NcL=Lo.3IyA #[|X1(`ݰ8qbO1ȓHysr!&/dIYLWWw>10!ثwN,d`*^BZ(0&`ȤD_ezء\vn׺1ЕH0d'pW%<ӒS~,j⅊ 0vNc֝&%nB -!$:)duF;zHT>-7KhK?*]w,UaJK(_bWYqXNP ĞOb:7.60dm[Gq$c5R}{uH.6n'!d%*-|U2 --Urz[wg1V0 [D“B7&XࢤKR2~=vxxo,fϜ %ϯi(d06jޡc1v=t "/EE\&CCԷdE\Ş47l`2[E -ȥ|60KFudu5oݡkD`UNhCz@\H0̬D$%&%ZI}jB =͕9fJonb0X CEfZ"  nߵ׀a#ZrJN -0 [͓raVER"bσc -v!F#bPvsSd!-|UTZҫΚ`l \X$%2qsN[=tB {%T`0V'uKfXoȨSf_b-;Piaƞ0qBO0)q{5B Y-KaĞrqŕ/y.>i Xog#0?{Z -)ucStU,DhHV.bx& ;$LK _mӱ{#M1w9p O&lUŞTs ].whӲiúIA-#Ġ 䛜60gi2dK>Վݰ5iEˣm޾gaC 1,\x![hf[ 'IÓ`)ѓh=+a0Y.A56Z: E**\ˢb6m۽8oC=5:60̤Č=Ey 1'1QŰk{xVvfw|*\Ʈ>1,`8B e1dR"FyKUMKVq[%Yu F(@g^>1-i׹WDZ1v>Jƅ AuO=qCB K=E$%=gbCwAOWE!c}>1ÑIcDyKaY }p'#d3}RZ2rWyԹKW7?)))[83TNd0lsDDg㫧FJ=w3:f1d:< ]A}λ}'%f{?3*[a ' ˲gI8'I(2%j20E% -/$Aa)c\h$%kV-C:U1Tj4$12^b$D95c!-BM*d<? 1S"3 h1@cO&.AFq9AC\}bZ3\ι2ìc}Kebx])'m5Da}A[' HF0iɩs糂! _h`a ܀uAWak\H|=ȠkzMp$j.%BF|7XJJ͚b-%Zʞ$QeOX] ,qa9ji: >Mc m cPR"c 6"ĐjOӗjhA(q7yƼ%+ll9gC1DD&%*U0U FB5a k;c'NO>E큑<+%*[a HJbl!({2zU?JdDH>UDkkj`] t0٪-|~bOqi#`OHXS|@mAh$c#Fi%>CNc2ҊOs1ҪxyH? lA,qX>v>oa[gAF#YeOrʙ 1#IeK>xdtYW[0F=`O(êV.ifE"G25- u1CMwA̤&RB ʄad # CF} ԂO*!'&/ъ"a3zA #'g$80ds 1Sr1i>Uq1!%F:jqY! -s2Vm}8ns8.~bU$56*6SAw9`ղjQ#KĬ8]"(V?Lx=0K\xA[K anIW!Fӗ+%FU%F%dh.gPO "[{rxr%bZG&K0aŪ 2di 2Dוּq5 - Yh-;8HdYV3a:[o\L4sY¹>3[=Vo1 -7_bN`BZz*f2] 2<*[659fE\<`X{b0RRHX'͘xŚ [wvI #^`q5+`§xF1([LJW_"f4I3F%#`a?<.`8>ECeC-Ǟ)o_6}fUJXb6'K܃ 3>`1iB!UkR±gb)/i,~bU\K2t oRn5)3q/i+VU+׃2 l`E\{*-[#1/iQ$bRpAF`Z%ShnR<|JX7r&#@GV0*pCշ< Fb(_"&?/tX)a5.*z.`]^8eK|rR -KP%Ic\2aud\F`Ȋ+#/)/}iKXK\ᵾNJYA1aL`sa=a d vW`@z;U, -ڗFKpVLX=g2 &McpT/:1⧞d׏jՒzb0`S`H2˗LREStX&.Q *a0R/%zKzzKd3pªLY2 oӅ#cm`0dZ/Z} T : w787pxǰAf[I'[/Fyyս׸܏A` -0K01YSK/}Ӵkⴵ&qо]tH!qD=|o3.y%N|MzWd2T)b7TmURqˋGX22/ъ. wK3&1o > d%xÚ/[Qjwi FgDӳw0ؓ$\|n;KԀx+%_U *}#vo߼ Mztn`r%oɻtD%nO|U>e{ȁ=7_k&9_Z FIa-w,*fq%v_EV0k{oYzŒy35.8 :`p-VKVK Ic)F ߫K{: *;| q˗x2 ->EdeYS':WBequ=_H+_dWF.55bvhݼQ O,**f-5.O__fn䫪…ϵ.=-b̰uǻa0KYɫ9K܆2,A*\6f>: 0J`|`"x[P{xD~ % >VpaSu:iѸ^*K+(?Tmw#iQ7[CUKilp *dPS5KڄI0 -9=2r -U3A%Jꯪ|^pQ$I}\B`H:,eqڡ%qf4a2d%8Qtq#}Z>%q_d/e,e/ءڄ/ٶۘ%&KZrNYd,}R2'n{0~(`)h\)*3A#TilSJ ٮU3w/?wv[  -T ;T%[)"_q`pOɞ9/1I8K,k0Cż/qDfZBwW1lQĚ8J.5S FR)R*2qw+*K\▴WӭpQM\p1~ɽaNeq9ͻO~/1fun ˴d<WTDSuR`#JY7ufqN3s>eDKK _BF&f铺h$[2dY,4}Lpѧ--2apB*}R{\`$6nQ),lw]|GwKdZ"U,dT(S,}. -!VeqŪ|{ˬ}p -YW`H:RFn}p(u%ڧ(nHKp"usQ*\&`$\;i Zrb ֗ C>8%!-]MdP!CU;]jf0H4qVJZYw%A%|U2D[o yytҴ RF0vkU-13_*\TOTYXk2eqo_b)qYjX=-UZ"UZ &r*C2[JMO}Rsn (-U -?З-t62N kAFWD%FZUB?WN`MeR}I2XmAQ›͸$CD;vKdZbW _?`킉p2XA%OGcji2>ȳ&2Tv(pdSě"8Mկ%jO-9(P'Oˊn@-V C5XE퓢OQoPۚ-9b2\7S\V%*q%R[A}T2}bQ$n)\L*L~Z'M]V dPKtH>^*T3VSˊZ)k 2%:)O-ɣ%g=Nx:d`kώ뢗-3&Ż蓦["'y6՝4?C RKDD2Dkbyڧ}n0I,[iiؕwlSqAEwB`FFHK[~]PpPSV_wFdLAF֨>)-|y4Lgk -? I f*Ùvd, K\m[I2>Y2abk~l.V &J + c2Tk'I@g.{Icw#4_bIXɄUU2TA%.yE#׌5`i.x2l 1dvޭ'<&-A.ܕD'K,  -2DU [ONK|grը~ W4?vQh٨}ZO#ieon6L짶Arg4/1zdWXrkDzD1m ?AWLڂ *qyD FRʽa]=5߽}e5z"K% O咱ZϪb(~޸r va͊%4lT2 K\'rX7ߵOU᧭&KTJ4NA꣙w>}3c|ǫG,8:i/KTºf-?93? n.WwG /ORºVdt 2$#c5&wEE5-g'՗̈́f.т KO䙱G7ߍG ?%r찪2h"+`0^&=~z$f?xCvZְuk 2a23V9C7ꑜW?Dqi +nC%d,όU5LN_1|w%wo찎IUf iY`0RUkмf??|QUqc&C22Vy0Q:#t~KdӖ 2|W01uNpFk%jZGvX8~6d2VGD|~~=ݗഎaU ĉ`2|Uwĸzw\|y3 k=DV5A;d`$grU05LƨoQq ?]{_`M1.D 2AF+pJծ6ab|7g?5e%&;YOA+J <.7L zabG`~ڛN_KD#M?EUKXeJ@%_9lXeW2,:]E/9&j\rS?ꚰ& 03U+ -4L\u2ȗ~q/*~$dMEa7=V_fk]=2~[/~ /䖰}t(ᰛ =c5{*c5&ױ2hk}/*`čD`@o^Xۃkxbk9/4iKXC o(~eW</>a^J7bř૗hSn5.K$9$TXyw$0LeL=V j ~L0>%%/e% & p8MQҧmk 8/}ɴk+,~  _{JG"m&Cˠ"MS5^/dX/1 \?a -߀/5>L: d0X9Xkw%n%%6.,T4~I}à -G-\"N=2VJx.s\ZK5Ҕ/ ń I ~ow": -fj2$s,CN=V#c5v9& ?,e8/`*/q%! AD?8ߕtH8 c(rɱ c*8v~c *c7"hWҶGs\8,.k\!Hsca@ -z.C!#xf2\\AcgRQW%:\X@GBo]"𐶃0 GBb2zQ2֨`c "c56U,.Z`1%j\Kl\sa`TH(?l|CPx t H(6tiK/rX3Vg'틇vI2͙`_KMN.\XP_Aoj/H@4:p$aI.G˘ -QI Q[-h~KB(pB  -B0oo&46먋;&,E.sJ~ ?K),W.3nPwJ.0@* -Dw}KAz}oƻ"!CacC?]8'OyUM{e^0g_O{>bp@&>H2凚>J_M8rg {p*qgP褙yaXM_KDc Pv}@Ï>O>$&?Bh=6 áfd*r٧Z'M2RV381TX "RN&mtOIiӤI |Gҡ@6^ذg+[L.Nş1,2 {O+d3Ht.L, -4} 2f g̘!Cz`$mԩ d bt)φn2d+wuq5e^}{,#`RF XB— &D@!#%klٲrٲe͚%3@8>LIpـ<ťJōI.e˰gqjO2/%j&8| a00Q\'"@*2rK?Ν+WtHp|>ɆElXM$%{~n$2Ą*eXb&84| 2:d.>8Uj02"D$uO#9rΓ/BD%K.Slrʓʕ+[LҥJ(^(t8rlOpK_5xb2˨$ -\ -~b'mb*Vʐe%† "C,YcQPJ,]l*V\JRUT\bʖ<3f4bC)l7GGn24U+9!\Oeőʩ RF 0`n -l9r,-T+LTZzjծ]TvZ5kTVJJQL+SKF 7a.4.Mr,r ?E'͜p/e*I`Hr"ˆE+WVfu֫ߠaF5j԰a֩]FU*U,_4Q09M6 Oyb6⏆f2J7i%j.YsS~bu̩ 1-C×#z@#ͅĢjԫ߰Q&M5o",%*,,E͚6iܨAujլ^p* SХPhoPɰvVLm3piNeJK`'!&]z#E%JXԭ߰qfZjަmvڣڵk׶m֭Z5o֤Quk 8ʔáHy -l[aC&Cus)hkɺK]<@Ke- ?RFdXx 1s"VXkШI-[i׾CNtڵk.]:wء}6Z Jʖ.QȖR0p(qPͿ[iz]f\uqOq' ?U'2a+e$G8}$ӂȞ+oBE5j, -o۾C]uѳW>}JӻWݻuܩC@FysȖ\2G &Cum4G]ܻȥ]=f?᧭7BR1ORː)k -)^ -VvM:tҭ{^}0pР 2t(cA׷OݻvAѰ^*W6*7W4p-'&Kj^|0qŵ -?OUti3dJ_* ieȜ-g,SRUyE0haG5j1ѣG9b!קwGM7ȟ'g?hCx 6⇆v.`+-@]ܣeסF-wB"V6c fDORpR*V.hױK7b!C5f""&ND81"bqcG1|A!۷iBQlElI)0GQqhh8M6CwGJ Xw ?1c ~OuBЗH lLA)ORtJjm\sѫoAC=v܄IL6}ƌ3gΚ1cS&O0nÇзw;m֌بTLIi6 I#lx;zJ \눗ꑘ^ IK2e7&OJ 0,Sr5 oשk6b 'M6}sΛ? ϟ7wY3O2y"1rAFlݨ]jr`6 -B= -4L 8VZ",C,[TWZ4Cn2B02 0VRN&-Z2|'O>sy .Z$re˖-_X4rE ̛;{֌iS# зW.F ժ^Q(ؠEC#dwR6/Ujz1)q!Ad1t5[μF՚u6 o߹[/bq&M>kμG.]b媨դ訨U+W,_dтsgϘ6y؏hռI:5l. < -"5O4.Zin3Va"s\wfL@_AFy -)A`4jֲM= 2|f,QѫĬ[aÆ7?7_vut"/?w֌"ƍltnߦeFh6ʗ)A$^hgM9,fh`o{Tt\jȫG4OX!~ڜa2R #wT 04l Ŝ,[*zu7nڼemح[l޴qú5@Λ3sd`cĐ}{vԮu 0m/7|Ahx c,LFPwXɮXqHԪf_0L@Y2ddʖ+_bWQ!ѩ[?i*r|e7oݶ}]v#{;oݺyӆukW̝5} -ڍAjʕ*F 4\2X0~pv=Ma"2VZ.~ڛ!~U7ibL%U^AӖƐc"&O=Xz-`uݷфIc3UZa2JR^`L:sQŶPq؉N>}YЙ3gN:u {X0wIGGѱmf֬*ȩР+ WaI&dmdݵ枱ۊr^g`Qh7Q0C:eɀ ]@1oҕc69vgϝ?ŋ/]t ΟDcGwnۺ XxRFЧ{v7W P@#D} ?yE { V d=㑱 u]m~X_l]D5d/3p SUmں}rX9{K\zիW\t8} -8o`#jEyMF]ڇh\V5LQ -6J&f2ցg]<@Ɗ2b |76rj h[X -Uk5hڪ]KO1wҕkoݱg#N,kׯ߸y67o޸~+Abc5/Yfc˜!< j$.׵(T/"plE0ƌ_YSFƪ5Lļ|WWB5t8mMdgRNh28m`1bwSg_D,nwܽ{޽p޽{[ǵ/]qؑ-b,7sD(!؀8@e(?N)bi{f2&/7L-_Je7Lգ ?dZfr&am!@1sQ1b>v̹ ōܽw>s%:qط{G`6ϟ=mҸQC֩mKf(Yqc/ /QrM|C3j6~CaBWB.ReTZڥ׀ac"d/ٰenEx>zԣG +. Svm߲1&zE93&=l`fɀ1Q/3 ɸҲ ׆I5߅H#0Y 1vҌGlݵQҕ`/,}'_J=y1xwn@6N= .%v5QΜ2ap PΞ9CZ}Z2s U-u滸zrg219~KVYeǞGN> rcӯkS1[!amԲlD>DLˆ2U_`ddhXT-c۽c^'D_C> ŒQZj؝;yegOoVo@`sRٱ<%g}V,SROF{R5~i&}f2T-c6 eX&~seSg/\iDOt-`0 OAl@.ƁQEX` -&kUP6%b%P8zy UM=Vj2d$DO{ni._LΠɸsO b?M׮\<Cvmۼ~HDc,96aM׬R_Sf&A 㾢f2Գ -fk(z_ Qu9pɕ`2>$;$!l<. gO;vH_ktlָQ(%(ioZ=~a~}@aeSfUdXCa"^(U:au"@Dqu2>ɗ ⟠$*h6._rb1O-խQ;\k 5 + `2\gE.Tv*3VmW0 7qYHY),M7n{'O@.~?db߹u -lٱe@#b̰vhݼa=!-?Q=:aMX -6{FƚYef$| ؁Fofӣ8??r%a?r ˯;ao!)w\:ԈҾU3]XgML -0"q՜ -6zk} eXx+lغ8[2BCcFo\q葃 H^'ڿgv-֧GB -PBø|8&C=gQSfUf01䞶sF1v.^eC mH 6x( ׬X2¶aMB | -W[ǫ1I&C+rҊ\TprXImx -4JVqbJm"E?8[$0RdC @3B™G0t%r$;BVXB|54Ԇ/7ֱ NJʩ`gXC {t/Xt'>+V;L?kyGH?AW`!%40ր5z |<F 2=&bP&R22EkT̩ߟeJ4݉NsVqS.38 ^H.48d6`quE/[4_@ 򘩫*'~WmLH%(rj,6v'/#m }xmcd4pp') kٿ{ۦ(%E)R|X\-rH ߲kT䐱 -*LIPPd.Hl`Р0Uƛ6bL] ފb&uY\Fm -NݝeDJz*"z M~26L4.Ẽ};c7+$J&]O߈x8ۚ7.nNrي\b AhXu(oAM5?rMv JZOlh@J[N; Ccծe55bL^Hzq\ʶVᒘdhuqGKeȷTp(gBh~ D4\p4>+J/J Ǐ yqSGd|l4jkb^O M{]6ɅE.s,#dB.h|JmȚc'͘25 0~/hPGx^Ņ,zB50xS<0u_𪋛2I.`*O1Q2\itO߬FCkr+6mtޭ5hƻʟPC\ozrk2|WNrEd -9';o=Pk.͟lhhPo?ܕP+3?ySlЛ+2oX7Ydm\E.Tp膟$g*A՟Z\Аl"~C){ P -51ODcYNQx5yq\1pIm×^_NJ늝2Q]\7'E?$$II#wո Ի~trT(:ػC<- -2?hXb.dzq9e/r~ƪd 33EyKz\wӟy⦵:bYzz\#s+W&u^ܽ..'"WrX^0V"y!q+?F]n: \#N)jڗ(d[ɱ9ɰeO, k.+^w;*ԸN'X#.TQE ڸB7/+l]L&[iֺZ.nlpB>$b'6Ʀjԕvϼ?$ouOVX;_{ 6=.c245/n\"WB?fԕJf\qZSPgņ#0FR ;(Pj j3 }^"udU2e ߲ս$3uUP?1аFߋO6_WAur(4=>h~P- dJki_`d4l\W4ɦeݟP -5jREn2!7ThlC?*䛗ۨ2 =; ^)צSQ?2yq#zdU2eCCd.-(B*5ԕJ^ĂNwpLyd_Ô|2%>B|vZry@4s^-`O-I5uFƬbTZK5J\2Oq'?{PA%G(]ɝ_]y 11"?/#hWLK gu]1LL5 *~2r.BȟdSORwn0G dXZiF]c,jUr1c3暣ן7]A3|6MԩӤ4}&zwENki4qŲbdu+&V'"8C Gܘ -B] 5ΤT H.]t ծRHLzkiɸh+g+ͼbZijK]<BxPz=uyރD BџhdΘ!}2fΚ=Wނx`(AP&ù,Jyq*c5W_.NgTS<sɡ=$ n&-hd˒9SL  dO;& VG+s3"԰Tɝ4sj?q}u+Ι=[VPpi"?at߅r dX#xS kSvԶ6=0RWj<$:wq@شQ:jT^FZu{=gvlwnjZOHbퟨ'A ArB٥}xX թ]v4n -!75炃5Nd\喺]\ -X/rGA}{tn޲yF ԯߠa&[ңx0ƅE7Y=,u`G^d&#, Pﮚ -H(nZJd>=:wh*y&7iڬE6;Gk<4Vf_do;;w㐫?'#b^8Pۍ !2W$G2j>=th*(Ux]z0t=b2h/3~dȇh`>stream -#M› ɰ_%h2,3~[i]>}UG¼nlُbuB0E r)ch٭KA:v1_#_jO60fv\KHӟǵ&4/L/@AN$m55bȡѭkΝt֣W_cLYͽPtkm,9r 5\z3 _ӢgQm)yd͜h Կo=@={?p1̘s>k&:vz_de!?.߲h(kE@A#24źz|(/ti(j:j""h^lsgϚ9c̙[x -cKGNA%xm,Xnfx`'(yֻJ*H.\Ŷzƺ5/]hysΝ7e+nؼm׾CNSk]r `2\ǂy/'j^ԚV - \uyҹS46_rŋ-ZxIQc 2sܺ4?YLMX0nwV\'H.K(j`2WC D#v5ѫV,_Zbe6nA0 8ٺ~w|XmƏr'o˗sErQ&5.˻П\|IDc-ǬY***zu6oݎ`? yd8n&},vݟq>5ų㬄 r3Ɓnٴa]Lڵkc֭߸iiNp1W{.EfФ~L[cMeŸ\t8oN`c󦍨M[]{:zux+hqf2x,Ye'8T$WE 1g BJhPFC޾q8~wumہ}dܹoyD#h!Vʛ!g\,'5܃Pձ S'>ݻvv.9vRDD2Jk\&. XO̷֌x5g8h k~/;}8q`}H0 /yS+ΚP7 ` ZnR T$GshA(=иx,q𡃨C;~"/q{doJ7 \J^,j؂Z~2W j?1иv҅sgN} qa%d25Uw L(.!nG.w l}?{2n`m2E*l2N^AhE{ݴ;n5rW4l8tEs 0\l2]P#s팙ܵ]h(4W̆",`Hj BL(etQ\g~BCApB,$.`Kz*hF抷Oh{+н$;R|OBn`12W깺hɯG܊ !wQVܫf]u6@(zBP   B HATEDE@Qٽuu]眯 !d -'|LsCmA}ad ȓaAdTt&4Grb害z)k0oPs0{ `P'2vȨtr[nRNi3>34 7?/ IUvWr:KQ̨7DwH|4"#RMB+W1Xh) ѯI w+ lR)RMB]pJrvbF57Dw(~ T\4◻.Fii ]Kc DFХsXvKI 5ƿ*WwU@d[z1W9[8EںSl4}W ݽvF% "ܟW.Rb@:kB1ca+*{龤s/wf+$KN EPC.q|AC #r; 241WnKZ#')Sg*k[tF`oZHJ(-\kHNkbik X\44|C{ DFHO -]vVm 5{"#]I kxJR^J]9XSE}"2^]"E+rFhdJӫjZl!@Z EOO|%Zʥ_|P2sjɘ<m "# -,+wM>g@d@P2j=ԓ IյBׄwԭVC0/q͗Je ahDd]>ʒt^ ?Q](8d L0#2-$U45LC2q͗dqYk-%V̺&u,!C*XDdEA#74kO|VE )"#4TTqFdIeIbׄFG|[6B*NYReA#RVh,CV%@##gDFNhhB24T]W|C#A a!Ӑ -4xR#E""#$ 54T'%rG#E""#84Q_J #ÂEDF4Ʃ)X!J2WJ\tyikRi&O4R(G"# -D ҌPV{rUA#RAԃ\)^6_ -)ʠQ*A6, M#DF)h]`MjcԍC2/>‚h-qh re|@h#u)^`1oC,chyBɌ/Ȑ#,%aFn54^XߖviR`94(4*WO|Z5,Ir?y|)$M)"#R?iG܍&m-YC m^0WJG!+P4z؍4%.%PU&fI8q*Y/xdR> -i܄'eȅmŪ$.<2nkR> -qBCnC1i ۖ4taK B\'rXxkЊ?1|c)AH `,Ú2폍'T`,%iiiMxHcaq+-0 -Sj0ckR2/P RЗ`%mavX]&sxBw|q./x!/4д*I/O5BCn8y:-0h2x4`@qȸ~K|n^zEDFd/WkT22 {¼d5ax1OЀ~+V$? 7Os .OjO51X6 2X_䇥a Chmn*K?T?)qAJ[MCdaא -3xnJ G)I~ҁQHh) 2 o0cfp-]6m^0Pl-2?9$hk~"&}dgEq/ 4u!d45`,ᐡ$0.4t3x`W@{O/б;Bk'@ EڟK[w1h31%"2*ҁ*m Nj˹]  0AW ʌig3q5 -o`<k$o&`I~_0&4+j 7X?݁"0cN"42x`ؒqƝ?|oO ~2v.4Tgq'` 1M60$pq5폫gи~kwݷuK m^ŒV q5N -q[1k𕱃#WWmJj׾Aq7_1z {\:;Qq5ȕ- иoܼ5֍#R\Ml\"gиUbnc\%iז 3vP]"'#9_ݸq`O/_1 =va%Zl.q<-z5c'^tg_1>\m[. /)!TƫGȥ''N#303'X\c3vYJh<yo }xrO\Kϟ=u4cgD=vQg\䷔?y˗?&ƀ>6;$>&* 8u pa{;0vd`lBCX"Ehl߽Qƹ.\8Y0xWD؉\"ilܺcԙΝ;{a,ٵ F=v0c'줷D"q5ƺw;x؉N9}mٰJDޛp!49%r7`aʌW/^jm;>rػk *_A2v0! -,@㵅+lز}}:tnZr颲9d`="^"Wv Y4XrFؿ`u/y}L~ ]bw&^`U6mٶc={޵c﬇dY'USd<SZb /<&oklݾcΝ;۶kV,}c3PCd' -И9Ɠmm۶m}wkV.[it\Gd|&(z~,^ذi-[6oڸneK̛捠{xKyq/L1{%VZ~ƍ7_ (_X̩ rd`Ì.EΠ1~KZxk֮[f+/?i/R# ]4G-yqz ,]b۫Vb%-}yJɸCd GW$hUX”g͝pqeZ|iΚ1u"L? A3v02\b[%HNI+z~-XxIyyŋ1^ٙMV"W"@]q6WrNҌYs_pѢE ̟71yafk E BFRsY -ƕ5Jg}l~Yksg1Jx.%Y% W0w%NY:9s:tK зW7 G4]}x22dҔi/|/O2haޙY-Y_ѡk 9h܄44'NW4:gR{vж g'j ONy65#+gt%'8eʔ'O,?ptNVF}{'vJh˟+VDIŖF6Y# -ƌ{~B /P2a1y#2\q2=bZ&2cמIe_8f\qc -Gf<%-=f@gtLl:S^)2]PXTTTX0:7g0b^:%5NX2v Qi-ڴػoʠCfe=|jڴ W'.G]24bZm;t1t#HK?w۶+V)bUFz Noxj N:lذCS=ѥC|+:4[yRvIШۤS`~)NKOOO:h@3I l& I2a-h \6F>}|6>=1Z͚ԭ]y@DOʵA8CأWR3գ[gj ƒz, y҃O6F]zصSxb +\'KBdDtA2koѽGϞz=kڶjNф%5 ㈌0 I[Mh߱S@]vԱ}Bd,yB^DdDtPO5nomuرSN;oжuK` d҃'0kvym'ŷmӺesNZDFJ GOj&hb-[&jղacԩ : 2"Q<4H'0 ֈmjwos"M|anژ?1+VDFDI ǩ555mj;."n4kҨa 38+VDFI -S j7kfZmDVK3sF&XȈ0`P>xF-S Mƙffs\\&c 0j i,AdDРS b l7b5n܄qF bkV yȈ,IА0 ^ ӠAC&Eի=283"Hrr8 ɈRNz3-Lk`<$5F` DFD*J=րZ5k60jS_Tc<&RACoaDo< Yf0j(锪pC겚;`8 \Z6x$|^t>$諁+"+2}Ff ݔ)bȜu:ɋpIr;n9vx$;v݇c/U1=83:IIn4Q.tS3@('p]>stream -%AI12_CompressedDataxneɑ&8?HPh$kCm]B DERhŒFf}~{/3"S]U d(E1m7_/Ӌx?ˏ?JW?{曯?~Џ+xgxwO~OOaA_~xuӇ߼Տ2<}Wzz뗯F߽o|Ip_˯~ɟKlqnᄐ}Ep*pWO_j5"kK|[/y|y7o}_~Wz_n߼|Øo߾8 t3p7_7oiLK]_\,_?}8z<>Ϻc_~k]1w`/?y|"x]|M_U:Tzj~7cu|yLV -y  )_ܥChs1tZ:Yf/ӫzɦ_¦o<}Xߏӫ0xRK^_>}{o>^mla˿=}XWO~w1kc=lJkWckE ABc˱~:sl-?~xŲ5\5⺭?ǏOێ?_w_v}q|Vfk96cϼ/ogl BwO_f?櫟ÏXW㵫_zzqz~Oga}x?W}q - z/OoB;!/}q?_~Qw/?\)|b~|c ՘0D_5 ?}BC/?iw_|=qۯێWOWyDBmR?xw6A-}Kke}؏/߼y凗_s l9oo_] -˗>~l/FTۯD^~8?AD0(ƾx ]2/>7y'/rpw?~W̫\ӿ z`W!_be{7GΞ?~xzz_!U׫_|x˧1xnH~.슫nܭ;;w}_}[w? IЇbHCl)B =܄p w>Va|?o;=Z(<ڋ0Zw7'?ѯѻ>zF_s=c0qv1!9Hc&˜]{s?f(|}ݘ1yfƘرecDŽߎ=WƪU:XvDZ{c5X:6trX\~`L -21 ulm9zl۱Qovmcֱy/-=Lx|cߌÐơhqDe,86܌x&n,s껜s껜s껜s껜Pxx']|sS/>sp]|/>տ1sBTAdqufvApb2jeJ)a*#SFD@:m2DdThQ(!R!AFQ#d簢:Fs1Jct(Qru!mQbTEi$(JN@LA^렆ypakk1Ǩ -2^{9.o>?\ރ|.nF?}>r?S6OGw>? y?c|կ囟vaH1-pM7mV9םre -^_fnyUOoO)}PnWDeYYwU4eG*9>*YŇ -գxq -QŎ{QCTqʵj#Q!Tx4jnT=UQN{*^ՕRR%TTS9sZ1GDT&PwC7Ȫs -mR1N1;TUJ]եU9s=&@6jCSaNؤz=O๝ϐUgJX~36šݓOr0l|¿o9זXik]~}S^?va#vvu[66g؄==O\[Xރ* b f5=Kb"#G5ܩA NՍ ނV' A,sbQSĜpV{ODx؉NtP]V<2=vtG:ڿ;}1՜ZpҝһhH< h[Ynv؞y'sXRx~@~6@aON\ϵo]߂vy'9J7EJ^>~ES^??h7#+˵_z-eN}HY_U׵ˈ/qgV幗>g1~˷O_\} Еpxb!p <TzBдnsa,Oz=+xT~'ly|+M4*>msznVqB6}+LKQԹtTc$w0ݘ۱nul=ř|{ $(ç n }e^$ ^L/s\o@WIqq|yWo߯l>[{ĵq|8}ꅧ]x`O>~9>oys`[WZ^`S6Yx60d]ut啳Rvt98w3^F \VLz8ϺXtcƵZ~pua8l\xk'ڍv䅓u=SnsɡLn(|p69T}Hc7ߒȾ/Ѓz?;=t=I͗D'\dÙ= r]aki4M>OI]REgbY6ɢ\'&$_fJ &K.o+sUv<ީrZL"uuGI$n7{y.G$)(q.C~UVb),y?粳}g47vƑ;\:ug3.=p;egexʾkEDd;i!m[WNK~; -< *hw3ӿj`/'0n$u1?aNu㢓jcUw[7ܔ0ƚq6j;?xF" =7)smÅ\b9Sq8?ϞO?|(}d-8It B\ن/=sZVA܊a߼{9c UK>(_F1?Əkz~~/ꮾP?J/qE;nYlRv8每I} -DK_tH p\ Wԡԁl4]Upٷ **P_/l!1,DR]R0klʴH(L+!He5~v|Gavz'^]>q.5=ܛ:+W`2fћgyoo&$ͥ>f~(uY^맏k"o].YץqadJ:e ~}2 n=rx CΫot‚VX$6JCjşY뾬\O$ڨ\%S:iL - -vA S8it}`_\B}['. Qy+No̗Fxo'*'. y$`fߨҨӻ7$F9N`_\鐬pH9 U巇ߚ6ĺ)[~+zۋoA)tCUx~'byinp2HnPꔣu^(O/Ef_ C -1Eх_$0Bkl)jc(.WC11A=$"^0[7)@3}zk˼!vh:W  }7\Ț !yC40{o:t3ޘ` o6Pȷ Dz]kX0'ĉ!W67J:cFj|uv-q̤8A48 ^co@(zzM9Q1=9=!L }D)} -3g2 {uEwxA5Gu\ NTSEZ(-'W$\#hV{6ztCp%\* t0qb"(F_m@\T$@S41f/I7 f&[0smL|@4ޱGSɆcA NIyCc]G$\'${ٵqrOb̞A 6苠bD`u{\X:xxu0" F7PsNP X'+Z%WemV[Tfg++%y 7*L|`EA[I* 68lC[,l]!*wyzFO|Mp5Z"ZSـ7` i ⣱W$bʗtTc6JY߹eN):LYwjf!dujf䐱5`\@ʀwOG4粒? d Q-##N0UCP[ptyJ2-lB\.$4+jv6#)XIC *t_`3 +&y|0d~n&4UdjD+$45tc6c9äST/4"0n7PA-!y).fw)U!Swr5`ùRpfmX)J={@-LH&Q녊ή~ma7/o愍C -I|L|>GiU;̹Bw$@ C" -ݨO}ҍ sQaGS+ؘiƇ%L&rGDxs+#`,.LgҨTq48*̪bb\ŋTg9ݵfn#$WI9z֕c`+6U06]22t^ n&çV~FuOg8`XU=[kbNqY!dZnUg[{JG99k E rFp3̜q7b|1H M֗ fdd/T1;Upx*k< -p(X5glڡS|  oWq$l13)2cEn48W{ ١2 Ct-FCa)`?*MM.fY5恳cٻ#8odHbx(wƹ;E JLf%EE;I@o? MaB @Wyr"-WI=mx]#n\Zbf*) l -A"# k;;&%FPqv IcRx&1ƉCBb:$|SLm3;LdT3kC-@W-L\,4p%"mih|Q!Э&Ͻ*Mչϐ1c"C~t RVr@_r sxLs]+ACl2C1!ΐįk@b=8FBsiwnp&Q44 "At\ovs8\8>R;FJ&DFi[ -󣙊"ΉZhi_u #"c5 3TOjo{Q(|Ua[j^h@oSntBQE'OfD_zSFLd0}2€`d@ Fx)-!3%w@Ov7_<=$cjK\9OӧV6iůPVbOo-Af4h?ϓ<Fr2Ev<%%1n2FZ#$;r\gz!J8J!ʀ 0SY>M{c}vof5á[,u`k媑WӌƯZڟgo\ƅt'+I.GE -vnAfILM% s76 W0NJJp-A9yA:dsy/>R`.RJ+ BbIF؊>2TMCZ;P1)OFsE_5car|q Z K4'}!d ֐0Vzbf"@0(P('l:ZO ? -qE˚$J?3aTJ"(E9MZqt !Wˁ>z+.fy;@Q>Zc^$*wRXeL>fa6)E;0HĴ\z[[.Q -X*E7Ij /xHx-u rʹ4{V{un3z -I?LbYi`5s'/ҋ#>Vs -.RGZv 7c3nY앯Ĵyd1Y2:[#<3}xKh@/g9sJu+MdD\ -Dl*kBAk(r9jDk.p 'A Jpj%d7Cڡ `HkӄWUE5`4G!%z@L*U`J${|BmTwjmbk ,z1NRpSbS@ZRTc,%cmzvd*.bHSׄ aa6R Lj}DTp$:-)rLPxQh *C2j1= -x<9Z}$N(>4<VV*V|dP&$M8L2`xW:cFscDAdoD(+|n EK﨏XyU¦033mC$WJ䒭i,q$jS$:%R08;r Jn~YfjjQk3KVDšNz~JgHW: &˵^@:)xߋj u=Qg5-LP78Y}/# `zԷ`'\l:͌a* -}/yrBFt}HhfZmom.>c p= `^#x0Sh@SdzwYD@JPm(<*R" ;]-32#`:XʪI!#x/Pd)ۂTb3"ZD wR7x`q̒\UXȫfA0„JdTfa4DIS]rđ#yв%%X RlUJx؂=Ϻ#0fs4kV`]%lqʄt hQ{xUDkjgnZqO4hb5lIL8yI)pF[h3Loޗ~mes0v% -)"e&ayiNsJ!&iLWeFе: pXa825dI\Zyjk$LGrLX$KlFKef4t&Ʋ͙/MkHO9I%f@FPKW:UbKXO{1*'7+0!(3>9IcYReC>: 58MdceYz3 d3cg5U?b - lXƼ,!i8FkcDҚE¶D97VDs_U\,E&,A&L$5"1u,L -c+@jz3v)kC&vVBdYo@߆]7Ba"UL| XKPV@R!az ɂ⬰@ZJ"wC8Y -KUo9,$HXq-?T^gZ? %|Zݪ[*KOqYuA$[:”vZ댥6#hޑ:HJtB>X%]v[6/{<(:s NBLxUY%s2m̯ќet{@!_HG D+WyZ64J%64d˽g0u1N BC?}L\ `| Y)d c,ҩuKQI<iky:YHRJHcV}e_ - E&fy e̜I<! &5ݛZ)K,Z4~['8 unϷ'`-qC[.A+\4o!G_t@{<Q/LԐ_O݅ɒ 3KCZ^ͱLG_YܽRo= -n݀;<^=j6F;/7~EsO>-FOpD#oehԻ+nڇBN>fmfQmmגP0x9Y cc/Y4~{\]~6GAK,u%ðGY͘I:l>KHyKΧ+:+/H.'?D+ջo=`Ea{S/dE=}B?~ $F'xרwqߨxX~H>mno~ $F'xhY?7PCX{kOjUibhgc'$Q0i/U)U%T xC$5*VO|N擛+0'ϬɚoJ06b3\kGЏ頔@ˡfvEe3;Rޭn@f3Z@U3-#o4mw#a';^glw 9ZaV+tÐ~-*(`07Hn'`-ܹ}f@dX@H!yz#W E])f']\ό,XzuphVvH2Ra8+K%}Zu/\IoQUr%[7,[d1 }6+ -R's7Ynw4f~2 - 024âH=^QEpnrޘg?/hNLxx /ҬT92$A)d__$_%hb-`/̈/7 +o6NGaچ+^Pl^ui>'T;.K6q -8Fom"uދٕq -|x-NoWsT*7Co\fdVyC[b)D xU/.vsCW8)m3wCADԣy峇E/}xgۧ/_ۀw^}*nzypo7/cWW yW8zИ4vÖnUWߟͯߡS8X՟7Wb15b5z؀jS`+{(Cb#dxB՜ $ -1pOidzڒjZeo -Q_GZ(z -a -UVU8ζ -j,ܒ1A„%W} -7ZU$SD4k)jz`E#ܥЃMNy ]u%.[)ײgCͷC.VGA̷l73Qrrw`z H[Ż*(h.v@oEYNZq -Ns]X/r|%>L`5 9e@5P# R! P; 0JyC[[X*S1Z18P*8k%A'ĉ;LܰK} j1`-S7f(-LqxM ˮM[1y -`)ϟﮰ.2J|P+ gomFj(f i .`"+Aw%L]gFb( -4F`@͑R^-A"oGI{h@@Ҥt%~cK5p`4xZFPiTh2m ?OkQźVfYntӀ.X`UAV9ʀۣfB9{$+#ޣow WgAgu& #ZR.0Ԁhm:i'8P8@^mucm fj-&!S-piPYD r5Ja+'@?kўIN*X/{XU 8Pa@C9%U_L42[i9>q%l"RgUnb(yEʩ `mXr!]j -NZVt{b7l|!v UlC=]G[Sc=b,WGI(ud`W ':{\t򷴗{# FԒXC!NBJ3*['m; XMYq7=\)Q@|[Kn7BVby`5M;&;!c?sՃVHpdbkDxS*{#j2Jcw0]2J@i--?Ԅ.fé;(ZTln3XiH.P!T1\ѦZ=ءr0(<v F}׮ Y6t+LuP6Va4bz+eY x" }n%ܛ@Duk&}UȖe5O3GyW@oKED1H dHT&PQ`!/K7OT`Mͯ4gk;:%iv.un,džZM#%/ݓCTݕ~0gP΂tI;UZ l -/w<Nt;R?cjc3<_8lR#,Ѐyn3& Pm``"56A<ZJEQ6`PoGW$:+;o% Z+iݢn.(0`#Ȱ RT#%Ѕ-"{؜dɣQPk{8dR=ƻ.*, -x$"e`!DM9FhdNlbo;:Q녁? Ɯ{naAiK`*ؒ>]EASݱLK#ʨ(奒ƖngɿYogFv(Y)vL)8Rs'bOa~_[:_ %=8.~``"'v*ZAk ¨`YDuh)NfD٩yȢ֘\Ph9TU*`NVǀθt4dȐm v{gz#Bu,-mQvv@Ո#HDP`G f% ZlKVL ۾鯧GR/R5.U]jSPLpIM$EP39c 5H58 4VN!m஑ڊDӔI%tcmBUdJEKQps(\R7tK,ձ[C[~B_[&)-Īfp@YCzs([aFC.[49Ls[Gz\[ EW;<)ezCsXu3Q`@u6M'Mikn;ZE/|۽Q$\h*sřYj;Ȱ;Ew%43 lF`JŻ8IŹL񌬈i3F@W7O+P_| ]˂0 -[[jOŀw1@O}xB(,"g@r>.,َA@*!I<$>CW1Z@KX:liI,$6X gH3kMs(ǝ\Hi\Q -\(6`9JpG |u#RrSV7%CF+m\=]5ŒLG>z cV#;6A/C`+0w;D +Iv2EV`єu_ыf$bR 7s5(*io $ 2g8>Վsg/ sfgw' `5?CCCbf ꖂѢ#]( ڼ:ss\Sn=*Qz;=HIC(zft~o͹i=9ݗN9cVFX!Kƺum -rLJ=-P2C|3[h7l|S>}[v`3}8oF U`~wW -qQ~.db1if5o"G{ݢt˶& y0R70->b\Y$V\`@GE9龓a׆5$@|mf+8`nKo(8]/A1޼Ao7`ȟm3C$ŋ((`25Z#Xئ[x:yV% _H}n/LEOg:,bfL=,ʰlBty:U!^&4#IRbsf0, --CTҘ@i-|INqzID fGܨk- -4V2TLJ(y"HSDw51}Ճm('-`v>+(> ۣ- - i*V Ⱦ2ݵ A0UvCNAwWOț@+i:,ViQ)|h}2,``\"E!6+0h@dZH,ES<4N: #YZIcN}13#d@^қYDRΝ{ 6&{%n8qlb+FDFܐ{7aJ+t7˻塞z (GZ4ݴgtU09ٓ˻m }/r`ڤ$ea` 3E&D,NE9\},R8J X^M9̜ګ)踂VH Ae+3̨8{3's"3Cm`"7H'HK,bSTbQԨ̜>e XG|e-!¹ -NGLaL5ug7ImYMZRc +M9M(`%6R"6f`r<1*Po,c* l?6BtyZdlr3t编K si`/` h)U9˺TI=43ԩ\y*@Lg)e(ڻt2s:,k~ e—TT,%xR*CZ8Ty/pR!Gi+!2YM&fps$962 -5ʱKZY?$pk4\"SV ChOt!B1#ʡa"]Sܺ7Lʥ; jhA,, -[})D}\FRVPbp68s]f>F ʝ-.+ܓR^˚~q&gƶ*è"`I"7#V -%P-"*Tipޫ\ rg"PXrfAMcX&e@!}. G* N&S@aJр +ҿBRݾOv+K{d+l}NfetKtl+WO[u([ -6?Ok#y/T>7J a!ZBlj+3b20j84 dZ0Ab|OK &L\| }JUCv > UBA)_@#c|=iMHsla-'meT{Ʌu8x#wjH:־Xb3r\fقKRh@V$%N[63hU$Pʬ$ed`(3mݢ1rEIpERK\h̼L"@Fiղ>/@g݁}Ѵeٷ~z%,B=jnT2rʵs> \A-so]>:OW3OP/W0M4 )*7s^h^_ݔmkFa~9"Tᦐ0`bOfyL|R+Rێɻbt@Rgy2qr%XǕF}IZpi H PZWqY>$b^ʂ޺I|6;԰9;}"{ A㖥_'2}U3QV+\;76%`nI;4XaXJyd_0Rh: -6p֜q֚f*cDUVH35`9,,JjPyjaԄ8ҕ!uJ#Pgɬ:W3SZ:). R@ -Q mDPoou_ݙk$nA i)ZMޚ0:5F9Z*VTmi4NCզ:L/Pi*ӫWdy72!Me[ +1q\饰lnzs_24lFQOF&s@ ga< -j6pq&8Jn&ӵW8:I5.,DJWrL&E3}Niˊdx;&*)sqqLqH "2 t}[z=jkhD6f}TQꨐ.YGN=B:m Ī܌Lm)lСX."+6td4 r(V$M\\쁄D}ƚ+{7 mL*!B aq2d+GÂ2pltYVé1OWwiveB8cw8a -"iBjVlCiDD{ږB;0,S-C`0eL' V6 aM Wd|UÑn%&,KV$| 4 -f7d5V02 Rv+<309l6 3D cg3R`8ƒv}HB3 R[\ShdHq B:gO%rGU8x@m JM~N}=D81?yM7fӘb4Myy?7xeXc #"wXV\;!Rkwd@R[(F*sHqn8Lm-2(2!TBSk"h`8[q&T" G7 )w; B_,"HMYà/ط6To~"Clg'jnkl瀱 ufԜ [Uǎf8?)]HVH (B9(,h8| -6*0Y -1)¢^֖*!3A Do=R6 +@F|זtCyj,?ZT -C"G@GV1 i (cJ97%kYq=H=Nal؛)aAdiR*\M5P$p3{f҆u@V1;eL_Aꈾi܆3*D7u@+D7D0xF"kdS;R ᑊ,TD -٬(R0VcoKH "5ڵ~#%; -GNXFoYIXfO,#4>ĜM[]TƍC"GhV:&(OQ Lc؀2&(64! 26ȋPìLIZ<3t#}q^hBu -QۘJus#e`#mLEp&-hȭtYuWLW81(TIOֈɎ R 8xF'QOSYMIYV]Xwaf97W<@vc%OЧiԂTZg 7 P' گ0o$р9k)^gF49*A_Y 85zkjz`h{@c*ɚwLir-*W? -}XŦB1ƿŲ1}(A1iJRRٚQɒH*ؕj: )$bf3oS=Dd0~S@rZ&лWA6sܮ9A0[Jz#[="gݴIMg+ _OI7񴆁gekA(zcbY堼&haR Hd:4vu}HA-N@Q-7'U;︵JD;TeD~yΌAԷ1`būc(t20qnwD*¼"5ȥD!9a[SUTC48p0+^ދ~X i={셦N[YpbTX VF~W L[a_&hF'S-H[: y$ djPTPɐ-SN6 %@LGMp䦸 mF"d83ex0}VU^4:\ FP4 K{yPW΋t'[ӊ* V|2LD 2̎Rn" :S$ٳ xb# #a j2SWY5|h^I0U4}(h}2RDolfE]-!l%Ծ͇IL,פw9+ze$ lk*y٢C@/Ԁ4to7pb8fږ^Dro{U\A,Y;a% -* 3<[:.=] -Cf/<;0R$y sq^umqk= W<@"բˠ5۬X*sfj -Lm`@Z!_l޳G\p/&$7e8GH\- :ɐcNeD$yq&LU(dUc-瘱M6žPDӵG:J$&j7ĀfDDa;Ё; E) '%Ɵf+آt -6I Hbe&#{JP@V!,wgP No3iTBn51nCVq8H^ac[Yp՟%4aRf5 `7<o-WBT.2R(QdOȳgE5AWgm i\:aR:#־8&Njj κǂ;9ys4͡ƞSJ~ݲ`y4zi#şvBT3( m%`@ ЉQZ^^XNL7+ -55QO3)Eq%M^ (Q2L5#I'.~(vn m0?|&/ ,ԶU mCD(0,l)m$Pk4b#(ћͅ٣6baע - i]Wm^~)(]P"g2kE^ fgMcxk[Ln!qB1=\|٣>ٝGEH E ?#gPv+ܒV^L%_,5z&Q8NaZ<_ X[bQO2*.TʶfDFX"R~T+)(1.ahm*t tY~Z-._k(&4\ݨbQ9_zu О׆u.M }ZZI/'AV\Dm07=Q$Úh}#j"x =R3hgk=|X N_%7su -@oiq1愾5ͶayY3`/Z%!'6Vk.0/+';엀'ԦlJZn-ylx qDY=̱3.D0܄yd vQ -zq)1 iOe[hwRa@lQ /‚U/PT!~փ=]i_w $4Â7!h VQ®".s 0*p?PԋѳR Hmh`d'9(^L#4Df !d |FZ(B"jqP]eTؙeWAf:P#Ix <#i%4'ے!#.0N(ژFvvOܒ VhHO˜3:ӗtN_Ce@aQgGSO crmxBa-Ocx l3 wd_ u%U( Ms'P\P5kzjuѴ_LW]A_3ڷ -vn5gSٜB1qm U UME1U}Ze]zd& K.* [Tfe&Uw}6yYfhS@Xy u!Mvj6JS[xc4 P[ccP_a݀+x :iXPIe3zE fJspflhx9(60tEA}jtD_ϯi%lӪ( k]unȥ$&}Pgb}h7Utf<;ih̐-k~eMUL+SmT[?=YWuܽ*h"H҄ڍEV%TOb]:h]i+rRKKo6" -1.`Q2Ӗ$̀%) bi; -͂떟Ѭ*/ACNPXQߐ9|.fIq]-յAV,n]9/֣$9U3K]3nME[Ȋߚh#W*Y**3nLy{j3j(l2W⋵Upm!?Q Fe9m>F *J'(F!B%W&BtioZf+4[L)) X5~/#h(4x٪ia+*D\I}oܳB@quTݴ˜bUs[\n<hΡ KyF 5JM5LT[;Z:$6ku"͌fG *<6fgef]t4!#!ϰG6:'RRǵf’@½ %TUJT5'J` 4@(J$n)捺;G=4QI:;u[^tC@%|yt] "di0SQAj"Pnk\eEsdؑk/KAay'yDWϖ D[YV%siNfMHdꡊʠfv+Dq إŕ–jTFĴrx۲+V<*fx 7#ávTYxht0/|jJ hLH~ჰH([Pw+ŢU;-HGH"jAmd0t%?6 pV5J( -25ƑoJiV&O5.eoG>`6pbSpE؍^;(u`{!gM3Ao]Lq/RWY@mDDtrZ?=ۋiD1_ēmڱѧZPw;ӓRtEQ$ kإcV,j]:,"!ێT$,Q:0[2X]AB!{X Oi@-e[VE?R5D3|+mzp+Ν}Kt BWCrRj W?;^Nn/ ?f -ZP5G{k Š0 "݊[ --v %R$ /Ӵa8E臖NAEe D6}6y rG,InJJ 5_ ѠpJh\GP -CN %w&jRjjz&x 7.J(8^ /&)ef̒H -/pb3fenhU -#3V>kx5A4Î55(KgŌB-SH:մ)&e%S.gm^Ɋn*Sݠ<] ubq͍\+^,C07lmc0u''PgDt߇؏Ђp@Nk -БuzkЗ=PS~- #@$x8:Ug(%yQYTJTpeG!uEMNrtF_UT@2ӛXIRhR5w uY8`F䊨d}3ؘ"lWxg QCou& ]u -,(Z̷ϵH{f&kB&}0?%P-+lhڷB9pn:۶e^ #Zc]?Ԩ_/zj3 ݏ X[b (ZZAf [J)ynH5Po/mYqW ЉϳW k:![w_ФZp#ϬWt8uF5f]1{>HR5SOGtњG%FMFhY61tMtwY.0x[X7;~RHKxN>x% 5;V G @wCGLF`덂 쫣#՚NE(3LE;ٗX O,O[l-PIp{`y,JcM`@ۢNmhq<Ҵ\.THx"eMh]tulUpLRU dN'1FV%G"+E.9 &3͠&#}n'gkMK\4AYaFQQV}ҁȴ](Ze.m%tx3BC4`ާ3#5_aPY(/ ]7xj ڑBGcl8G럯_Fʄ2=_ Pfz XpzF>;~:5v(@ p|3b0]2-c(V#YC*[$ /+HF1ļ&mnH5o.`V$T꟢%If͒$5( Hjþ3{TfoHhYdo ^ArPS_Lᅒ]9{^DTcKpTVk  W ^` F%D['C(=FNݲ'\Dcgnf.e< -ݵ l 8/ #&^7o*9vT i n/7$pn4^A+ -8| -$y\$\x- `A*d2 -d{~1h"Rl~?BC̝Q"Gu`'kc2*cΊD4Wm7&Y2 VT:r*O@AW%TY[F l)Fp67K$4`-4`i6t.F-9 #j#so6qgNěL{І:tBmR?d`ȩ0 -n\5 6!tլBBs.Cv`><533Flw7+,C`lXYa'CFoy^ٛ! 2;\o ʜbP&L[/:x`@ad f\gm5R6 %P4Ko%X=lnY!N"}qhS8d رp[/9u;{DT` "*уqǷ,*Ol;G -;h:X"H!(1 E*=[-J-c˸l)hK;*ʯRp[FDa.ЬR&2dtyQ{g JյSW ЛŧK5.$0 L~mN:)%͎PF&r4k,9pO1,* RDR_['Xlr@n+ msL;e +!JBR%;z9 -L*] "Ӌjk6uBy͐Y&@erY_`vEr.?"K -cz{rdzavŸH ;p$.c쟆4$ޤoU4,aDbD/E\( O\׾u/52("w}=Yϛһ"N"&+sB5~E!7:'P׬ Mj2=ue ~?f`Gr+woOu -XZ"ݸ;Ȁ|Ot[z˲a -d=fDw[&& 7"⃵i-Z \-2E ^MU|H\R^vo|k˦4y8)ț 9;MIRTQo<PSyP@0axq㟂BQ\Dfy˭W.i*!Sp|gaۄ6~%3>cB&^.@6ϩf!goTKޗ2Q&D#@i6E*+eXf!M_K8@Ia|+֭B`y - ZK4[+ -Dt7UE~r}9/F{؍w`| xdD6Gr)I!NZ74al[}Ww._~ܜ^_ -gȿWL~3ތ/k+z{moQ A鱺2bډ[|;˗\/yOcb'{E5O[N6*L̕ n0yؗm0NLT"-W%1v^ bz ,xTO2Bt6JtdޤE_@מ6% 73.0i*JήaLJ^0>UYX -I --/rRx b xMvFLX&a(sQtcϲo-BFX&gƑБ(&mD(g,0=VxXjٶ(!TevjW/Y ;`0dSA a}J؈Mu1=T.}jgad$31g[mR -.bš?2?/Sgf"1a ţMT53Ի( ,T*A̘MQg"H]deFd qS$ P1wg%3o阱V@厡M5WҜ2V쵆Ș%B+n-Iw`)6ЙdYR9V/dꈘ(,D.[<=`_$kvrrԤ3v)bA -&oeXGDY$A/B[s]GKbQ?n&@h-{M,3Sg"7ϑ 39V< -]*M &)"I!KTA1FLҝ5u8YU SFsK4 Wp\:#Œ1}㥼T&b7F^bb-.sFIl9Ȅ8BEY>=*险I6\q™Qfp7>$m`\!L$OT0恜/AFC֫|f -O-xtn\60?TQ rdYMW!vIyNtN:]k -bq HL5; J zbמXT+ض7gjKj oc$tA;IF@z81Q̪cՓShK}K\˭IY0/AҤAlRQSȔW-s_,eŮQB]YCOMJpK3Vy &%5e'Cg*$阜&48,[8^F+l sޘW -ATWX*)/c?^Z2ǼO;˒VK`f*9(0?KJ\)JiIa0R'1F"G8@.cL04]T5HFE喻i.a 0X0 Qaeѧa #IrTmK5D$B>N-IX>l(ŜdLxu1HoLl"AcDYdJa Z+Afߓ35 }D\OVր1"24A 1ȴdU[9rAL -Ǣ1h=EAlN$h̏ CW oK%'~1i+O;2PW(1LAR "T@A"kyc\>jx$tR_2?)Ea QE^5!q=64(0pk17dSس)( ĂɆnWqqNg(Y9t}-BS>6UGlOFYLE@'g -1 CXTֈi'xp#g{6V$u\`&a2]LQCQI_ pPL3x)dž~,_Ǡ1RBVrrƘk$WmBf+6 H!_cg"L<fWjsF x2Vc^UF 8L%*t"IvA)d$t1&n6pY2k -X (5N$,K &ǠE/rSA,phͅBtQz%5B=p9+2\e^$w.)gB.4qlq(M8@Ld#Gܿ  Nm>+-8ǰ^c"!h~6Q2°z:9w]!z-8c -RޠćTk)vmU%{>j#*F5ӏ+i=gWc!uQ"'p#E:'YsٙBXY8 rN`˷$- )'LL/tu˛˯ <w/?}xޯOWNo;?./ǿP.Яeo~|.on~w?z7_9?<9 >o_]^ ˳Af98<و ^>kzlq1oo0U=/_;S#Qݙ^V5oO7;;.>߬j/?ܑ/.z}vJx=rU2G{7LWoONp}7g's+y94foNϾwZp=roSߞѽoue~?6ȫ\CͿ}9¯j&4bӌWOOn^\|5zqMgxk>?*-ӯ?Y{x(:{.^Zv7eg=3v2v0vͽ?y1d})X{l7^մ67~{1YC' c_}+ƋON$lWSub^m)8l"ox?c?˛#5V'urnU&A^T6Nw*au?Oxz?yӻ{uʷ (>8:z3CH~.O/[6f}/._ĝI{oOӉ|gZF=&^O@08?( n8YYt~F.ܝзQfmU3aߙ}7f]귛;DD0\+kޝ<7|޲ዳF>Xtx7>zNTZYDmJʉ:+͇46ˋzqa0/eW}su~5^Q2lybOdׯ;U$=?LԼ>~u9p]@OG`'֞ pa.~ O&'ui͹Yds׵-ޙ<uW_:yZuikוsue#.uz*^K={[0H*d'tNs3.T_? y]sy[_^~z~zrGoV5I }$InNW<c Lb={xh]cu Ǩڗp_?Ycw?1ǗgL޾YJF{ON\yXla_kVܞPNx3;?<=^W]{ UMmNa޿9wo#_&p9JY{ѬkF:;ёHO'fs̟x+7XO}nO~}u,9d'ss,4*ŗZZK+1a-k_8ж.)h;Y׆aV-Ii.K9YWnרtgx1=]٧Ü+HMt0k;(HY=.f].fK({xi].^+PA׵/Ow0C^aea]`~T?h.-܅5u<>iY~h~eSz6 53|yuub}>;G];0gջAxZrMJ}ooX׊`hwm'"ʪ&IHoJں@NCɳ<{l] -ٺʳg|@] /ֵ:ccSWk;6Zyl> $ӋA퐴`VO;Zޅg? nn_tg6lva +ᆉ"ɜ5 9pz|{ӯ.V\L[z?깺-zBus(yyuuUL=?YLx`}"Z'1Q !U$曋ӛqܣ%j% 8osVаL供.掼'fyOpxQ][{եE9?~ʖV%9N'O(,wO83=(}>Q !BBۦ !N.@.-څoC՛קG53livfΌ{nf>!yO?W}vu~zϮ/^׾컝}ϞcO'eaU3y?ՍyysإS1Hۺܑ6~z>kV?ulWbjmvu6kܞ-&믆2zZ nO!`Us;;0˫K˞e6̀!V¤ޭ?HAӓa_N;Ƽv~wR/ iwv~iwZruo+]6ΉskY^}2Oߑ?f+s?mb,Yiݜţkǧv#KnU!>@]t埗gu>P8?.(>U5c|lXe -=;c]#3V6-vƧww.<\ÝZÝZkp~:ܩu֟{?= -ÜzwF\5;a| 7.)?U=\4H\Z}isk\}g"oD̛̼Y)ڃuWW?쐎V4ۙf;諚4[3l.mUyiLi3v;΄~5og_V6%::y]y{w&V7 !NuqvN;57秗'?vGx,G߬jᡓ%NlVmeBC%pg6<׆;uIԽUqNi{ZwWaǛ>gGlNb>mg x^gXf\?Uڐ'g7\ ђYeI[6ҷ=JXwces|#^DDޓ9։ݹݝmsmLseïΕ~ul"?۫sפq3Zw_ѿLg++{n>>o}:!Ӊ9LmmȠ,gcY<&[|*DzZ$_YG(|R5߮/_O˓$?vsmN6<}兿5 .ÎVeuk^;>['g,>"qz];H=t"ngj?sj YP.óW,*5oW*SD?fa+Ӎv7+kEL/tu1#f9ӯ.I_^s{o?n{Fa̰_=H??_C5Ð{Zia ^CoէX~9qk'{0kF)Gx>wZvg/a9ҡscC?~?a=|ُ=D9.02T{Mk0^=̱a)h9}xqSxbm1u11Adb/ܘX|ir﫽_|1!BBx񛓓7/ 6|1rTZ{nq/%Z)c0 mW.ca.!ٌG6.Ͱ V8a`zKiƈR0X͙ʏA<GDZAk.l[%؃-t{6m(Z:GyN.&Zbw@,՝e(B* qp!Qb 4q|tdBcLc;{-9b1qNt(c>k ıfG{[Xh69qY7ysa6ncqAˇ0RLSRM4;T[{} _dc;Ơc.OCd- 98r8qV9˟hK$&-xxʥ2x/-av=4~$Nh\O2 |-B]S32v -7b('7m% rH  y]!<;ul.s[b,lf)ӽ(\hE*3߁p0;Z!I*!%GaLוqI݄CP:ZyUqH#~f=.^0k_X=Jcϭrt3c4sGn\q3DV&Ik|a ){܈mus"0Hc!*R"dpP - -rBh Kl&gm㿏|Il&;]TkaM0mvt | ic'hg&"Ahtޒ`JJ Y^\ ZkucIveH¿$h33+e Ak>uT^8U.G"pʞezƆnjϬ!s~׉KdV+o\&u}Z &R4w+D E73C -O/de|K}/˔kY`k#&4 I $ -$lY>]|Y5ݔuz-ѱfx'Wdh8AuM{b݁8L?No8D_9ʑ퉡B |iă;:th],xǪvГZID -tDccCIPqqh6\- e3mnn6)$y3I+~hwDύeOCJtPd5imDc5)In_*ƔB"̎,UhXe>':~0d0 -aN+$ڷkςZ%p <)üi=߄'DqrqxYXHsbdCSjyW$u^t'gŀT VR'@- u{Xp{|L&~XC^T(%eF{ <6xl=FG.W'eC NʤLd|4qwh\{z&TC՞K2$D?1La'ҀItLgFG=>(dҊtk(zst| Tv%rV+~nδY>Q$?*{xﭪ _MG=oOܢ-hӦiTRgoN}%ZWenKٶO1ظ}c {CKk&1 9GXbu(3~nzu J/rgw\(E"y89~uvzyCfg7P5Gy6 44= 8]k32_\|1v'W{i/oo|onmFF~ ^H[!$JӰDY(*rBᄄF;:rf\٫_)h]N$H!/"C:6.J[qf;-xCC!@}x4 ht)@& S_Hth;?S8dFG. S!5p y0QL` *seou,LDY" 15 ?u]Ye&oRe?}tH4%IcɩL`  -G~7y#Kq -yLs!a88+ȕ8mSzr6RS#:%36W/(cyP֘teo}n%#g(42pRuX4D',\1>ڰq~GL5X3Tdztm{ `>l %3 ѥ؅JpG#l7Fy*t9Uym$F .NK>'Mw|y7$WxX )-ixG`a k{0q.E^ - XwahÂ(E\8d4@ -Ddvaq{kzE32d-JO`.^pw|*H_xc/y?:rHZpp'o >}{S)1э/ǖ.ѥ".N7qT -C}xA\os[̥0ZIMtwFMnvз{M鎐u $Brl[nM"_ (2MSx;gpA DdjI0TL>&n X#Ow FO9PZ8S \_O9RRQ8:ԻTC<2d2sDgW~;vn6_=jHvdlIb'PJԋ/KCpECh,'>0LHmսQs Ty8y_ux8ioQxzT;6m.zx.hȔKB?JXѱ])y锪ͧ_?>OG3;go9d7XLl>|wu}3Cxquu~KÛWϯƀ?vcABD_?$§,)!8(\BO *+\ҤǯJ:ԋ5RٳdΔb2R vIzвgM3x(ETe}'3>V"-\ R:F/sK$7hO$5*eeUaECTO\NaOa1Rgd-qy)Cv6-b ؕ@I#/FwU$яX$'AHTR(9^hGðh( 1+d˙> Q Ia%ԡ>޽+t(REא >89EDnGz %^V2Sdk#)PHSQU ڴ*UEىJl%0QZ5F ߌ{%~$dhZD`mxOc3܃oJb|N\' a*h !;DDTz)F0yt'E݃rcE iusxnSH\>%赍GY*}'JxLF7rB$J18JD3ɕDiu{hŵK`l'Kk:q1k S%peRGYnRȍ 0=s%2nQ: -1*3"GMYB(q"$t,%uoA;g_ Rc!SF$Y@qc+pbbB A`6?& G^,kL$GCI$}_IٔP2LpX -E?9Zi(7>QJ 28O[4U\K!y( -IQ8q=e={T;21a̤1MˏTRe"RSZ*2@7tn*=Co;0Jm|A`3voNNǓ<-4VNɣ:%(tj'{ sI,_"jJԪ -ԛ*E\|ӵ1"usR H -B8Tz17J7C1e*2$CBA爀K@ജ| O685SPO-LJ$ΙdD 9}%V74WXh -eQf8c,ts&fW!IeIw;)$6u.%3]Ӄ~3e{2GOIE2o $"u?ʎI?ÜHHcc9%:eWx'0 "TmYYPScRaW&Qd<~9Ĉ dƒYdlw>Q>A bl -CҲXMΒtT>Z(˒ 쭓?X4JNK -YPYL*bNr⬗Q'+ВMp2]iV@Fbg2ʂ!Op)!aqj'AT ~2J [rx)%o\^Dt )c2 "R D-U5lj QP*y:69K#W*)J"Gs8CĖ7l/~K=޷j 8LEEx$A x%!BR$x'Pb`=v1%ӞX9Mgglt5)<1(2']yE~V&%hL@hdngLId^x޻ry,9dh82,Y뺟^$7OMCG{sitYwr"'Wd|A&- 9)PdGD|lGTyfv% /j~Nx nH> -FF0'Ė fzIx+U#c4DC=TbC"Dr?d6ww?qӧB͆&%ufNRgq/`1~{&WW,e;ldFCBu!/p8RN >EohvEq'ZZ <3Sm"IZ]]!sJ7噦L_Q';avn5IAA;+P}"8gZ98@xeVI"}=}=c#Hx~fkL:ύZ`Y+zLvԸk<d1AG]g=H448Rؑw1]7jH-&GY 7OV --Φҡh=YwryY5TVa4]\8Ν XIq[zsAvtW6 WTM<"InD v1ݳg.6uy ->2#5d[vDr])`Mdi<춨 -wnޥIz -鞬$315RJnl -9] <vyv[b]/Fy&ܫ(+(zHSΡC&,ckP`9"{7Y4|Rs -Γu*1SxyPy Ev䶸.2q֔#U:Hp>&"GxK]Š7k׏%LRBe0gAt ;lKHhHT7Sw%su RhFNH9GN>أEKnY1T5vp%mV}VfEpԞp~"ߑith2bTO4&FxVǚg2˛X!3et v.w/ݬ7%Cjmuc7ę3N5K ʯi.]o6N[ABߍ;r@(MP{Xr,.N/pMBaPVE^cڻ6R6(Vz ҙ$~Dl:'L%̏vOPrmQ9 18#c3K,CX ;U*&4xB`PP>`bG8E -T榢 U_8̒|sx],НrJssR&'jբH{>!(XUZ a"=UsdNӸ*7Ĕ4s/k|$ !;!-⤛uFE6eFX:| 1mZo4(fy5rxځrtautwͷZykIy_1jiFsnya+'nzԀ2["Z7岭B;"W+I C#y?"dn ],T;Gxq\Dx|/zDq8I ]P>!#C!5{"9D+䙏&*v;jdiG^؈ Ff<\gYC0e+,>_"";ZC^k`@@ {B+qd2EݘػpubxBK&6q*|j^BݾUgnm`sQtL2V?dmbEVy$(Z'`?ڦP0I$'K`lT79a{"wo&y*ԚL{ۥ{hCY )TPnBTgz5>?͓3s"wȠU$ԶYg9O; -+{]bG'/zJ[&щ83+M* S.|B9x{^ -CGI6OHoH`8Gan8. -.Wk9L)//|؜n;2A\O%U 7TrH%VA) %ڸ]r[Z+E U</VQwhف #P& fC)n(qkD -) xt2' -[Alkwll$oj8.P7wI;Su#QYuQ9 #`W/SiXЪ ՊK7V6\|6(' P]A\#/gz(jUef*840QsEZk1ʈ@]R0=r3!M呶g=LL//abua۸_7p?Ci)RЎ_j"{A1"{]yTv0j - +W8[&܆wf MܽFB"2O *f`yk -u7*w#/~em: ^Rv|}iF0%Fc 8]GQ9'Oc&'.B=XGoة#Mz t93_5_K(#j09؂rkjdV: WqHzhJsr9{( }Ji"^o4Dvuݯ){&ڟ9@nt O>Mݱ@C Y&dĢP)oWXNɬRpWzHZM6 < $y+$OTS3ȈVh>>>կTq圈.CQNd»M20:۲:Eáo8ORDo ce}҈qU$¹{^$zy 91d"<ɥH}+: ?DV\9WYkM<[.Eå#X`ь̓dXl&-,ND4wylG`Ynޛ -\W -roMA(&aTOD8h~>ʖYt }iw8BH;S3Xƃ]'[))~amֽC"|\6r `-m谎G=e3!(~Q3sNm؟)Kq̫AX=g0rGYn1:*pQQJWGOO5w|orV_g#|Ryv uSpx }i3APDr(ڷhZSW-B]b[ZzkYM^f8YAbmbz̚X &b †5mMx'dk vhgT%d)2#C>r -#4Ml(vb?vy 5f]P9"+,ذڑG Pb>n9`٣KV 8Ţ k;({6{ǧ>#Vd#9M -zՊU(TRj+]}BY{Vظzlup-N>W.(֍ܚ6c-@zO -DX?>vvp̩-:wl"n/" 5.A7piD4Lz-7hnYʯY.|A> -+!32nN2hg!OHusdTbH7R`VRZ{{x5lRe3oGE]s޻ih\&=E@,e9G !n 3`2L`R( =%CO X -0T\#5%Jy4}HKJ BԠAa M< CK`;sd}թHW΃3 Ym?R@Yap\@|~lWXCvSa!:woӐ!RoAq!٘H\'Ա^Ba~ۨShmfڠUچve4kU={)U%UT\O??woo~Jǟ??{[ )=o0ʋo^ܙVo^}~w_%D-"t˺EʒsE[LEqSp#a*h"QdfbaҊhx -N`njleQKk@=}kL(.Iϭ&=MO#|u/53g?[׵JYIgJk3d5i#2yԺb %Xfa<)% -M<K_'$X'gۛvApQҸ7kqSD7"yz7q|g}bٮQe:|/?G$_JǷJ[T8 LA֤q`dL{-,aŪ;̈́m}Dp_d)BAEI9f +mx@Y-HnIH@fPmOqf sW$sl; 3_t #on-! >'?hGy!BtEaUy~~q@ƯVrެ[J5/ tAEd%$sWd>A8PnyHȐkRf#>Ռ[U\S -*GhJUB-g8ir+_E&;1xRGtCOtq^#8 O#B/ -wJ%( Ik;bM*$Z})Ԃa!#2WsÝ v Wo5NnߑZ7fuJ1wwVX:pQKi&%?%A3 L&J4&T۴JgHQS]Sq[rGQ~ᱲs[9vɎffFjƪҸkFPf024-VX<*x'A㺾V@1slkjդMpɒg -L{,&$ zta2Ҵ=H)j*gnHo~ -RVy>-t{hBšrs^pv~ap -' > :G-Ð% .J&+{^5 جd*Ïf[6FcX].gGKVR.{k@+HvRErGT,jCbDi.JS>LN*FWa|HF}r^iBәxGA x}jcYZBX*%v(1-VwedV -L&+F;]&&5߯#N"8\>|vE`?g!`ky" -\je%y, k@qEAjT(!6$Ra'C%HbIܱʛ 9՞i QKާZV* * g¥noEQہӴHC\p׀kq/9DBK9½rʆG=`7*0)w~ehpcUđ2$Hwn]0-ڣѥ?sǚbN79xn B$72&"rY3H'8(7;B((Z=\‹.̧yE}Ϧ̥[(4IjwH=4=a#9؉W> -3t%8ҏ4(z "^.| :M9G)ߓ5V{A ;DP0Wr 3V̻[s(Q#ssd %w Y3lS#u<՘ĕݏwRr:5|i[8cI0{ tqُv%Z!">qB|# 5A6 -H' h@ʄ1S8i[ mʺOJ7[}?^!@a]<apy6+@MP4m #Vw|,&E[Z,G^N endstream endobj 41 0 obj <>stream -vuLops[*ik]W N9QK5׏~Z˖d厩qՋvWm2?5Έ z\a3*+GЂ8<0A.>_4z.T߉|u3fIW)?&Ύ!H1 x&YU%Sphf_լ._R9IľAk)&p.+Twi| f>/J4"R‒}6@Zf.~Hqۓ04{=~`{b@ސx8*3ˑ#vSٻ9/jХ fk >tK -"A|X ,"<QpȳX~+.1;PjD/|T>Gٲs EPQ߳q=록\b,,Y 8p_NyVdtQ+RR~G׫Ɏ6剗_fTչk:(nY|N8 PddLNgBOQ'WV!EV|Ol$_j5b-KGg1'u.xȺm#Yy݅w+٤* g@eh/Rwu#~iX4-ۮuYXR]3>gN¤|+C @1DqbFk}d*(Ύ0@ٙ -B'vmWtIC{G)o'*O$ψj3C(RS6ߪgY퐭/J4gD¯]v;#SgCegYg Ȼ%l/[C3lu>J.Ѧҟ`MIТE'1K3i{M9!aKD`A7 U>s0%pMS8~ 6Ip Ž q tLyN(F3s9=?c3L*]|JeF -ݰd|A, 駽keU,ypTU{E'Y+ --$^?D53^EgRPzdn ѣlqFiae|m]tqX1,phBf/PY֌hഴŚej){BLyM -<y .(SJO #YU@Ē *wPBvOz¬Cj BbJKU>8%g!եq -o\rȇSXST1EMY!OuEPls\`/ҘFе`W=+r޳vʹ .N$X}L7Yurkg'?1KQwK9f-p,c(J[+z -%nEO`1S$TF+n;ErLS( x'aʀQyW#L!fG0N, չʈc|EYy ^qhx$s>:S,zZ8;k0kg)/q>IMqםob5Bs[؋Z`q$2/6x>/?g~#u f*zn NwZmmIZ@[l_DؚƁ3D?nm_gS)cFЛz-JFY]>@{8ȕcD -5?Ds=L -0K(։؀|NI۳cVK{}_9->om;ۉ - -HALˆ\B8TB rJ9ڠmj9u2 -:?hZjw }a_g!bavڇbNrZ "gM#ڦ^ѩRt A(ѝ6rE7i1b$F/*R%I"@^0 Pmn$:xZ? OdG쫟\Nq5i3[bEB*UJ#H"B%,Kv̄Xw.KC08gw*C3JHpBi =riJLħ:S~ -}OS%h )oFJ9QG&/{q0>)xF<_*~WaλB(HxiU+QyMv;$Ei0*BFRpG>%t ]GX/HDHx%ȊeBT ~j56S\$g?7Bu_/upPA:0:8W__)#{߼z^Ñi2L~A}+eX^I yy`5LvvP+yҷ-i} 8`YKuyHEЧ>ea"CW>AJ VB"x>9yxF 8omzĉLkHi|ǚj31$ˊ~%Ί8dݜn,);K(n"1;(3E  想eW~OaeYi5+i~eRM[c_M[x阱4]b׎\l"@uU$F -PCMLȖ k< -PdP#m:2+(Ȋ.#}E[)#́o!JK"O|jA^W #1w~ϯ)-m&A_ibΕjƗLͻ#NL#Fv^kXz^߰ow%ۜvoO|bU>!@ -B+6sTSkE&%]Td`yv<҉j3Vzzߠ踂ew;!''Q -_lEx=8X{<_T>hګ\1XJg ! 0@2Q!6]e L7 -> iRv,jZMWvdUms TS&Dk/BDb>f{މK,[bLwQݜUC!7 0F*ΤU1fě0ݒh 6ǁgDer vE -ٮj);,"wIп: GYܺ8RA:)5W߳3%o'!Kcd]%ވj :9Ϳ㾥H Mٵ5뢤}GN - -"\x3[3n71A~Z=UM`m1䣎74BSdS|kSLS>1ꁫweA]| NՎ?[M bIM'ōs~rHڼV?6p)N* ʤp_=҅$W@)}c)BX3$T\{;WjIe jL4ITDYKڨ@SB{AĽ2iTydIgzfgHZ,QfM@[$Kq"bڂ*VbdVSp&*]݅rnCyՕ+w.>P`8tzˬ2iZ"ɠ4/!NW5#ljMw -=+"pѣi3,QY# AvCKg9Q/R8[:gx-jp+=etxq0_zycwkWy_i$ߺgYv﯍r7,Ua7oxɔpJ]z -pQǣBV~9߉E7jdpͥU1N$GΘ+~G³i?{\( q/=lvnCt=, g U'wuR`xF'Q8C%Uq<' =D] {l!PAS%TP . `'u=,m]]|y)5L,fu++;P?ڴp`/7}E.[v=ܑ]a5%`gEn!8G'{hq˞r!2J^=R 36`(_~Jt˨#6-newuQsvɹդch-@sV߀yl3~Y&_ژ&S#@]:R~dBd8]FջNU~xWM\יV/>r.gz@8}R@W)|=TBܕ(fad/σ\W܍ل>fxj"o$ $JaVJ~rH-Iݑ;_jIů:3|Vn }FCSCKy9򞮲"&9fi׺ϗ -.S_'k u(.+b G`CgB9Y.kca#dnUκA$3*9< {>sxӯs=<n$`K((z nݹ=EPBÛR/3sVNqf -yc,Y\MW K嬧ӨJdW]~[WΜN\7$Q6ʳFq*B Kk}MkІ*Ⱥ^dcOzy$JޏP4y=0%@~}_/ɴi[?")qV(VU\Kx~\3%[GQ(Ȧ\TJ4ZP-WZ7?hSlbm. > :xE1.x~%U%ϼzܛ'a -M>OQД'>QgdYtX|'n|\v"~gb, -gl=U5YOcrJG̃T4b0PRZ(7V1Mgz,U8%ȱhoUHg.ilgVjb)}tv z, Ek#y(bo7?$Sܿ3AEkR(--P1bmż)+Z7_IfYt>?bs-utw#+W09wV@??,WZ(6''ZAqa3.+.e~Gj{;Z(76VxGz{EH\vQEإ*X9ۧ+*4QZ!Jt!!۸{%rlU#N>ϼìŮq}X"7d_z"4Brhth׬ ALE(R㛧N533F }$Nݢv[ޣ\qA5.>-0QIZSk ?S )KG4s 2.Hͫ~]r/a') Z:TPHWr1k4nG=-guKuG^HVC$d[ļI0HRGuA3ի# Xї7=K]Æ |,<%:XBߪlr 6WNw -L5nyWbM6RPmE ''֥S׈FN*2MgI̳ĎtiP'NG >#*r 9 諡1[NDRU6JDO @JNEgc5k= 6$QYQ9N贡PzrF*Co4l{~;C*1tMnS4,c!I1 ףvL[a(MyGYK5*>5v7~隬_B?52(d;1Mñ@9Koy2=MaVt)PFG ,aA w+UKS\ -Pﱡ{^.N9qC,Q(_ ߬u~3VoSbcݩsⵄ@OPNDOWԪ&yMEWbCR*dqV1BKS.[&!dcprrϴEۣt|37B& -7'e^`@,Ȱ 1VCt/hz} ɓJISCُQJ.f)[#)'@f`VWD jÚ;.Ƚh9(=QzW*EЎ)+^.FX<oх 3Ene@_xX#Uq>yn'n4W s?X@/ ƴ D+iPNgv5yF<2̈s x,;{+.X.2خ0='t,)ēщ΂AdܡӓS6יRN}ī_ެ4w4 Js7{vŽ{ c٤oáCBs*]J| -sWj/;Ԥ^.:9L?u%GuM~=gЈ_&F"? ]+~#cf dXJX.?'->!j_N R'f`Bʲ>g8C49- 9**?fsi>~r]Q]s7f3ʇED ed e_BY(] -I]"J(T(8ufW?믚 Ebzڷ!n>S[JϚEmh#iZ }}WՈ_ڥg4J40Em>Vq .+ +|H G)c #2(ڤ1۵0GX?ޛм-~uf03̐GM=HC(Ma~pz{Y@134[nןW^V8enrn6>YCg@;L#Vb jC!# ~Fm^Pi^Rd>e5J1D'ā,K7>BoEYsUXywU/5wd [WIޙBrγMa\8o/}俿~2)6/jvY^ |G?g 4aܛ% eEU族6&gHʹjx$uF7 G5B?zlɚ%Bul/Ra|rjJ3YQeg2J_'āyrfDTEWt24H&_5@q֡?DZr]Rra<WGރ!t 2fv5 DWˑ'ÙTiǹPx:_2zB}eAÇ"l^GQO?;iANG-ilmgQĻlMl'뎂x\{!_ Dϒ߼˩0*uv+8ŷ׀] ^% Qbj)f`}u~ٙV#Ln,FNY` ^~ó7Fv/wX2;w&"90A*ϗz*Pf^q}˩1v6 LLP;s-\s%e02}ڙg)Zw=m ]oGzqJ["u0cE 1(=/izD=s75NQ5(.-ND*K_5=C Q]i-WLChE3 _zYUS-Ѧlue9Pw0IΨ~~7#5;_MoI@y_`\tz#SK/yʚ,C`s(ipIEvM `J3ԉ3ׅ"sI@+59 H˫BBquY[lZe3`zN1"aE>u4ԷR95g;cq>8rtht It;1GY-Ov\sWQm6=mҙ@hsA;yMoFMuҜ`܂. <9w3qx: )F;n#VA g.1DevXn! =V,(Y'Pّ/aLjzX8<;뙁]ѹC؏h%M#;xt3iuϪs%; Hhe^G1ˌSFL[`]N]< VF> qGުBG_i㘁7A/{YkRsD9IMdښ7%+ /FN}z(o-Wr ]>Dq< qw^4 #YdcMbcyow{r%F}LgʀNWnYO@;1$SMM0>V9+RsEpF␋%s]=\;cX3`Gw8r۲bk?N㘔e7ʻX0jwV} OFyK?SgWR:wgz,^`|n$_]O>Is ׷I,nAQ*T);vhAyNޑ4nDǼV20Pu2Kla!HG=zRi~~R " Z'S75f]2{,D8+CC IRq7a"3e>jsXpp༡H!JKcS˃;OHV۟7}ӿȳHJ9jzOL H;v@ ׬ ;+CnZ]f̪Wavh~^ߟus]7k7EDk=՟t.591=)@w̝v# 541XbN5q ܩT*J,m>+KQW8Tռ뫚t%q{I)cCɼ>p=F"Rxʯli͌_2ymmi'Y9SJN#ccKia?K]F1j6:= Wѓ8BH(\ą!oh{ -3r~^jʗ5V]wTq՝֑+nW=hV#"YK 1snw![n-JfY% -)w \zLTpqtjV۽ayEw3Dbػ8pz2tuhhv zE)yCsȕ}i'5&U/bD/❷Sn=ǜ -#zڡm>UR$zh'-)T16b/7{UoP&K"Od0 VgW^ʇEzxьfMw^c Y¨a$qqb(iHlH*tC>M=hK4]^ʦZr3.<=wA«*#Q=]~YgZu(s{VvGq8mϒ0)$d~&#%Kby )!Oik+*ڪ=#"!v6fVKR"%BBd7廌5`Fk$DnƦjS@V;[2o^ ?YWEIMս)vUcjU0K F%ƑY#޳_=327M5*`Qs"}0QEN-Kssx6Wh)tş)M_c̎Q+|+hw+- \3jDR=ʚo 28"Z*\;o֐T3b}8~g̢@Tz_ː?hG:h9p3jij5hsFF3?ez۹G+}5K *7 -Ud<׫ߧf1Rl(hM)5W~9Rse+R5Kp''t> ߇aywAh׿G3~*3]A=0sz)!>_ɋ:̒ -ʗ}~,fԩ"PuŜy&#l7)응Enc豧{2 _q!;FeCmE:ũ#'m2[|1,$[]>t隷+ #%: -v Fl 3Yz̧FkE,]]r90:nbD+tߒ.xgL ./)^ʎ*zc`MY g4=`8eJ/'³qPbD{_w sQzv A:b37%} xB9!ˣ0fkT>bpJ6>YLxlEUXĦJ^)mrf緧z.l@Ilnܰ*IƷSkd7k3/#E/p5qAGynpF -7)/ /\g༎sR6ܐUw8C6<+2,2?_n|xOKlrg[ IJnl7S4'E\L9A*JB6c'z﷦0n7Zn5Uf։$y {RGa2M޾i4/@KNZ(8#).#P}JgD-=:<1ݒ<"pEMR=ԳYbF>Ψ6%dKFm0iA=3B%c ."0f#% -VM["]RwM a{%bW^P'{<.1³}^E02èhoxۃ"|~"[j%r@$eYg>r (Xaa eΤ7nWmzh!s "2ŋN䰿pEV8ن YJ(s8.~P]&@p6y$!L05H[ҋ ܾi;3,ԃxF21Zp[o_#%Z*Wx<&$SLĈ#QН8H:g|95͜ଙNnFvIe`0I_K+ƽ:'+F)9DŽxL}zdr+]aZlя;4wde;E ϚZ[)du۴v.R#cWqkh ϶W'+/=㽊J0Q;T ks&ݑVV -ԏL^cj0?d88f?ߖ2G<>8u$n']'\{1J;+X4&-K3 0nbpc [C?|p#b_[٢.hI8<.H"9H1\e˜,TI齢?}g,n>!XtB_Ogꏾ GGt) -#G=jۼ 9~ɮ8B= oDexa;cG{d B}z',uGs|XwtZ(y\TmX*k~PTN -ӫ) Nz>!1!*Pk_~|N D -`O -k(CJ罨YZrU.'-_B@tv=޻$Mnci+@LayC)36Ee[ *r~N'q9R9&SUJZ@J"VxNx*b_ٓg+[.IvUϚ.˾Ik`_q~}A^VaQiݨL=}n<dVcYP1RB9S 1]3Can"#2 ШJWGgE{P C;uwflC]۵B#\ȎKk2YeH;'-UpnSr3a?ٺ1# -G!Ϙ& Qw@l\?:Uٶ)(QN2A^׶5Ζ}O{Dᔉ z0E%8ȶpDI4"Ŧk3{/Un8jp[\e1,Kc? vo -`(;C ˍ9[(OZԥs]-ZܸUy{={)jYlDcjЀcsBh"T1bC(ӟq×18?u?i{1a*?~KAd>sCf]Άh2=<%}ƷGyQ"9488bje|ў)a'8֙&Ø;-3Yّ1-1r]V_6 Ց =\`߳;[ a<42r5L(N,3&!_,4|V7A>ϐh5̝zT,Cwg<~ ]{l̾G01XzxQP^E+FHߎ=).uP+aMG^fkHë$}X6aV)Nş~p̌\B^{Lhm8~#G'EhβPOEtOn<-'GG@zoޭ <ڕNs~{v5 B' yP̽P)!-+x]p?:ugP`sxj PW ,,Q+3k|t"ʀ%Vƅ*b = tҍ-:s/_0=Aبt R -qJ:zLjXds.xD]FcfG -4RZaFj4 PsFF\H6鑾8]s2CDCSP]bۅ~_Jf@N]q L29z _H?W8Rg3g).1 r^IDx;O!<s3%W/Rhũ_ jqGLp!ϝ`a9@fsf~3^E}rJ!f"!4K9 h -*L|Kd,1$wtS%H vR(e.g^ ][ \zUj; !-~&2{Nt݊; @Ma'aRwOz38lZ!awIOH'ϙYxNp580߈6RC飉f7x -}xf%,B{[]O^R(a0dEL‰B*.o6ْ5- -4j(C{雷.Q6Ez5: xD)YV "eHHY>K"b%COtc@߃; mofyD$wHQ3(}1MCaS̹tZ$|u(P{!|vW!+1aU"SQ@7-/>CJ`Gp)r? VEc"cgl9!JGAGI#s\ϲH#0p^& bgDzƐYInS{ot<ݞg9rG$f ^=T2'M&m|=U@+,Q>;6 nO#g{'w,%+ ّ¼<+AGۛקBx?C={|2~dt]:Gc#-lϬ%},1x:{w p\vܡKL!*~ߥ|׳gB/Ŋdq$ޙ%H!٩.&Gb^89+]Sܷ?bs?NՒϳҹqdەh^ч)E=W=ɕ3*et@-).{W j?p}ˤ|=k'c(Aӥ>c[yǙL~3ŮTgWJ]~$QF}aUg)gόѹh?{xyG='OhU?`d x -c̙y4WK Q\|}[g/Ѡ2%XSM{y}}#x4RQ,qe+8G )kH~S@~gA9t=+qFқg2xfZ E'ix;Y #/cGd(uR 9$?mtN-br+o T4x0cZ&.L$~8~]MI`RLٮy>+(`02&AYw%P9ņs 'ϴ!7Z5gHh^!,"KsBٱG<->`,`-J͒h'W#o@Jh'|1%I 1ZW$b*f׭&4ML|rv󼇈Jygnbuy/)Ќ\< p_9QAx%8! - O;jS½J2J)RV$a9t^qhY`E 3r3UGVK$̀+}GT4Cr#xYW@|[/4;htntxr6_Y6S*bzn4+lh-K(-9jhV0g~1Y~/3w*xGEnhy*G#6^LH]x'U+[eDYR|O:Z2)ujFE}~%G1 -(z6WVr9bs]v8a;l]0*w"$nqv n(\IQ1~e40XæN~c5]daT^;hϕT,w^0c͞)R& $h}h]M"`ģj9GyP8ƍ3(։ Kuix4.Tr@T3'ҙ -Ohd2wDn~Jq,>eEP嶠^z!SxĹr`Q<.)5.ȧ!<2'z1+Ǖy=#f'j{Д? =E3/FbC)Tz2,gKEak3eW8 5I~9DN+3{2k`pC;{"fp ;'BO>a>,V{}瓼>QijPNGoeA_s^{᝸|UW5!6ۃuE(eKGLQ烙1'#iە{*G)PK+Ff\sqD/҉sh ,=4Sb{/7 _J((EHM]UDG,iW{ԅ[q@G:PzO`F3=>] -/.KriX\8b"K}Ğ鐁z<| -HA0'f}ܤu{'X=h֗, Pvc:S;.eG~#-]Nz *z6ϨhݡfJJ}JKF44#bUp$ào@]3TR] !`E4v {,~DMP:Weq%;։mmWw5Wmjœs)쾣IewL0x-uW@txӇhm5Rۣ -v!f&l >+}TBW6={;U.2 Hdd)CڣL/~?AWoEʧR_R?o= 7O_Y%h@(gM=V. pR?^'oDa\1z)S7Z=]g>OP)8uPo;љZUv$75sQ% ccAӁ5j9nC^ yQP:a>wh{;- ^:4U~,3|سe -/ʊ O)iQUPf%=ј@=[]LCJǷMZNEEnav] Y+d)D?fpv|qmieuJ#AP3+% sjK {責nYP1TWĉfu^i\4)'B\BtҟX[NQCJ{|^W׆vQUBl94:p -h_aMĠypd۵ (۬M SE.L`hB*&:zGvBn}{SB/=IFLh2U"wsYĸ2 ?@[z̊yeU.`YCܫOS2szd|$6}`  s-(98TNՑӆ B]ʺcqV&Q#ֈ}?*h8ѣU]U'P#iN/O_DtU[QC4+gL-'͍zy_J%@4XݾP^4  TJp}?RW\O -x -:fjuUтguZnˠ˦d/*8DU"pȐ32 -i ,Ȝ[.OVN z{Lӎ 5<"%z `3/fx'&XI:תf׹ӅeVb*KQAL% -z~URgQ & 2MB|BZ,ZZDa v#;(ۂk:刮hظBsˣޑ5DQւ+ռȦ"ׁ(E7 -a*Fi}I$6(}-f';xp j9A< -5w.)(yбcnX(-Y-n#je(y JDCI\B'M[ObSt+4?V~f¿DX.qJX|L 5Rx@Lt a5FR(e#=T6S.lCa&oprMțn<4s%yrķӮe -#!g7HTXo-d4{Erz8SaOdݲrܴ&V2րmGp+5ʟZAgU:b<앺j'91׷_ym2 |WJX13l=^g1QO?:2h*xQh <#Ygh$xiܦ:(O!'H\#&Z4>=Bv#2a!̹u=oGH=U݈cz7e%!)pzf%{8-imvfPO_SN"M -=tEĵKe`x?:v;Z)p}|mЦsO:gskظ:0>@2v Qw[L 8C{7&d%w#z_tCfiZi(FA(ak\*uү0NxDkn8;1d?֡3V]zxMuۮf:)RgKt#c:m YKWRLp _ZK_Z%: ܣٿb*@b,z,3.d-Z"*㮉Vx! ΄z{$Bjڈu -;Gc~5[3BW_ ܇8#|mE{jw2!(mǶJ3q*(/Q;w˵{1x 1{x\|G5ƒBKQ( GfdK 0 87&aic dz>` -_Wntf_ * вK:wS`JHʄw?&_Uv~ c^x5 -5.2tkMk=(Rz\&O Zt T7ӊVV))w*#7'J:ao:'( 8%Hq -;+_.ߔ.lj(.*%E5B-'&ډuC^Mعڞ c-f2Kx%< yy<Lm\Ȳ$+^}" kЦ95MFM]hΦ`>X+LJ/SXj&\٥.mPdz %U&w? n Y~?)R(J @E-P7M|Vv;o{M_D%Y%O{1UAKXEMB&cV- rKE`GԎ,1p\iVO'gbގ2K|\_s - Ywq:zޚQ%^`7{~&a ^ >Z293Ju0Oݸ+SBzdJ) -D5۫Q)l7h*ɏko$)Anlge\ -@9 *@sg8_Ӫ2 'P?BxUpYq:"~k=Jr+4鿛]yc RHEzAI֖EQYUciRN*yh!0Uk -.qmJ!ªxYX\՞9IBwg QA P -If-i&]EbK]o)7Ti<7 ҟ-[j)[t'ҠKQ~)F-Om=Z R3v]q)*<}TMBȬ?F,FO4w6/8?ǢiF|Gy5O]_ۯ420[aK꓅9TJW6cmtK?`>Hv\k5Az\ICL)Hd*VU$۲ y8twhbk\pQŋd`970wmQ =xNmHo5+4V2R,s3LN -GՖzRۧKW+/ -C6ƚ4? -97+m>RħF Dtjf .iԗxɄZJH0"+A;#}stP#1qgJNqa -.r}mo%V~oGAC֣W]:XGݠ LDA*ڍR.Tg\̃&+u`efFȽ|X?2ؗck($yz/EW`fJkh}W4`uh9? -k -w=uS&(Tnu&8+I+3ZĿs)3ʯyq*0c(#ϭDy 7S#` ".hPy#84S!n-#zR|,,;ɫ4HfoftAVr>Ϝk>Dz gTpχlBM}R+yDڋU|ƽ_ڋT,Y77|Q#tV8K&,;v3k2FU1;ԈK`&;w0154xSd$V OʉHq+L/F|ԯ)gs2b,Ro`(XN͉1+u^o37;yfr)\ O l2er,Z~e)~C˒Bt%~-6r |@#d M8^0febLN~n4azD&'dy8APH3Z߷\x8KCr3%'Z /հmVr^=2f Tފ@=Da&`-LotQ~,PO#EՄMqoDhQwɥT奁3Z{C9F{aO(K*-(K-;& -1"^@Yn_%dEAޥ+SN/HQ=ىG{4zz6p0k؆!*Q?J,EI3ة.N * F[>V"8x}1JNJ u[::wnS}lBx :GdKUD18V4(JG`D1*oNh:[>>g*8r(%"\ՎU@W]wz]p3 UrgEEYqVzJ{$Fz;}U{ߦ*&xWjޗY-wzVELh(/N]JY|9$ĈF< S)]̰C~Dܟrgb"nM;9[ KojXɢUsY1Ex#]x$.?%%wI*_ -PJ -s4y"uȯ;Ԭ\$5̘H8]5ZVd f`k|(YHP}_ WEw-{ڷҥG=$EfFIxh^I.x2j2 sp _RXf>a{^#@(2^8rfw<;iLSs|G0ZTHOWF #&ĒL '~,2O(+-6u~ VUչ}ڰԳ0MTQŕWդR@z4M]=p\5jbޕmdy=~_~=ߓI&"<vZ uu?3zh?1!Rwq"ct8@rIJ >=;(!wTpBjd),Gt/ x̤z5'$;9d 5f -n;q K}rq6[4c_Hzr)F- -*h"̸|dTD?kF8Թtu:aɇ4)a'jŚgDX DRWR/P'c\%ued#}j5Rbyv`E荵i.ۅTkr, ^ՐbS wLa1'y* v Ar#h$X |Gm[waP˻G07De$Mpzd8HGKLpnIMp$dl^+jۉrdXKSseTzI3^&*uoJ*i\M5gzK4 v1a\hU61TyzlEvw^8zktVy-{!Hv]R}u=nՠt|W~Gh>_}ts )p)50]@ږN/ɵ!<#MTOc<(ƳůjojDvM_{󘔨HH} fl 臲gy(H5]l!@1VWіg711OtfH烟YϛiėF00i Pkr{_a?kTkW}8oƤ>3C !yr' h~{ Bc@+;Zu;Ghv#ÀVCJH{=8k&G7F,KD|@b/7`7G\VwۑzЄW R -jՏ@Jԣ<g{7㺌_ԒL +^+T'[ӹ1)a?Vigئ[簖őΜ~pȎ`Rw{JnƚJXɒTd6&yXG -RzCyu=}s'3#َ(K1 ]1bĵdGɠX>HUYcr\{C0AGG) ʟ*Up}EEߞ% -xUR\R:VHD W\}١&4αS9$²LLE=yQ2( cS1y~"aͶYw3Fܓָiaπ!o XF7gJ\ UybLq.2!J /nLC'vֻ{QzzBH ?#U* ~xYwŨQWr#6n\ޖ2+$̩VM3d'Gi>MHGqEJX)&j״(I[T/Kln;wuVU1t|E -bZ͎QIFA+j<:6Kg -]U˰6PkoT]q:5}-LJ[RۗMsO\T5G,0v5|pi\5M6aト|Hw)1 > rZc.UxqT9?gcym?[OP$0 υ\91gA>_?nQVᳬ~)w9j֐Jl6-%DŽHjjCݱUv>t@,6;nᰇj;|q}µr;a pF`*Cק8tC fo,%ibv -6VdI2e_Ue <[kGOHZ,uՙV{BZL;(T7^WCEUxFjHo5&\M!Au!ˆX -cg)WScRfĺVuՊԜy6=]{?ʩ5.*X՞ǾKǐ[EnS Q/ -n/\?#/kr 8ReIvfc}pX'(ۉ9jjX-t̻UУ[ օ#K%#MP:VGN!ƅ[&#g`fBs pwU -t?` GG&R۞F8z6YFNуlN:޿_ئdedG@1I -Rg0$"rY`ir71'{^qj_~)_] -0>n#EcO ګL=/E܃څVwfHϞNw,.[ S (6Txzmvvظ–x¢^/ - ssIR旟tQ9gcc&შQO /S|u; -%5&aW4*3)K$wR{n `-wI3"RĻ󐕧SOG~U@m "םj񠐐_V-*vFkz'f~Ih]sٜW[xġN:#H? V[&g݂lR/ %Aץn =KBSp\ӧbq=6#FFuн{Pvsc>_ۭQ׃11or.SXL3z_"Br,Im>Q?ZӇ{S)󊡗O<)qO>Tx]V =}[$SYuj cN'4VPg}pHTkDP0)xb%oA<šJ^J6T/E22TGDMtHR`JntË|jY@=w 3cYr?wT?X5KXw_BQCSt.t =>p qzi.>Z*̽XmUQa@10 !"?)ּ Biuf߃޳M+H` -Q%cb)$O@}d8T #(*laBWGJ魼qW[N;FbCT+/R =<(!Acf3K7uk+2w>Npwa7 jp`cqMؒumfG©wnrÛPLY2࿌Yd&X1&0EQ]ŕUWwA<B X]RY**8Da5qYZok QnQD?Bqv- -%ʘw1TGKIEmi([c^  3zoF}[BoRncm? oď/?J5Mp@;.r*&_{4E-oȨn*)'DlSNzdgG[ ÜD.|^f$So!^IITO鎈lCKbݻ8" -)aq'QXA:,mi>V_n(= Jw:[(pףgF#2o5Ho*'h>ɾf3A_xhu=)H=gW#gO;rҡA<;BN8t ?+;=Q4U(w] -rOQEf>YГyKB7ED̪’xڀ .WbO],W @I2\%&_$x|÷N -O뎚x -5F~wyjP&JF Pd233LGnnW/+j<(cᖳjP⿭.u,3w6PXmnE|o0Q+ؙ y*D&l V='[.^ '΄ic5-c62]j*i?tߟ.{+#oyt]0J#J~Ɣ'콟 -T]&rxP5 Ma[OkӤڱ=~^6ŷ =CnMY;%G,ׇXi0IF Tفݺ.;VGE/",$ -X;\O -r#v)-aw ;֭@_bM X)hjچ){ўb(Fd|Y^w@?Tn:-8 ֤h\we, }95IV\#wqy(O =C#Vg| v>JSkL1%}3uVo2m(}X(eT9|d:ۦ`hNЙ^[YHsApK?=-} -ng/1ׯ9!)Q3g=kys^N @\X8g?S*c?JnH~xlpX$ GjC!?!1&+:>+N =ܝjcLtiKVBl~d+q+X,VW6iˍd ujr]h??odOU;Vi7)ݪ:Rwj!0Hv:W $ 2r:0]jP}'*)T53Fg81lU WsOB:ER]wB2ea'Lwir9iş$y;%wLSBkASu ŀ)[Hcp,he<EqEJjr¹]~:r0*|q~HU}|/ F.i<`ҳB؅C(} }bXR)~b{Ո(c푌FoQQq13w. Ic0|_sme~.߯%"3j$?su;ڎ}ljbKq=,kAXf -[uz߲o.ɹbH?)s/RP#h֔'*DCF6̒W֧b]U/٢cH$+Dy}s(Lᥓ82:Y;g#j*nFɘCG!w J'XKuB$GJe7$_ˊ[gυM{`Ħ_n䪗uwJ=UD:B- -$KC|<3*ǡ-#8Ֆ+beu -))q&>BwqP 9|(al`,*tGapd= -Iey+Ê"L5ϪWSteܥ/aE/Zud83"=D^+OyoEVEZ:񩑡2c5C9R Gw4ۢ1v󡄠耖 f C+Xb"lt>u;JTTz,0)%U1ғ # Mτb #Q?2fGNVΑ~CS$:;遃TktV[eZ{&A9E4Ha ,C͝Ȫ6lNgҤ_c5 om!VrkϺ|A|!L3DаSv_$"u@@%f7jE#x"d[sYL}2|sV'g0J̮1kx9:WTo{9կaHSse{f+7N`Y(gGjڔIPCN× -k &zߒLV5 SetW<#rp=H"}\T>'ۖvIR0ψ=x`1I)-s}꧖a>6u):&w(ؼ/zksoieڳ& VXtEvU9.0p٫X)(M6ŞĶ5O]i߾~Ny L!vfDyps'W0*nPqo~.*:,];:(Ԫ.4@T!v9D -j(z#=3sՆA:#4d8yy? Tf9<Sc/Ǭ"{O `QZBLLuGT j'pݨ㫴,2iy\XB;)'UY~x3c {dEKG9̰)>WzGe\YRh],R?Ǿ߃GtUwo6"k:tXKvnWpMV@!‡ڹb`'Zyp˾wN?H`:{AпQcPԩhsκyv9g@l}88>ZKN̮jI/e`TeIv(EhK `X8*ʜ)K=V_"5q%g~8Z(G'KY ?wɜbZ Az -;uqedht|ȃG ;<<61*GPWJҼQkm'neM5SlE}/CiwizW`o"Vəo< -B# -iJhޯ2cqrԞ+u;2_DSd70_ ٛR .!R$l܉5!(c,;j}]jbG4Ϙ\| A,=JC_ ڍ4 t%v"ҫo((}D9vuVNt]'k~_oa*=XDm>L̚wwzwh|=J[;L,̙ f*JHG8\,Il!G%TG^֘tQ1`kCxߓȎ?ƿ{#r=1Qsף=/ RDD*`nvd~QiՁh 03w&;"]>j_¶ l{)ɃDy5,F -]1/5GٶNxkjt"[} #I'XxF:r5tlEE.3y# U~e_֌&dIHS? tFz*[w5b=1]gikdЂ1:4>]u -GS`v}dLG}_n~GW]~uf^,J[mXS&Q k Y>1V[xՎZ茝;qqΓ<*/qhuC5ucw^9$oBcdq>ǀ鞥[rjV󊒉-w&m?SP'<%f{p&<#RSC?.8(0ɣQ;n=#DFMi!-NKWDjXgE -2x,)_ECW%VARCI>=IvX ]ىZ]s.f=A?7c"MutylI:n9>~\dM3R%#D߿Ty)7ypޱbKPq& -A{2X% JΈT UL]]eFMSq͖EF_mAtzH4j%й!J˭zL𪶌V*x.m~cuD^qYٸJݞN 4޿~Ώ>7+75No9-uE[f/Oel Bb膗ΞRdDŤ]ڭ_Ku[xYu_=44='"w2rTsuuy;?#FsM=mvME>bw0Lztobp;Y VFvzEW0s"*mtuWn#n1B9ϜU( Fu@,1h qU;YXnFȱC:lf{QX2=h6F&5G6}.YWNxXtB4V*ly %aqY]Ϲ>4B3>9k(n>Ȕ|~+kBF*<먧tD}vS8f_59ʄ$d=;H\ydD!+z0r`K( D`,逼x"ZGɱYcf#gɪc޿gL늽 &iղqYSkz{?$5F 0q$`,<~>F"VZ6_{u|*XπJ+_z$!t;뙸߬c!kJIؕW"b|C$Afd"ibYRH@1LG<ύy҃zV "Ȱ6 -K\:{.'E`nM."'84\ ~>GP}t<.B_݂zL@=W=D@pTJ#)s;t+s_O - ~4?[b˗;FԂ#;JL~ ?uml/puz5_,a|vF_.*{;-' ^UibaP-CzbkW#C6Ra.X+W)oVΌE?u 0b{2=4:tYcGkWM8F#;ĭqFT) 2:$HdQyaD#vun1`zƮ)gZƣ w/GvuQg9惭GQy>+z3txo8g~^cj^7Ia:Mdc,|RNWׯIuP䉚ojm\EQgEQkdyfTjƊemѤq*DpqziAn1Liﻈr P\~.HKmUx?^Œ8?2?a4 9 -|]H w~wi?"^1){rJ1sM J p0qr"n{w$_s - fdHu+\ZEԠAۺFesˏ`&D֌k'{\)hxʴ0CΌ̵?W -adr}||(&\f~Zjœ}f*67{oqu_0~lAtUW]p! ^b;x(#K}ϹzF `98qZobUm_"]q| %e-I!/FfQ WM~U%n8I ÍYI?1Ѱk}3eXA'e5AljkE.zR!ȃ@+* Bh\WLiC.1!E{xUGR 6'X)s `$AHVhSp:a R m٪M1-_èeNTAxo$1rY4kA*X Xɵf=Ҩ 2ZQ.}K)}̌r _G|RT+ylR,hfRh_ -M_i=\%Չ$Fj}4Hl⤹@3N[cV$ɾb0.1(\.tȎanecyV+D)!Z!9z򘳅BXA#߬#mxS -` ?b@)VMZS^} 7@ӎfyV&CZ )5Î*1lJLd!̈ՖWuo!xխ_eRW -7߷X#n0jMhYPs1Cp`ȋ,BUF䂻+_.'#i죁55JC!ǫT$ ;a WdkR4aHiB϶<:LۣB?kN(%)-̘h'$Yb7eo[dH\$2$kO ܍ }6"l@1Um,D!% -0ߌfy/Bh.kLxR :@Ttx'vDP}#@WGvŅ*$Y$LQk!1B%y4"s;A*Ƶ %e2M D[D%Uң颾V_#9 L) atbyaw:hUeیyAEAмJ4 -Iwac@$tŰ07_Mo>F&M%!KZ}BUxP,C,y+ nF &^R$WiJUMs H@j( -8FL&sMb`}B J8QrƇX(9/%X s Jh| -0,KMacYcӗ3(IA gs0{^3j^^.!oC -iWvV)8JEXO_o(9ءB^hD,`/!'1m͙. ?=@}ߕ_ Xa HRB0aG$!VQ -:nxp`Bl!|kcے8Ta˔6:/ vj?6x,&&8, P AzhhA@6h!c]*Jq` sGJ B1K"n8vVddFxx5R@3R .(NdPyxg[TneH6Z&˔^S5mhS# S(ck}z[dQn$Y[G.EŤN!pd0lFc8411&aI%'V(=-!mZ^pI\!"tҒ6D@# -$\l>d7Po -F1B -BvSYz鉯; )O#V;&83>U٢'ضu#upB  -%H43jE^^JPch#rSFN)!}f`%V -,+瀵- I춦ynOZ*(H C,L1,P1J6GH'ו^mR'|ZRZ1ga%0G,M5x8"7V^LS,nH|pz *?=L$*a,Yj&BkK' ,B@H@[2k`{EkmG,!5X f~2u=OHni{ D,D8 -eFn(FQ"[b FzEZr!h;*K@UyGL DSFRFJ`d]֔zm/4ip4C?^GdppQE -|*ZuУI)AjhZwGBHrhA ShBjzzl&z#!RJ^X̹&2$D6ڰ -2ґZfZ%B#",)NT뮵`-+!iw)&SxAN; I4qFA@][jTzMɻEjKtxXTkEWPZŕjH0dXZ^:) zrLM;EI88uֈ()%EcR4kF -s)!450qL1w Laq=.nJNIҥ(Wč8AL7&x@A@DZWܨ2\ 9hװtiԨq2:x2F $P3}c6ZZ mJB0XˎoXZ_mQ ۳R@-K=RQL@Cd(T{@+Y)QhGlNR -%RT1رT+A%U-Az?e(+]I(x*gqNo u5B;k`ňC"ϖd FPIY0@L 8 Pq"@n!(kʲ8a"NsTJދ58VEtWXitU+-*@l׎Ѻ^Y@%!4Vwvg`P kƥ̈3`lXڞ>ra5¿2K ^.K%EԠp#n P\vquزu#>0T0 :1unх1R1ÃShs((.(pnz[k@#Kfzc:TK0{m L#+G:ԍh/ PʃUtјU\tT lCSۣ9u;I ] y_r5$ %- LRj qV^udBk$)ĬjsTb1Hx4Ѵ5-TW"ǑЬ)cN %" - 캂 V*\ L:4yr}1[N -ĺQIIǹsW*`p@4! pTV8L9=jQ -Ϭ2Ȥ.;ȕo`*4)0c XC5kQ&bڦ6RR ij*uw \T{Xh78'`tU|u E1`((Pc6~7LT-R֜xZ$o5m4~VKT5`Q"Ķ+VT}-mUJlBF8&V=^^JCǒwl -ȡ7RBL1,yeS7V{M44MҊy+ebUZO=0-)JO;+x)(93\vG%-]utpj,ҷȎȴCĄA%UIԔH*3X 9QH&vl -:_mO~k$7)TH| @Z9`7%^ u5(IlF`ycaDiD:YFL,Kgu@pT<'I8mp a1^)>WJЅ׻;N,e/Y8;^ TAbheP6_T@&\+݀4'&!"U[\RKhq5!@` -kD62</TYɜ;iEk?*}TRF0 -0&^D+FG7#ַ4j\7toA94|)cpO 'cvw.hx]< y 4Tv-%Dh )ܕ/"Bvvi3XF|xU`3ܕ@a‹44ߨO:)rkStDtQOD%q>@9WLLr,YyxӴ x\LJ?!0vƂC`A1j3E T`,j0 -38Ƥ5)H]pv`FIM/MQQ.a_oBĬ9&9:mFp@`[)5#`qw}My|x8Yڇgݝ/&$L.GkPYnxocW6䳣ݝr+dt}׽'<"/#㛳i]gJ nor|a|ԭڍx#k?vGntwhkItS3)?I:Q̰3l֊wt&{Œ%P%Gd̅jF<=QB{¨ zܠO: DdžW!KW"J -wB TL6V`MpZ1P35H {K!_QXXB;K1y B8C-ÁVBJ}vb{2 -(S k5nQ]rP JlzsxLBXykкHB3F,g$ƒ -]ur -$NbޔʀKàa:,KiF hbupʍmċZ[-uh)^N5n$҇RTZc#v4$햎0"s"/{LCG %Ad.n!|Ӣt]QaGbB 4\@He q+=_Fdiq'L`5XZ`}(P$dCw$ҶO"Kv0S8N*&řVa|frJ -ؾB3`梤|y3)cNTGBYN!OVw9|]a= P06@5UikiTJUKT%䢹XܜEXK( >kRRvP!Xz@.FK26.HClgC9c`*CeH򜸴<ē.#h[i24' 4:\ .ぉ TeqpKҬÁX v -\u+S)EL-g;-d 0O]Hc0V^BYYUhUR@ Ia|QYAmqL,+CHVPBTBRg*, Nm텿kg$ˊXXe fEA>[ݲƂ Ϋ5_JsZ\q\BQ_h7!&t4NI+kN!`-Rc->'%+˳m:P"n[J+Au8p\7@a-Rm$$SJyB4o\MN)|  =5yphrdlrJ`zJD9|zNppR VEjt *Lqr$}'5`;'VCVܺm;Ft;=P ]uo1ATmeb G^0A06Z~("8֫L;L DI*p[%HH49N6WJ1)'^Sq 22YpFZ*Zu#(MFvF";FbJTl>W% c* kAC5Zj>!^^=%a͠Ws@WN) - Sn`b'ۙq > *!`th\-`JJ>BbG(#GԺ !'ȊdWG舆DcUɎpW4/ŜEc"E |4"R(q"!nD8#T+T&oc$qWu]#~VSH=ЙG DdsA(X`=aqEąuJHjUv7δhSB W4RE,Jhz -p<4dpw_ 9rfQˆe¬006bD0Sc8`c(,jEWlj/4Hb{bHݾXzx1[HyBSEx"|ad BUJjt#_kc# %`Eh X!kKt@u#;V](+QabRIxH-Dw0  -|6ͻ!yr] g +E+*9LBRJ b%mj⺬Rբ#&z+6<&)Dm'-6ȇY; T+lRO8[Zĵh %-=z EB+8u:2X,gkʲ>p4&G*K`";I$<<2ʮ9zHħ`NlWAF/. -!<*pDA E\ -dp`C~Xvu8|k\r{ids4E!&,]]R\hyAt d`*1Sj-eȈ=FʹbdNu{8p(V) oOԗԪ0 XQ<x}3-vhj'2F` gb-D`b[t`uA!80mhVDj?n_AfhM0uC(`@vED n$YA/`82b.qs +|n%1\fb=4% W1Sw9Upy@o:M0^X),GkRA! Sm) LmV jN_"/J?A-2r@¾DZύ RXq V`N2>0$- 8O$Z꾐4tPY# -OL1, Cl~j dDRMdRA& - %btjCLT+רa-Gs?ֲo*+c K}Wu/祏e+=-L*)8!6kE<X2PQ-Rufi&[ nV,x:4vez a^R3+p_ŽIIi#RQUm_Ī1ޚDrbkRP ӧRsE8Z-_ \ՐkMT+UtDa7Z6Nt#|ejlb JcCJM%2[% 4I tj1[".g"S`H;}g&p)4WwnQs5u*Պѝk;Z{ 6U1ejm@X"PdJ]RT~l0 %~X-ؔD-+Ýp> -f-y(ķcFĠ%X!F]įyh$HبOpJ"O[I J1荞0i58wޯZ{x2ڛL71oą%x``|-%T_`7-vFwNj.W9.gN`3~q-Fᴭeow7΅͌n'Kߦe| lm1F(?Ai9>/\66H~<Ŗ/f8< Oq/o`_&{Wʹ]kK^Vg?o׭_"p n劯1uolF'>om'}ǩXȓ [ypa}ϯD 0KÞ- "$A 1$!f[eUx*F|P|KP#%R4 oǤFѝMO21Mi:),bX:% uU좈 2^CHHm?-=QrMm'ߤ=S@L(=Uwy gnr Xc4?Ao,w}QqQIT! 0by$wx&K!$nСa(SX[0aR>5%kƕN3ЋR*Z(5 A6֓h$J >V ӸYyVV "8H;~Q&naLg=6B Hb"DN"rH|UD0)PI*Y:Hq;;lWHytԭNZ6R:NR$Z:keazm7mz6X3:yD&`A*}쯤E-S^ -ήĂ4 k4+?, RHnrZ賡j,jW|MHtJ␔#"u𶱻}8B!33@J<3˚}GȷB=/"|W>Xa|n +H9ƏZ  d -N + `* ƛG@ypLɶ=!+х#N^H-۞] E0YRy\L|7]@0P|Hp HK1HtHWHk˸6 -Na #It-H>B(a!Nm#QU ZЄˢ$ l@:T"'ɢ DR!)l' -,Q /Kf(\$'c7VWq%v1[=k1)ɤ+'GNX1#׃i UArUznJr1k5 -')it$쒶f.g2; -kvT::@@3fs4mH,*[ '`Y3S=\ErJrӠόdy2ösjO6'>j`<.ODgIs~\k1_T#z!u@FIGiNgIx:B[Uf)'ŕ&9ȥ^"2+!k#a!5"\p=ě$Nyݤx+D{7&tܘ -\N;9NvZ<{XNim E8Q SYi`r'i"! U5xy!? +ǰT($d+ %$ 2^L2S)%9{De9 Rco9Rem<\ ͦ-ĐDw;U?q%$X'\:qeS,=]VbeOJ@4"T?8C`*Q9E>+B]܆dp kAvifNꉪ - =ì$㳔+i2),&!gSB^* %107`2ls h rOJ&'#4a>ŢC>tDU*Rd6141 ["6 W -HP>hUWLIJJHmd!^/U }kMF")*h i"-< V=&S_)(3'N)).t!brI K$dn\'OȜŦ׷Xޣ2JM~X*ܸ6cBV䂲 @; !..m8MIp 8oWI[ k0ݵA LH6m=)zeI#2Sp]3fc3E7S='k6f0P5dVdkiHQ@ uiAJ'7xf0qy5r -Ԋ%3Yq!!XכP%p(AhÔKpQ~B<'3^3j\=^k1HtHjLB 5{7%QIsBGR'ۼӼ.$%FrdaLȘuMt5k&-C hP' -Hx(ʻ7$]!ƯA;Q6oN[̸M@Ys2!eD<3Ns2 e|"sv!ڮԡB$y(l&8esM̸EDόjI<382t̴'M)zE #Cimj -4ZN]38kk&!HlkFx5U^mcŊb&>;:7c2*z ȼpj\x[TBULw'bu a`XGC*=QK/AJ4&l%<-5LX_H|*a Nhjph5ə= +l7.=6duP$2\ _K"~F9xjY -e0hO*sl$f7.?$K5\2c_*c+ -C L< 14d -ȹ El -SHT+UT.&iN ɥM-58@H0|&jDR0\44:dgnA@yk? [SzToTFc eޭJۙf$wI!DFPd)Pz>x`ejbIPO6AJ'Er'9lCb>₨|ڤ6pFF))B bX؟&Oe8 -qg'ʩ"XIQ &#g۩j㸂g#PRFNք/K%Q'jSs-Sā^Kٙw' w%WJ6lԴцf{6 +nG-0[hJWY1ӇK-K]u]9 |ʅI?c'ktF -hH~t+F^/g5UւX։AF׊ -u9Xs3W3EAρwD%7%>Cha1'VhtoJy.v9aNIɉHDHx1h!,b<,p E0zꀀ*|8a"rf#M(DVӊe Kl监r@ ҚJ!C*kJ)[zԸ@ -ۋV@`1.'bN( -NDDR7nPJ|МC%)KOszulb@^sRH*, mtyS+yD^@򚗂ӱ6(1TlOH(Sĺ*O{d;1p A aEe6+6iy%"WVQ`UVzB%6 $1l>/Wf&&[+2Lr0l - 5u?!(G+ X[ZUR:=YV¡bd Z>6`PT}ik -;DRw/-HB8œ"1N;˯E k5$dFd7t<= 5<{X.$BW^j$?ef)cKHV( )0P4Xb`~hzS]RF$7E2>`Z*a拒puQn.D5ߖMoOZ~$hp_:j\ ō/QI `&8emT \De9!eU]k n?*VߐFg^R({-NrUJWR'z2Y8OpNJaRSP6N 5 ד# zrofV7NpG}9=AOrQMC'{HO!A Ue * "zWF=kH)IaTK"BQ&aa`S(HrI1u")!N$c(:w663QtE(0c3G([VbDmAt.Il-4)kXCHi!sMzҮe&(&GsBL;j)DepNMY3@v*hN)r8_'^*lPIɖ~=7٠gYe9 fg*Ȳ:u!:ӆЅMعERBp: 3z}f(˧G -^2?RLZW{ c_o 3r׀|ZBUⴗI]OJ"1_Bň-c]!Jj©H!$/y8M5sifL&UJ[ (d1tG2 V[ca*ہV+4$H=eG ~7' zɉ$9Ȏsd׫Q̑# 5ͲM< !z")zb$ziq&J J^$6ppi愘EG>1ݺQ.21"GIb.O}oջLl\p{RbMLuĩs R_6 h YI͵m0dDfވ+rA7LO>S6Xq{RbIdR9]2~dasɮg T56 6Q"t-kf,wz.\Lq6MIOJ'F~$p=\&-p)16_~6!򆒼1*F-LFJFfJIqFR\~z7e;".V/%5b.3@y;bɲ-2D]ěѠ^z;~W3|l0p-`jFbL -žx"ga"P$BԸ-(EmUvR{||]QŐ416rPFIC9lk[՘WHYeoJ?dME.V]pZ71J! oUч߰PZNR,_O܌0   "4}|H/b`Ʃ'Nc#Z̗` (ͅx.?n,!Dd)4n7!7 *fD%Y+QV8k((|a")~$mȍCؤɍ p2)S3Xvs'mZjҐuk3b[U#s&w`{&4uRR:(>6]]M~% +ӭxZ%S:MTNg՟o2xD !W "JOzI[e;P9],]g إ^IJeBPڄ9ŵT8cVSm~FYeQ 7k؞3̰@9o,b9工'_!jzi )@۸[w8EВ"-9zjaB{HMyJz&_jxn?.|XoF p_cD~< 4Eqp"jK:~YoMh-xʞT_ Lؖn\">0(F[\Z*Wٯ iqT9s'm-ݽ*(fH$)Fr%)-R|(o1L1vJ{{粯e|+җ9@]-%Ipxj{.)um.(< ,W-}Ǣ,MW q]F52"4&GCG$W)ǟB#6y7ZlE_Yij}u22r%CM''39<kB%$?ˉfB ɷU}hՏ0H$_|7BK⤢Ybr& o|ľ@.d1_& ) "S~\u1(Qd ]aK\lq-wI]IY}Q}:_# -R/S. mmݶn\Ѐ-u:nQe{|ll2~}٫cD42[hӿ%,K{)kK{1۪V;(<]ﲥ]mҴ6J_eg0[MoMt)뽴ݖ>/) :/.>87HŪdGs]ӷxd<;>\5]ڼmb>nOng-vwzytkGn?f_ì| endstream endobj 42 0 obj <>stream -L'L.KG_ub״x|s׽')~_a߫W|,Å/'?N/\ܙ?/ggbg|Fǭtl1-uC6ÍѽM/vHۯhWܚOm_l'oWx>2㗟6YϳWo=O_n/vt ޵+NxtOG'{;؊@"Έn1ٞc-+ dCHaק^nw?#7[^W`ryEwަ>{gIwo=6oq?M`@_E_XGi/%ԡ8TϚr0' 87L5?G2-z?5ժۭ25|Lr[U,אqS^}6MvFQ'kQH6dx+1LҬ{b䊾q{`d\b! %8wcZb:οN~̵& 5?:t^l/:>KwtDn{F@ej+z݃noxx8~ep}|v`119[Slnwf:"k'cGNG=y:`tat)yF kNj#>v^bbN_ž>lg;GgD|`Ļ[l4KbsLn4o<ΰ\/5 n2>B>e\]ke.c'ukWY5+_5+?#+w盕ϯȫyՙxErE>3Kh(6ƻnk=}){׵GKV8%F,_tƁ孞L: l}ؾ~ -H#USv}(0)ۓlgxߟ`E7| :PY '盋@!kfjͷ|5^5>o׵9|pr~kt K -˰ۓ{X4J(j]kV&A7~9^"ݝ現O&|e GmR* - +^׺M~-fɬ[ďn$SO.||Nj'n>=J˃nOF" @这5c{Uֱڼ 1es޾>6M-oc^u۲]s5;\gK`WyU7s[DS3j֌3w}FZk>5RV5_ɯ7~|s>| pc~YyX-)wuZ[35c;;/c35Z_smUe|kV5S4>Cvkfk獭klڿy(hr+m\RIhqJ=Skfz5kJjQՍ{#L4W, wuGcmsϡY35;L]3WV`YxMDUn3OuLjT)[KVa׌2X3WuMfB~ ov4׬~#תiW5[s։*ν ~h5Z3sĴI ilaZ5ZƯl3+߈w4Mm Zj}uLAd'i5c[3 @7f2T[OWL`ks|m~lH^+_6kclURV76k/ښ#>xe\UY2'7bdi -[f]kuX:UMo߈ EQ"^hښ: -ӼM\3(õ>k rd5_[sy,-K߄ϳ*swc -JhuY|Θ)kƶfl猱sN<_̮yU\W ~3ȶgmVqٶ1ǀAfkkvn: g3UIH߸mMjġkkv:3\o mv2omeCZS9o{V-kv>=9npq+#s;q΃Mtw3pE.͟܏3?SggZ;J5W_sWW}\7ghO} Va 'I٬[؝Qw.vv;]qd+ןu_k?ovMlB|~) -:H$KxҩukeS44<%ҙf'omzIНNub({ЛV^:ymm}3)ZţxvttgL`Ó^aFQ?O'y|z6 9ߤ|w5_1?ow#ԿdR?;?􄯸;d't x3dI[.ݝ6֩LLC7&lݘ5|1,ɱWylg;OGųxYk!|`wPVn7Q8'mh(펵g:ەH.=/ʁO3$DtE``})6lO1oܝ?M"CnDdKv&w5hh2?pּw{ߐ?&|x>SgW2vTՍkw6nݾ?]|U̿ݏKf{{#og|8&,bӍ K|ੇZJov .Us|.iק?}LfW4bb<{6Y|3y~p4o;hz}<۟Fud?z1d:n>qä77oov6(/pXfϮ^Iw {q ߌ.}+_7:_\R/Ns织ݹNURыo=)]d"K)l:0O&Mf 6 JGG}Q[~ e>-#}QߪQ oey+&MմM>ؑn蛴BcC#LG&6|ƯnSgjzsȜƖaN5qq)+o埯hَvM҆э{㣚0KIZq(\ #(71λ7(Q+i'͢pMҹ~Wk6lQo+}dϷ&kl0Z>lҥ-׆N":moSJf#Tа^Hcbceu8鮋MjtJclFЅ&od _sCÑ.ƵW߰OON6w{g=ƝLt8 ?EpַAwCi5r ϩ/?ɾ$?Zt7N/[ㅗƗo|?ڏM[tJg*.nyivm fY7zY7hsՠr (˛cz_1yޛA3>G='@FI_6*{w\VҨ sLףd^L  "NL[]ı2Ys*XSE7{OSI@35/v0#\h=ko<=Zw"N|8ݞ|w~V㻪yc}I7:em -}_KohN !Mg]k$]|?!?\>u_ć=؛EO;`^?i ߉߽or>l?ߝ,&3m3ضIa'Gby,1q7gRc,g|OnaxI@:s9'PN)XbluPFɷ}9v~yT:z !R89}869/d~Iw&O-v;#|dT8'wmw5:yۘ`i2z4ptsgpdw{fѣЎGwƳgGgѾ>PM lg?>c8|2/vFfP ʍʴxB1~dh@v6KjF [~tՕFY3ƙVu`noaݭyAp|q]Fˎ&{h:Y'V9|.BZ7Dv4=J[wݷ,oq!~3}?M#&e*FGp`r0=swr'lW?Zn -rlNw{|ۻݣ/U0Y7#S5_D&y{yt83ُ!lj{t2%eDqx`4=Hat/}y9@\?Wnv-6ƞuI(κIwvngp4|(0i< n?M;p<.)LwgсȪƇݍq35N:qݜr{SX[nh'|6昩νb}uikdZ[| ``|)5n(IdnFYeU>Kx߱ݠiaW8G79 M;Ҷ1}nSZ:a#9CK۳/vo|15Ν(b+)%5=xX΃'n'\A3+w/w'?wCÞ=;+3T/1~O uқTg~!='gv6a7w -< 6HSga*,!V>ʳMP:;;y[ڡg;Mdl'ӿ;μcP~F2#kIhCWPOy_8{zĵًv/|߻={1B@ƨ*eCwLEx:>5˥Yvy3Ng;b -YRIy#&Yt*|?ΟEVv)Q O[SlO7&Q8|omo~||ۓ{An"&q_ښwwgtg6vݥ\}վo}wyY^4=~H; beo̖l7S@8|wPNLՓcZ>[ AZ.-5h8Tf[`I@]=Qwvɹ6}>ޙ,&'oj`بeFLٸ ?yxszAE004ٙ/#7[ˤY䪕 ;p$37sDKlO/ۏg ;B|ҮG3pN,dztb<3>C۬׫'؞'?[$P4蘿i_^$#96rSXuWvW~wdCxF7ny'(<x'nʕKـh:N-4;촵`_ywmg'/i?f74O'臨{~Nh;^}u.O,7l҈^K7&O;3zƢ<0~}b5qv#=qr/qMetZ&1MYjvOJk#xj%Dé69h-4 8C8(Erb}P4&˂>5:{h34NB2ĒF3?sY>Nwψڗ20/<cqE}g~a?,>wWH+7`n|o[?|l+np?N?A}?x&_wo[?Vum.\h{(~xxW~8[_~9Gpn}|ʿ`,?e9~wo~pݺv\l6xȨ7o olNbݭkO0w?t'cOnW76[;߾o=_b7?m6Ì}ܭU_)o߽qm;z c=v齏o}?}ϥlCf||{OPG~_U]^՟8[o?yX2ٵ_oO.>M7pb]?ؙ^_{_l޻/nݸt/L6wwu>r/U3GãU3;y&^^O߿ux;Ow}yg?7/.=bc?Oe6GɭK_z^=xq;/>~+…rwɟo<vzrƭ_\F}#;c__~p`tk`okv{ s^[!oݦCugݝ:q۱e5m>O7>oo̮<=}/oŭ߽~{l';5_M_ܺ|xeg}[t0ϾЇ{(r|g?:.oyɵoy'_ۻݸr֥v:ᕷw|+~pE'G7ߝ3r"?6|ŶMsvzkTGh~x[~_7Goo>/[]Xw=|x>]lN7!͇ۚ㷋q/fťq&~>_߆|ǭ+6ҳ-z',uC؎3a?k﹥6E>A?YB9瀄sgEnϾw6U+5gI|mG)?G?A 3+ pup&FbfZfKiDK.T ~tQ~!?pK\WC|#7{~hdh2ƀ +8SЌ1'>0^@$nD &mJ%RfgaD3,ru#Bb~ȿ/ ݴ:OL04V;xT#?ʅN6ߌ[ޖ'[W?3dBe# -vEvR_;i)Uwį1w;os;§4=kf9d\o/OُNo;?+$Iosh'.g#h2:}6|L?<}7+Þ$K1f{g4XLN;v~P?} -z-{UN/tʟf>%> cVv/V~uE 5mIDaM`e<ȅT_o mD7Z~]%2Q .i2$1S2T%h5@PCC_>L+%0Ͼ˗4`_mn|k9~ӯq<.gSCVo -VքFp<2~+WY t()8~*"'ۑ6 'A[WtS:oZyX@Bu`Q~._{F; Ÿv9^چ0I;[xz@P<0-s?MjYLCc+Ul.[l0=dg2h ;=?J) #KI4cU.q!S|}2 E|.qc$VL.A17EGː*F-h+Ύ.^(R }+qg>J"@Ԟy -, StC@ -Y#:pȣ8qZge#7(!FqlRi_ܷ4 Y?=k~Hԯs˟3ȃ0J,?,%_Á%gjRn$4}d$爫w>iuV=&AsY( 7mMiHD)+37e-ȟ㒽Z? PV7+mOkjm3Yx>EV ˱Fx־6XH Xơ33 h_1Wݚ/5W7s]*G7Pmۚ~]WtB9m]c>6RXX2]C qM~įU6a[, TyDHfs/?-R׋ f8Lx=̖˅V̢FSC*RC1cmԤf;*䱩~'ͯrg2u -bw k~3pa˼w"%,9$M7?@0>J\J$>KW] ].jmZvw~A&6vmPGs&yA`[_LJbFd~ տ(/%ق;}'w+WosdD FUf<vgAif/\Ɠo}CJ ^u@#d vc wϛhDӱO3#Ͽo(݈YEm߳P zu /F~d{# tY>{$?N-;`Ą:7{v.2H95ϼ^qU!I*_#BZ_ƍY`^QKj^+ -fZt4Z_Li brwS\,q=*ZdCX:3H&}z @f503E/}6>S2$S ?2i;Q}`S;ąX1 (Z~cux3mώ?6*|6N{Nai fk** -*(=~Kyc7۵ -0F)8 hQ -zp4  ݯ.Wm!簅SJmܢkSp+;fgzA*J$ L5=l8U!fo xx΃1|b6#܌J$d" -7Gzk8< -^U%++v!x΂m xz&*xV08~71|0%OF}OK^uԞM=}6\#I@2zwZ_2OQ] -<oTb~%"@TRsjȚ -rVʵ NZ*q!4/+5xS2bYf˶urM{'X{i Cc'xB.vIK[V9>1Y Ӌ*s,SnGRp^̰>7f{t| v%wQ=bXnFΒhsYuzܺX+ Xmfbe8%8U} wXe_% \29M󋭙kH^FL^kM ħe9KV%;*ϔ/]6SD73kE@gpAŜbz˟o|Uyh+tP\+NˡЙUz+bRy9 šBACQVB* cY4^YA4&4w_@4[=JfSq7Rw,j:6$=F-k~ 6pm$])q9.(8"= ٷr~lBl| -%>aƼ}5CaWA+ַ罒$؁eXˤL(/K9#=↷9fMsW,M۰V!m?GNsWc\^\z(7v498k.gxvi+M7dϚ 1/|,G1yȢ~F<;V -}Z [ATvux+#WmVn=dl{Xtɩ,.+ϒ/Ȭ\vuax -,H'y޸MbBR"ܽZaieI?O}#Od.;ܼ* 6w(x-D,6pxLfIr^-9hs(WI٫s@K5wQJRy&BjP]j5be_? =QؗƜ,LLZ@q[^nW$_:t[2P-vvI@i1p=^%[&٭Ev& A޵=?:5jEC+T;Y'K629.,'+eBЗ~ZD%, VKn'Ps$4|WZ띤j^Z"ՊuͶ>ԺaO7gMcu-YnviO5Ӹ|_j`n0ܮyNt$bƘhÏAggĬFsȩHqwj~\q5o#-؛#DvZ{~tQFerԑZ,zf^\tĥ#^値~ѹRg%iwM M﹭ -˷2m07`DT/MgVq@-RGx~¥6n0>?B!+(\ET+s^S{vpY˿3)usiwakÑ]NO[h,=ǼP0qVOPGl'- w#٨`e -]J>ej5.s@au{Iݍ*5<y4 Թ.ҸnWʪhǂuF鼺µȫ?fo`_bm=Z[ LdOF er4E?trqZ078=axW_mhc֦#m_N*=gmyC>nq_oӳdyasbqO{Py24O2YDt7>죲.?^KBBzB,>x`2qM?nCHD<>S8tar5P(&]pEpL|<:WwH*̞Zk(W{*O!-:]y;Y%sg4iJSk z%Ijqj]{&`ca ?+WWrLڶ8[ݍgL pE^[k!yɃtdjϹh1>8Eo1E˯SFa(˽̪3w? 흏F"ә̼۲<[;v8]~'4 35`hWhO礞Z̸.B̵~˜ izs.tn7np%5۞xe2\<Ǜr:5{o :~'sf'e[4s26댲xrT%͗S]'X<QFʨ/CT[ スrqkR Qe8s|wDSQ'l#|GsR噘W.;.$Ru6TAUGUnhOT0VݜN@:mvRaT'$;eר'Ř aWla(BRT- --a޽mXsmaK'8AvR9j,FYL]ZBbNf4Z7쨋2. V:5:+@DOx>rZnuxT^ -C%osWh;bk 0:Ʌ%3=hEn![0uѯ^Oa4`y'Bӧ -;,Dž8i'v!([صM$d'*@CZHz\ɴB/;wvsN=,f(aYܢR̔[ -O͗P^ѳ:R`joUmZ9p]F.§Jڄ *%Ms7 ;bx i5~r^uNxǁ¿iƢaGe)5VɊpO(*iR<*R֏A;t"yhd;>O伤Mjs6s!/SKc)[Jq@J&>&UVʥe\jM4a^M,*Ib,e֊1g'Ʋ~hݼmFIN)tg -nřx"gr@*,/hإ{o׈3rKP.d3zI]K<Б-nMlinCIQ4z+ -qO&g%Os#7K3%L#_͗ nًY/lB!UNz)1?bd@aFiMnY:`(1#IOJR^)C1+㺶[VFd9bqk@u{9'VZG!X]qŠWVL򫘉k8Z rhfV-ha"\Hr'B ǘ'ɗQqGʹ<9ڳy? {ѳW hd[FVPn7mo=d;h-6.ޱp\e* I+K'yVQ<&eʖw 2$jgk;9$C 3Rmq P7l6VáXƏG6qG{"93aE" -RRU3EՖ>-{AveP:n&fJ]yvi 1J=YQ{yGi4u_"?n /:Cbg߄Sfey(p1puPɂWrr۩ăKCv-C*Y=%Q |V -.H)@r((.JxpƉRwd6ϦLF}ʚ>fd$W%떪t7^UKX -֪r+ӉSni]-cNF'ʃ+Kx(WNRfmgzK^;XG#Q鼊GKQfbJIn3DEi%%<-#.5[yjyz'K> fYծneטzpu;S^f<Յ Dv'2Ag{͓`J7.䤶G$xҜz'c+X]y+7bb%K:n|-(*߸B$+ʱ sH+YvFOs"D[znq/FSΉp{1g mҕL@:szoYҾ1؍@'7)xK~nSru9GeZIqgH/fydU8.$wTGc1OY46\b(KӬ&2D\evWN:HS_+00-CQXIR#>C4XeEK25S>Oe~5SGC]+ug;(,[VZ6,aU|rFx+u\h\G|XeJ5Ce&TSi ݸ\@zcmt5B _قڣҨ:=`SDOi{ó7)y!!~f2;=]/YFjEI|fzݽOG,-\!{s峗!}H1M_@K`H0%-G:=Sdc.Ʈki f':jT=E FgP1*qu>!&%{xVgUf{~$ԙԀ)ɞP<23APamv,«cCbU`3[øʕV]gp*6QvWh-}Z[̔+$Yaz)9تW7D~О[SPt 1ͬL*N /H\3]@GvIkI@21.9B'˴FӸX.D5os3hM(W@;䌧[lCѴ;lFB7%XN`o2{J tqʘUHFPS^*LwK:X E[m\3zҜ5?rN^z}e !`ʣL P.eqڃYZ'Ψ9(Dۿ~<~' -4,jbCW}Smgdn|%'5wسܰqtͷc`gRa3HN 7gja]`I@:O?=9NlϢG_졑Eb$ٳAHVKAT1/S10 - iShUBԧ;$uO3!GDysSPhB("E9R[s&=-9YgWD( -]b_c'Rج鷋408.Rf"+qOV@dbuGuʠׇɐ~'VS}*!3t&rp2: -8ZAʥb ΀qc:,DkUͷ!ېQ,х"^ӺÎH//Zg>|`']zYZUZx\@0sܙ3tHxNTiKv 0J.s|z ո⎍ 9'! 0\N>Qw!bc*rmkrJR♜K -M _lK = G9˱`EtP^۹֨6./+grs.%,oŜf&=e1 Bf7<5!d&-U1*o8%-n: #<U\CQKpڄ|Y'4ƈKAW~17Qjr|9N)y0Y`Q{z!슬K -UGYnzKQGZB~P6EKsk}dl۲aAl{탚Mγl5҇&)èY|{\pV3ۛK֕ -j*e-.$G좜fzn?Q,$duېY~9EvU:]/skp&]8ʼn -HFqe9fs[¡6=9l]kewfiNLRNJ?|T0K.af$&xvj(urXZվ[$R΂LUx8'vjc={kyXLb⤽Ğ8uer -W5?C-EioĶZIЦ9$(tg'A=q^B-Xܱnǫ! 6!ҕ] ԑE,SY &b+%IO0K`@"dMsiiʢk:Jr,GU5/(周g\5dM}gLwxT.:$۞H٪՛¥.hjz:P!5ǖY7>s/Κk3>Zhyu<9 ru(e :wz‡;uW ,N(UY9)e *ofL"@\ahl#%3~Mݗ}Fswc,4O="RƗeP>בղ$^^JIT r@={خ\\rsCI -]ن]ڈZIeYRnՑ!?_|-d1l8YʲJ|禿:BFp38/ԓlK篽Biz%E!5=YITr}a$>P@I#$A("7ť,ɶ4@Rޡ6AFT#Um9AHPsy%WzHǗl@DsN~o"!]kJ>l$9T*ee;t8%E{bTdԽ"8 ώrKYa\~n=Hѽe`vtpޙZ][ -ꕹJ|޵eK -z},(%dyw&#֣68v T.*i '{MЉ/$} iSz^dת`@ !^닯pb2 P=E~YMA3n/m -q^.憙yWVh=M,^ͯ**};^r"O.^\2R˜p+s sNJ'5[Y;ȾowK^3j~o&6S؀=67 a^UVǷ`ؓYxD-G%-DSbM:ׄ.2fvNetD鉫.Í2|!{6|D8o}2id*%ʁ>s'R֔Q\Y~\rJЪV[hnKDvIxFLZ8=~ZfC1=Izې6LcH ٦!UoUFanІ9S󱶢yBXN-w}Oeiܷ0ܵ o{+ ]XR+n\Pru4r?vXKr冃n*Y䑐:=Q7.0dIb'R\򳢵[{L鮂ncΠQ݂KTlZ^&;\hy0aIЗ3a/۸;sdlkYH8ĜoJIQ<;kX1[4**fx5 7q -W'IȚD5H02~\a2 -wj AK_8k]ĬuUG]1.ӆ=) ⓧELeX5ʎ]*2NX -aX;c坔^2X 4S{*SڕI1bJ)Ԕh\zNI&O:lql]/1 !$£FI=hT~}Nr̀!<jB}ߓOǬ:LQ=[|g-13v!;M(#ܼrxBhV|}*.6rC 0dxL|c9XQ%7INܨ:%ZQIǪX$'cșR-6oCZe6X#cӴ㸟/֝KNwަX_ި<Rqz}spQQ"U~eI{Qv0^Яnsc _a8cIR+O6nԳ!L7ɰl_W9ϒxy'Bcsu0yDDs1م}q$V%D"s=`IU][ 7.)14ue?g|i<'+۹f&m۶`~qxe,|\ vdK̓;fJST\C;)&11HZ,tWTÙ[&0^T_M_;hۭ>o g_%oiqtQX՚+v'Zyj$7a4 /d#^T4AOMU f4἟Vxη?9e"KoR=v{YD BvprzOp_orCn<Z(B?we3yj%J~U_>~`l(D0}rTBR˯xse T2'2ΣLɵ̱`;qٮ%W|:*Li*ΙYc5wHzdZZVAmiH3H[TWN|w$0쬞1bHfU./̝t-tewyr vSuYi^$fl$8YsﭕoKAqGRc4Zݾ%dไmt\Iݬ $}I88Kɡ@N*Z`1?dWyh.49`"qZZb庿.q,5TYxn'4E}K#NL']luۦLC! -_ʞ tUKYnuD{"]*k|ݝSQH`.ڮxp"xin[DSIET\ɨ rTJ Q]:fƧvoN#PT[zX(W(.X '5]ߋ֬*2C|TR* "sI$]C@ܒxSU[`pCn" yRnҢag{-k]pbEZ|Z}_͞4y< rU-<)+/ESZIQFԪq{0H[l .DZb/Wq}5{!eTWi쌵WFSN>AIcl &tT8qػywű˅,<"=Co:~Q:4Wٕw|i`,'Tk >0]yJcj/610Y~9Wc$:*>5>V}#r*cK?o*FCXm]ݤ^ *[biV;Տec%VPK{\z 'zCdŐ+.}eU0 *'峥9ٸ&PD~[ݮ; ScK3SL"0^Is/Vmp5mgUgQ^ ՙurVuYƥ?RuBbڊ͔zjs$, ( Ny.leНE-A iUW:@j3Crusrr ^4 uNי"OՓ6'd(w*vً:}<@&ӼZ]Rnb! -+?jVm/*cm} Xa=$t8A4w>)"Vf5hD i74p')_ ɏοut{k{l:x.sn OYHnװ ş=IPAP;;VZ 0ާ2w -4yÝu̔*OX>Um3ڍ+ w_Ohlt8@UO '^Ɠ֬7G.\ڢbPIu6Ib6mNi]n;T:Z2]仈儇oC|K<1HR^5Ksg6+N=] {cA -U.\wƮ&\f7;չu+weXgWqk\5Ѹ&;#PFC}F \%`mz`Kɤbne(VGM+`zQױ& 88x]6m]5skxFbw,h7ssw.Sd#CU[ht]̘×/_NɃ㨹@ŢYeH|/zgbIjdI1Vyn- 6%R8.['Ń^YIA͆<Fyw%2wY_ -΢%Xr}[5f8mz:Ta:t\]ITȭ.RG`m4SR^iڨzGmg:HMPts#Z QZ=r 3Q&vd&dv'ٛ“D(8_u@隆5aE 3cWu8i{5xG's u:&sҾ{ҚaHaX_Ǎ]kʅsuJ@Ot. -[cpTzqط*s2^APkvZܲ]BP,$zPfќiJHn $P\yazk÷XW07p<닏ExgBm| W 魚n"R5,&*"Cؒޮ_,F*zH1ј:Xy.GKq+M#Ӄ dƜ.7ℸ%j)Yckw %ϖS )xWC)]S|]%+)Qr\i-w+kU]<=ʍ ǃ1iuv>xJSئ=z%V%G!Y؈.xAv=h%Q:QəQ)_FP?`*ZD;jcC^(P)PNW7[޲W.i0^"uG`TO imfotR;G.5 h|[Lep2Z3jb%dj kT ![m=n~F7G&K#- 3ϖ/.Cs3~5;(w};5aK) ;g))vN-RQc)byyc].Rcq3ڲz\|Wl9/:49aK;hqai'pMljAܬۖɍjIʱc^Kď/>JPTŭ3 z̤m{Kg+dM'ætuI,;n im}.$.P:8L3z # -ϋOk-yﴎ3g=3El :ϠzM@ xl+ ?xuH2W+. ,}> Y:}Ze~=K4g -I ƩԌBk| x{lq4Z*5.% -P ٬L!GT0bMAh_nD~I\BG.O={=W'ѲO^$ʕ(̏ AKP:& \z7 aDp[ק ?<[Q앙^+oLtt̍R}:alo _,lr.K[j~akt0r#ҺKr&؊;I -. abFna<4-ϯr5mQI.!Abhc3Mx'[? VRoA"F(t!P)_!i!DBHc MHs8-hjk$?&/ Mڀ5~y -߫@ȻqJ dziUvt~;IlX -;#3)q+T[0;-3"]JŜY(Y?Ņebj|$N²d*5E!蔠̋ENߘ7&T9=?Q] -.ɳ>,РN[\lՇ#M8îX%yY^tMl00wi_4NoyӃo7ާG - ˈ<֮x*6f#jAX袿Y!T1ih+w>]y -9Z?0nm@T[JlW7wA >7nRiY`׼?|9x{q*T4 wO/i%dEni( nn1iD-[:0[=)yٻيTܰKbeF:5,s<3O=K-akVz{,:yRk8lK -H92Y(m86jy#%WƯgLY>)IDYX=L>E!=g|'IQ쯧rumi!w++v8k|{5wr܀q\%Zv& f~8ST><ȜeܸcܵDRcj@0 f+X`Q>BGh7;❶snJp- [ۥf[4bdz~t*X\huWPq?lKGD{QN;,x?^YOw-ۏZѩ׭ˤ n1Ms8yh9|?$*j-1H,wnQuFw{>>nm*]i!.FcDuVۆ`{i=}Mg(vՎ W[Bp}6tvɤ֢P\9>[JA!PD[ԝfSGϗ͗e\߸kmѮ+7︓[P~7}Wh%D@Z]5>!qQ5=m(^ -3٩|eȗl]"*UI -Agp2ztzۘvLNCkt^M>9Yn -4՗m?XL]L{=hLdsr'iQλ5"&W-78Gd䔻Zr 2iZ|&y_j <Q(n6&z^vb#wb9; I*Cw ML, _kU(ڠn1$)G'x}z'rA߳zcx#bǻ4]6(m].P"di/{vk$v-YWD f,g$po{yvZU&Br5(͞?`TVZk ;e:h(\ETz-վ@fVw#8A clRx`Y&!skگ~=kCn5_=Ĝ<9/ˌ^ ɻƧ@b v\(C[:psӓL'b.2!\ӣiM^ -Ew) Xk6x*Z" ֍[mh̝J %^ѷ{9)J7Ai7d˙@&pzt;͔G*~Vʢ6筥4bs A0sJ[hۣuо~ۙ!!ј$5`K0wlv]!ØGZ%}?((=0Hא2l5ƿ3O(sq ֈƒ&p!ҥZ/Ml3%cÖ_r:[m~uf'+HJ<6-ꆼ -bcx"[Q }ZsU'}ά{W,nSil ၟ|qftkvl=AXt,2)&:5Y9(^<濝vš9e՛Nxͬ>~Jxall7PEYW'#o4жŢ!MI?Mj.}'NN E<'[ ʢI.P&1gv=hmW5gq+nM6ܾ-VJrt8S K6ݛs ~ROdySRva!6[j$I 3s" sY}>?w07^=1l5{V6o d~7"it\_ -4iXz؞R]>ީœ9bZ\S{`.~\ f~@v66/AaT 9N;_v-G\f!"E#x(hr"dr7{SWi?Udʪn%Ŗe7+>ãS)nq!X"NaŃ;v %?sVzlHxHLFPQ.~[+? x}V<6K͵ZIRrpl*= (b>FT H_5jg[< -1 - -@xjS3^!Mi:pL ".-2 MާtĦڠĦKNas_jñeո += -)+쐖X݅YyYf0 pMzlq%կաzO_aN{V^<k)_FRna ڏoA YCY4vQH1zyUmY9:-VvEmici޿[3 >Jf{^?U$mBƗRZpS>pXˆGGBs*hPSW믔vMc _O-H|M#Ǝ1i.uZo@M x=|7&~Mȭ+u'r/pSNLo֮[1ڶJ@]?=X|Ֆr=yk*0K[PuU>K,.~3"k%Bnb{!UɶȢo|G+IKI9^%r \T0%77R.C*KN+DݍtIQ +^AD&w%XR oV v uF}i{=qHzA: T s2o<{US,@M~ߗ$r+?kHіН.#㠗ړ/OKue4(A -,w5d̙k̑T1v:e ])ؘ -V,4v߯Y3 ;E›#Z^MfSR:[m#M? !t3OzC8o@xS/ 7Ɓ^oKN_RŽ&rSy6|L0d6KY0nTsB[4 -~@[h( -(-,fE=g ^uQ˫Vx&?c~B]{`G*V^_,y$Z66e)}Ϛ/϶to*e:N}O/_ouPOmWe!WP}/TKam"S\,m^j$d3E ֻvcv_hh.8duHV  D 4MS\V nan7F} Q qm( `כ{M붿d梲jwKXژͳ+*r]}]&ŎfUӷΩ~L0~tj))Pw,]=8hcnifLU<2Mo8t;Ϸzq]9P{cSd\;nMrz孉s!{tـ=eo(ќ>1 0E>˯eur 5تB>_6;T{ȱŅDnRQ^9*6Țӽ B>TEVVn-bqTu-`SjB2fpaWHpQv2ݪ1 s>+*QAT<"QVJ\nUդi.t!V\+Lܦoi^3{'Tn+M OH~lAKdgk~)}Dn'2SU"nD:od6nVޘ~AyQ~^R R)c6Ud]fLz.Aꑉm<|i~g+jq*s-!WnyX$ bEήۓw2kCǭc9_l?\8cxT}k ۇ$we- 7u5}falE#ݕ8H`By}|9r~?^hRQ"3\;{Pड़ |DbNN6Sp˙d!muY64:HGvfƐ[clD w󀖺N1DmWb̥ [ *o5%rtY*:;ۇ5;h어n 㱰66%FUܥ=C u]r>M:C1ݾ|_Ć {Eش\( 5vxC}ɂ_zb4T\Ǯ˹&[ios]k)t-ϞEh07~ -𜡖k$S &ĊH)ZVi+EH !^x7bnwϛ=R,a;MP$k/:ҞM҂bH#l7Q1Y#28Oakb ʥ*zi/Z'MV3α Ja7EUݏ^I޶y\ڣcrB߉rsrɰ&.@}pX\ؼ:bNht1kq -/j2Ck&`Ni?E,"/PLfh ]2Zk=BKv0Ƣ@~Ɓjj ,{bKAе wDk'O)bȴGV:7洉T - {a 5hpӷP|N{=^T2__ uh0AD5+bCfٓ8}1#\NtLIUnwcK4jݩ -O&:ZS=EǐB)WyBzif@sJawc:tϐ 6'I? r=o?ʊTOƋ[UGnɩ-;Vu>6#ٔ5Lj#CIpW{JA -gܰgBd^r-WK^P5N}Z@{k1Tʔaɬ"צam0-[rzbeiAPMg[8mljh3V\⩻iX#){#L5jLx .P4v|Z,(tqqW_Ѥ#Pחk%chi~ 'l`]Ëg?BǤGjm/[*+dwMV+[-Pz>*[}jÑ-gӡ)9[th TI@"bA.rNkJaWT^l}ְy240.՗u+4*uYM˷Ȥ~ΞuTog>&5N_$D0n\[彣 `\K(08ƯX20R*B^w5XːR+.%@0ϥxWLCVJ{0 B μrþ]xX1Uΐߡ"u}U,7F8>9>Uמ Cm1q%_`;d3"o'Q>^ϲ%?2cR~ %fY{1PB{bwwW[^t3O%{.C7,+4g`J2ibqtAxv-Oٴ5ˡFw]1\`KZvm|TMj̨< ]w$t3 s`cP81j!mYpQq/\zH{Aӊ7rHr5G]{@ԓ -i&PkLH^ZHlG\UM[_F4\p>)ssi@.EHlUxsyFy/ۺzA%[-xfQl7iGt?c4(^C(=Uؖ^뀮.u]9۩^\iW/Mk!z&ӖΪX zvXk?~R.FڪHAv䧭PNf'X֙}"ϐ:p;XY=D `nWeaם=mFO鷣1k4+xjqXJGAk_q̛R 5NswBxmVp2I^CDTF2KH(_}SBU*=Pzb{LhbVqdv߸;M=񺹦kj2h1ϼOjE8|/wvR=fQ#ǹrM-64cLYa|e1G_+iI{{]ZN;*m7O?Ʊ#xU>sYq]lǔ|94]h v!>Hb4.{v+doEsKM&gxm$vȷrR#:NWY6Ua*t(QY'l(k<A%̵=YAv& rf^lRW8Pdqw^c -nOӤU:,'Z= -8B4,^Zqervr;K='_pjc.z #8)|d'|Uy'z#v8{ng~TrmwHlhv?Wݩ?0p{OڹuDxNsvɪi] Sd J=']tn{bD,)˼f7^ݩcs~;bЛjͣekZy'jmlYTVF9mV;(_SW NL̷ZQbnOm\N_{cjeze~SҺT#t %cxbm{Ms2o13\Ƌ#P$%NƑG>zOA-'jTnZW~s!ڕG], HjY씤۪zΊnqn8$m3:)T8? ̘1qK0ݸJs昧tRrh@^jpm%~ "vh3trb}bdxL-0C]cW5k0؊ ->J'Z;2^ f䪃H$*.p-т|Y)PֆN;y/(j۾@H8_k8s: wh]5D9=~&mI~A$]P^żlmqs$t푑rb`& QEbFy' Su2xS@^| -9Xޑx1]Z]TR\R#A]'n66B^I.^jº&fo9rg`1Z:)=ԏE%[b]9ZzSa]"18Fo T=nKVudRs1 7wAц4؜'1 -omG -"g&z2ֺ$֏{Z}҄M#:o\PdGMcb|!{v,'PW:6|nU$R@weÌ2g2j]8!ZO~t4ˡ`Q;("n ;Tr&Tݛ3[%fg7nei4ژ̽YJdGtwϮ/Mb7w8( I䎿_l6*۠Ms3X&J|UQ7\n=ٓ:MߕϣMHҝf&i!j0|wPoY]9٢V&#%>pɣ[ ޕI[Qk& -]VҮ#ijF t1JuH!=ah^#zfb3QLP`cG \9ۄTZn+=~>R\#t䟖l+^ずFOenkОJtڛ<%ߎ.Y٩ӷj^NmHhE)٫ox眫k1q]0Vl/_ ZYІEEYX֌T(ٹ4Vuzz4(6y6b3qqVG} w>݂X[j+XȰZzDݚ{KA:-[`I6safiJ6Ɨggh+f@6,1 '~Vۿ5Xku3*Fhn$/Q[4Ό+2ԭU{,آari{תn){`uP!`\rcaXKzt߮љčYvZcIzmZ4G JT3! H,1lQML ?Gr%@ -_}vVLSϒTY?]%=^Ysh][(n@aC1N~h_v̯/~?2ҿM۞p~JnY/C?$̩z'ʫ h.Zk_jKi,ir-7>H2~%.zNH<$Ҝp j/p*SZ -3*+J{r9in9T7:5E> [w`IW>3JJe`bqzu]n7ң•hK (M*33gܥV?ڦߵ2h'lm>x``Z3휗O11tCrSQdLUpxn -# [4{9,Ah3 <0#k#4_~΍4&S1Z&d[=s>u}Lvz/HCG2Ypt5zz3N](9C5W;e*tSV'f|i[ -.&y|w ز#X`֩<ڑuy4uӈ ?zu駳&iA1:ͼe}j=(bT/SY'Kd@ˤ⻹?:|iz%ʠUO`:pXS^o]`}Z]˝n\irF*vr'@BUtK mg!3pXVLR_ձԂQOr,]QƱ fo:ܮkrNnB}jXnY5s c>1\xSs3; -v^Lq"evʋݔXYIvO7~z95QXjOV3fmqd+>柽zzSqŽģ,k5NU~^@(=j"0V#}? -Zc5g;]N7py%۪h[dt91)7X;<+y[}r9Qzt|RNSi_'۽cSi>x"/(/ś6z!0w[ޮڛ*@ߡ=ˌiH5_-~3K*=Iں4!gaG 㳝_Bwj`vV@ -쪀(۷k:4Mkvw -q٨+i#vNqdU {6ʥ,4K*ϗZq2=w;llxK73VwӈrNͧYX(5DgtS~ZN5Rσ+s)lgFj@0F@XsB؈8NB}O698ůS1 yL&!AK_fZ{Sx꺶Б`7.ؐޫ/A[z|mĖAG|,{ls,W[mr{N܉F4:[ ~A5OJ\u4y^l!ne*)a/ȱ[Gm?'2_j8m30y- ==z&|#Ϛ\>9-rv<ln!K܀Ss:{ʥ#ZTr[w$ -?ҧuV|rR>wN{:tJmLU"8daYJ-׭ߋoV5m/TnHaQ;4&>":6Tѷt[Rx%j7@QlJd|h%b ;Zkk"}3FphIJ\L]m^Y3qgXr<ǔ%m1j"W %oT'fn)gfA6jARC8¼5dTAq^ m9s3|Kx PhJ'1~Y.r ; -!ǐ@kl7*Ϭ;p-:>2ՠGE`3 #?zLaۧ!f@+O _dV^QC4m/w;mY.%=$cmSVY -̑<[՜ǴHӸt9~`(sV*⎺ Wo>ZUE?`6^bN/;2k?c';I7MPPv,Rd{Yarl_J %)^g6EZ^\ajz;nT?^"^MĴb9=hxUge]7)>RNaww.֣="3v]lao p5gI -bh6d #*8>Y?m2\?K/E_[ucoN wA[zpլ!Z:f\l62!Zfxo݊3P,p,'y[ɉT_MJ)ԣ ¤t !AqXHgF˺[=$ac z%{‚7XA9~tE8?x]ȆըX}ۖ5S>e˘QUxGy944u̢ѥ떫]ЍZܸ'' _oΗj9]QAR%*U{At9>7fn+s5~)!lǪ3^mG@t:1:E}Osb>2;+=|q<<q6+hbVא6ÀgrXwJ-TJe/"پPAp&+wת t `ji N00eMo gV_rV+%{=~PěØ(*VoH$msf,Uۜl}`ַGA)SaZ6d]]mLjWh,ia]md ocT2iT^cn-ŀ$ʪ/tނNK7Mf_v>mWLNt1+qH״g/O3G_CU 5ΤF@`v;J,4? 㐇K;ʔq$eO@EΗ|al"QҲYd%:&w#fو|$zG͘{jYx(\<- H @ԩ>D^<FmwZ H06_Ef'>eŮdd=<IYl6' |˗h#:fr(} n@BNP9B]JYSڈ\k .nXڷGEVXnI>;w.;_&'އV-cPc RPrkjǭVA(o˗k+9C]g5d8Z+)o5a- Ҿ'슯n *q] -nU,Sbhq>82hIjG$lbR4d^CΆ|673wւ7/hq\J C…ڒ]s"5mgvOޗ٪ y)TsᱝE~n6M)n`lꔶ⬖o6|mC+q=/zפXQF +.zZGF {5;S0k - -B> Ċ$>C! 6",֊™#3*G'R '.17:^lA\&7-ryxRV|bCdvۖHdQٻ䵲g>g<X{?NSK&"4V+gg+A{cɫX}sݳYT2TmAI3i -3!ee8Y,[k410 5s^9XS#xuD%|:$BByn~}a5bJ<3PXJ\%AM-zFDx*w-Ƹq!͍vM:7ްJ@pXuhKjOpq~gc))]{_ePy:( |HcPɪP[J쥕/YAPD$Z$]V^ e /ʭ/ue?W=XտcbHj):-$B0(e$=WjuLWs7*}t5+1Ezfz{(0e6Zp(% vVn8SU|=W eS=#yzy/wº,8zSS_ЪjSUaǔ eڙ0#e*fwFKSw2p%ۚ0rRYFF.l|ct#F $@iI؞ߙ<&%"~ȟB5^ ! l-XVvȵk>&o3ϳ(]akYﱮ<$ ^{ 佗 ~t랙pUUfFF2}(8 s``Хrte*vGϬ4B>qVOMM[1/8coH+?OCmbWވE[*3TX8^o3hS4u4%Lo"xk5W;S7#3ҽKP #/m@`r8bcHҹMr XxC;&)p Vԑnwa; 'kz/(T>~4oyچDݖ\q 2pk[޴nid7\E9qOW͊cݞǰ=WXែ!6˖oH5{k # ÷FA^Q~PクF3=SW X߿5Ѵz<"HM?6:+8{c@ogo 1xA^2U*`4_l 8e_J En1 ʒDX#Fg$ -=T/GQH]hQӂ*yR#LyW.27F"!&Ÿ(J$HB̔'oE?M!,Ť -dncb%gc8t§N{'aԗNuY{ܰwt& %^g-P~" *^xDT!Q +kamu]^*Yˤ @Oܓ5ço:f}^:otk-v{&?w(4j Nn4mr0a؂V}<eK}|w+Y&?n\w'ɤXw!yuow#ըн-ã3fd͟6`Ot_z}_8Xs߱>mh͉2Ruuoj?D\j0&"vTe*ɻ;2T w]UG6##Ώ"U)r̉1N M lT&x9\zUCw>oTvVdW+sa ;)A-il^T 1^gAm(FS@CewR5_ZYK5ۭ|nd}D%'Qz1<Iʧ2ǻz)(?OW%ق0I^SjPHAG@?1AvBGU7mvU`z>UNvٗ%_{.JBɥ -e]3!qߧM4],~6K75Ko͚׭_/ //P~ 339ӣc۱Q٭dk՗e0nq1 cnBZ ֺ[n>Zt:V;!BIO n27Ιk5߬3J_:Jػ : -)ڃyQb~Y=w'a?;>FN.hYf-2Sm: ?ž>UGV0PT&?`X+˽rX\,$]˲?9WW?%`9Եh@+@V%)UY_~^?hvOǤFKVKaZ*Ry8+|ěVr"9$&+Xj얶M7z-J|>unɶP8\ )w*uTQ!lExD_χ:V3wl~bXVw QFFTX*}RBk&u mfကF]j] $?иT~vhkaлKjwL,{zEX 7[iʹk~<]qFz]X}PNJ2q)75Whq_ݮHK;&oZZ[{hme!8\ x\6blC"ݚV ,2x}m3BG(4ʊkq6U@d -Mb,}Gc{v~OU 7Xހ޵FaQ2؋G~a4A+pO 6OeZ|_"Y-مXA86z4RNQŪ[{oz*mެoSo6Yw"/~A'"Nv{x=cL;Mzh& ]x~]6awyx=o&/_je槩6G|T.Ǩ窐ՁT "ᵩY;g/¥Mn:ANNZ<~ Pn(Xޯ?pJ㴬AF,9Iuf;Tcr݌-Wؽ=]vٹ5bn+SS&U$| _9^YJզ(Ș|k|VWFUؤq(beepdFygeQ<Ūux!qzHXrQfBou*8j#%5UhM*F; !Ξ.,Gm~A*ԜPnW-]= G.\wk5<전ǽOގbh&`e^U63aイ:-aR\sD&\ YYN7e*o?<߫Za*gyf]]X%VN?'Ə18>~r̖W xm@B)'O ͻKߓ?y_jݴM}U)˺AOEU2RkRb {BQb$0+P ghz(3Q.R 4TIhcw\W/t? -^.c+&}s3J/^E9kXwe#mػ@'nyR$kP%˖u)VNZ[Z+b<&85ڏ[,yJ3 }W鲅]XcɛcRa>5.zS}2ګl#ޗduPEyؙ -}| *͗gweXm)\?;HWHdJkpzϐs+ h[&O]+EȪ[8o_V!~@䡌)gu&ک,|+mpK򅾠u#E%1{͵/e9׬_]a6)Nb.7t Uvle=h!^uHwI}w٪eZo*[S֖5qO.Z^Y-i>\+gaoC#3kmNZew&jv:4F;ʏ,$]L_f C|?%}r->q޸v<ˑ ^in;;6C~:ͳ'V s[ؤucW~ԘFۨK?]A~tOnYPs4&"^/ D[$r ${5cמTM _k!1gb*{CM큷?!Bā62R˗U ګj:{Y͈itBO"P]SK|DzG%*[/o ]d},n`p]DlrPm08d IC6%i&QozB砺**3:dO -+z?6ֲfBT^ͣwdE:?Y$UW֔i3+夰d-C(ߨm.#kxaE;{cZn6qA cX'>SWKKu2=DqZ(V8Z 2J+&1Õl㙧c>uV=xnzm S4XUp9Ts'ɯ]cQnik`}o=lNgAF&r7xcӞh_ۜiӻ0: ὦ%~^y)TR|/ ՗̾W̅?~Y'r4WрO%;U4RՆ6R)ev׾ּݴ1R)>rVLo߽èʙj9=fZn (/yAZ'$k6efmM[l,fACwn0x{i#-"yƋkb>kH}S`iKfLPk*ts |6Vট 7\0G(Ĝkuܚ,5R!kZ -t73};6kx ]G?E膈' 4ZSQ̃'ateZE"#7jtОl5ynسǼܭCjӑ 7}͑hV6s{tT1󽽶faie<*amfYK}E1RuoSƧƄNضSc\xnvS0 hk9a -eGtk,guK;1z2UgʒzKmFMyw(ծUs'+=%luPu\o'>+k}k^#OExZKگ9MKa]BXe0Ap\q;k>_kNOhM5D[KDysl{AO|n}=J `ҝo {R? o ?*+c^iNܖrc({IǮ[3 -xzh?km/,.T.dZЌauc~KѧoҤx7ؼD0xTr&:YzKo+(̉Wml axlW֠ڑPE3;pa&Jp?5^7c;8tO8SsmG Ɵ s-}Z -+YTWsqsL栟ԝ3ok`S.NOvt<\M;I|}-SAa=gfYeF]p@Ӧp3&~ݦwRmZ=^gFu{[,X L~I` ҟ<3N4]]1K`]WLWB&n~((_rZ_E䴕A]ygܥx^'3?'ڏQRz%|Gߝc.`$ɦ׹鵝^iքfeB5ME5/,;UvjoǘWWRL?ןdAAP 4 `tv_ml8RZ\` Qnn Om|b4^+G&)[}v0z'S{steKê7DveFxV|v,ŴJ~Piox 9IeM -묁V/@l@*1ncxqm]?rtIJ{WnU%+gQgSn3l-~=FG#I)ܞ]Oֵҋdr w564Jgi;m`)l; j5xKȽf?;ҧ{QڍG -*RAݑ|nMUW^tHtCq)I[=Ĵ¾ S|nCxīț>]6κI^xP3&es:o -lO/㲭,ug;v>Vр9 \}J]xQ #3H/L;)+b'بMN6d)% YAI٧3ҫ /ıd8<+VȆxQQ'by6u(' lX[v{o6Z1@@Fee! æu P)L-w-s =`K.ㅾ7 C@v VE]nla\袣HZ#:$J(CWh5W(w ns)P⬔R[3fĤ!:Y.濑V';ZRB +^7IC_eXe)6DJ+Z1YHY9~s Yz GwM2[?%>ƀ";Hn҃^VL/ɮ} v~ƸRCMЄKې٩kC4 -ClngkʙtdXH˧m4BY$c|J,͙5fg;IedѪ]1yyR8Q'miIEgƹJfPjFw"-zj;2[^c=dg$@_{q=sQOI$w4l?3xJ19{7+ySjN7Բ6|P}'b>rRWSOMԺ* |+mPIjT[RS~*:Mٺz:wYW7c6㧁sG71X+}[w*nQ8/FRYEMRd_a|kBAۛtuc驒mQ8~\p m+g%ʆyK)?1%#]r?@}l@d9\I PۅHID7NJuP5WdQQs|R|=j&PH/%VݚcOt?`#/KzV.Qy륎P>14n३ - OWY׀ki 0ҰMHdw̻Wk">A#G^/좩lͮT0|@ZWl .Tī9u] O;R.fPot(6.R:(KN VYsϵ6eN!8듆֡ZU.\ .X gPLuTUaݶtd.=0z̙zނWp~\a9|Ufzxl eM5$h- -oxQxD5nWɞ^r0Z޻,^dˏ+ $d6*[\=Vػ㩝X{h ]\p%f;ndxL~J0C1:+Ϟk][?še Ӫޘs)//I,M瘅1J9 }׾L>pJR;TvfI}3R,T?y溍Fߡ3{xRszW/-Y\/x!v=xjW^OAĘ_Z=6aGltqPs)_s+8}{C;8<xfЙ۟ӄz4bt௖J 蓽JTR^yXg u!c@lEj1씈!nQnkJ^!Ef+ -`˴'=խ9ߵ|"C\m8XmǗQ~lmo?^+5k7f0ŻjK۞8˘Ѻqͳ\]!߁\~l3GjrըZJ -U.ݳ{g?m/n -Wdžji5ޢ[sk,_Ƹ|fP's>N$sgΪTwU7-8&jo&X_l고v"96@ wwW/l#NG`1>!`obsVul$^NjϹ(MtpOqcd]+gu9 mZ#2#ltGU`Va-\v5n\^%htn j"^fb,%沱hNs~ -CjI||K2[=ٿ,'msS~B}.p+ҚBhNݔV4\NCf=Z Ut['j4^HqTјn_gKV85@%2W/͔\-,{ҏA\`^m!Wm􆼽[ŻW |kh/ makKu Wp3~A"/ߕT9ڰVhn咽ee[;$ga# kI)4wݝtw=H0jd_m" Wo6TVn&vub-WOŒOP?ѴJT J|Gvⵇ"C#!w-M<+[Eu=4JnXޑ7yԚ*7*w:{2,t ؗgɼ4şo#t]=F(}Eb ^ yƎ#sL+9! J؝ה9:ⰲk6Z&g_~YL3hxJqDC_]yuZdf@yjI.,Vr|!$AM);.h(Wv==mfZI$38&>UE9Q, -mu^޷1jPQ;tbFj9P$#TĿok/ED_c=ڴb}ooHܑe Wꆥڔ'R2;>QuoZᮽZunز un쟊cfobœ@ -|;46HkǡuևKcn=7:yٙY/! oaRmƚKWh^#2#a'GLv([_ҝDA4> -b.*]^0% kh&2iPE iC;Ϙ5 -lRb>'4襵3,s 67l5f)Q8 &:'-/E;пkGֵ[dHͅ!.7MΆnʑʞOn3),z<\%Žtg-/ժF0KR+F#bb_`ɻ*xos`Kv=j,JOģ6*}K_UݿeFGWF܆nz z1'sh+ꎙI˛D?#%UZҚ̱5G4hOIg򗟤]kFE7mĵvdQB,g9.a>&zQ>\]w;6NY:*Qۿ";$77)'Wޣae7Ɵ?wñL-6 wN-bI^,?,2գ;]ϵpJ >K .X::f?16^9OU::< 󨯧Ұ~#e2M>\iKe <[f>h} -8u1R݋lHa8KGtdk},+v9=~`t5W)z}pnf&@-[}$%&)+VD^ TZ-zm=UC'hcJؐj@jlHTľ{BUQHx<[?%4߃v;n`\o5+Qj174^-S-ûfaFuZ< -RQ=n 7..gQwMeRL9ˋc%dStj:3tvMoҭX|tx_ q`{FZM -7=]ɣ4"oj -&'[d7Ergj~c$<@%/UO֯nO9?-cΛw:SZNs nW/1hw _ӐP(͏[MDO9T\q ]iu6*nzU;wn*验sMhpRjX+Y K@'Ƿux\VoRTKE|ʅF^|wz(1⼉QWSe /)ȯ*h*JxoɼaKT)m;lV1mYp\?)<ǮzA1WKZ#QhtxA:TrE+-!BdNveCLcwA|8 endstream endobj 43 0 obj <>stream -ֱS|c^G#򁼳>d(s*@gYE&mm|Bk^;1'Zd^zɴ<528]pti[JϠoUq0Ҵ{k#PUՃJ,ώ he)Մ'k7$uF:?C-vf־ݧ0]5ieA-땾^ -ia,#}eʹ܊\YEamn9K-Nr8F@8n KCf1Y0)<h]C£giT}n#vKn7Is˶ˆKݏ=91K \_?hߜuMu36<~ۖ*^][,^C0cEb0a=I[þ1gi7"AID?/miwo 1Prc^lf-^y:|YSglsGHΡ2噬j:T\"| =:0{[G<Ea-:Wqhf|W9`t&Jt%^ :z41؞6i/a֒c6m6vp#e_0:0 .{ElT` yCslNDu!7Cu}J*հ>^4og -Yۧ'ǢAKh֋[98[f֫g[hKi{/A5a -dZnГ]p^{T~(q^>a*[<ӷrA|@'bI7DD֢l?4k+'˾uuKNX?V- +Zu;|s-Ea*_R ~qq͡ ߋ֘j,h)]Kp$p%s2Yp(f@[pc?_I8)̻Te6=G]wy")|(!pqzł(9J^vw HWn&_PPbw<ߣh&s@/-Z${Ѓ6VΘiht CwTGJ d-$Qv[Zll@JAJd+'w[oF_̋/3i ;B+W= &Y -[@ Zx~[ ow1,7|jf#'W:׻!>f:ڳd:,0J-Af[kUVjmZax\5Rn@YY&7S(C2NP*UvY1 nrUy sh:!c_P3J A -&[^:(}k6`£*wZcjۚ1 Ca0a1l˻jy(EX}y :놗ߣ.)7ӏoVx>+d`3Tk 'Z=?'#/zYZ3ttطN`NO#I[c^M+>4tL7`!}/\@n6ݗ\Qo!gM=w{0 }O&\y -Fʃ):$bvpk yGެ F5 |.A.q -r9>z4>[/w.VIF1ӽܨٌk5tj_Lmiʋ4H_ƱƝKR}ؤ=4A=Ȏ$P-dq22?@=xkV&aɖ-c^UM^c- vΫ>tL -#nZ>]]XZƹk#tFzJ܂F޹y{Oh!nUBRlki_a>fZZ"i2 ޑH_l}r5`m=On~g$9YM+) 煜L񶏔r$4˽ #x5~w6z?ՉQ^kV@=;o.A0C@i$?=P>ivp|]ltf8|<C;xG EUyXWl>PUȾS'-,4:Iz7%f_̂}k*qU7lꤢp6C(RAPCVl7tH-Qr5 @l.~x -+w\eҌ 5o5Oz:1k_WΥhu+Yq}zm,DIy@gk\)D?5V7_VP_[{+RY79M~ocrFgf hOFypD5sZuVgQjSښTyYFh\&yƻm;ޛu_E8kEczڧW҉)QAYT`dH)|=ssEK1/3F׭?.ڤbǼBi q84ZᕗLHВUtStRGcoCz,Dqg(?_Od})wf -L8g1|"8 Qr-cg*~2nzյZ6R@&ح\A/1#<[EEׯ.I2zӶxZW9.70+ިf 쪥o3^75LCΥm򅷇τ5(6FR>PV3<5LF#b -/*7$W0"0,MO?5)x4q]<PGwӣ1fE€:6aFbh=S|˴kby^3jAխ!m% >zQ[L@'A.ze_CZLW[OW sRzෳ>Uz-l>R2X'N4kSw+?~|.@g =@릅j:0Oߣjwlz34>Dd@_]rP;s zKڤxfyE|Xce5Lo/$]hvDwM|޶WԲ6-ڵ+;|X9lPRЬknkdf'ڨ9}=:֤FYE: ]=$ N*Vy=PqΤY@ptGV֛ >YDk`jݶmJ.5п9N>YeP'ӓHkVl/g"Pw-L4TVC-zH,c|^6ρuz9קu`d(vF<ޯם0H\Ǝ #oɴ׃+Z]V foRj#kߙ;"fiޓ ٮD%Q})UT)􁁣YD$߹ɨFdl4#_<s\LB})A:u3 *4no̓z6Ĭ_ެ[+uqqf,쒉,~bួI۵;::b_bNw7k &6$LnVD-S,JgPoUѼYnd.R~y<2x int%N{=)/7#dݿ'yrXBoa|ag5<*o'~;Cwr3yU:l -meGM+#sl{ko# 4hMsד'(5X .6[W7KQc̾0r()/<)Sl>oSAn[8ϻ՛(Iu~2:kPW?3k6x69FBc@y7c.}>Ql?L@vleX[[wSmέaUA{654 }܍\6秲aC'j>v)ȍ߮BϑWH<̕֍vɀN X~ -k܏Q9<{s)޳* TvOz1EpRù}fN/>:#^*Masg! 4>}w[]s+iHF:Z}LCR]4NNV -WVC Rk%!+$?5R/(ӟ%+v~7qnjJC `~jW7tOV=6bG7:9ĕAI[cg{Xj-:'YO#<Ǫ翕ةs"[=dqj c\SJofW[﹋2U*< Muְ}JF׊tsiDٶN46(oFg~e@W+9똻;{ai"&āSQq"yYEL aPn>=+,$̻(<^tjnO#lNK;aeF&0_Hܲq+^2k׽#q+)VU)%;nbu8:Aov'P#wa˘qiVUjQ-J~o\5i_ uɫ<1ʫǚqn 6uD\}fTTpn%5{=cX +&jHIL;)V1|t|U;YTdն33-9p;PoCE|8xL1n1q{w06w< -qj1ްA<bdg{ [KÔln ->CDh^_zSc"Ey:ROww$Q*}i8TK2hsBJ dq=9kyfJ,KLE {vdsZ1X h]b0~E_?۟KU!"dUM7;7@.rwao8UG3I<#*&7a$lx“W۶JBWmDv(lb%$ÖO?1څ*̇Ӗo- P#+/P}_zn֢ebGV7Jv)oP;^Y43mEsVٰD4=#lF( oʮ a˞M^XF ݃ wXeJ$e7 ^K%<#k{V]5{Lob]'F~ԮRAJ֫s!!tRSWjXnSŋ%9}9V4- LnLdR(NW}X ڷCYn --۩͚dx وƤ4tib%~LSNYf)c_psy0NHV6t %ٙ'Uy^^X^}ÛʥnTiB⅛kڬQZh2Ziq@VH^~]J1}:3}}EKH/&W欈}CG2cK> -޺'SzCnm~PJR TxP.)e ~/O x+Z]Z]}U^K7{l"( |ҶHY wsjLUY. ڸMoA^hBܛ!6RT:t'r&Uc(SzAѶ5r1`4-?h6N )}s瀛R{Wb4U5$12 OIֳG0બ"U_>;x"nu]ky"1y1shlZ)6jZe7z9eiOK,¿~~*4:Mciδĺ**:\1]<Zn^uNZn BEJ+PSwʒrU8+ @($\FA]zsd>tl-}he0qyj˩=}r S ʯihQ \:'[tzK?Зps fT#vwNHUY;ͼ)6/C-)ʎ߉߉Z|2ʾ^Phtv[H]џZCp]O |o)NZ1и Y0$|^hhC,P=òQ2{;KlKnU+',]/|GD:44Ӌeni OX۪6p@Bm~-.ĪΔ=ؾU>wK|!*\< -g{+sh3?HsFLhp%&Lu.]IDzLL^Jt@_y،٣H;'x+C݇̌RzWF [o|zyEfEmZS}rfTzߑ\]ߚ" -D؃s1f3OQ6nmQN_9 Wj;}r}OSh|! Bf| Nژ2Sd,s7WRDT>tWJ;UT%%{)曱*M>grEqÍoOGOMy3N n !+#`96 ddcTfTseO=.|5):jyl -b4p&#o] -pH^Еyc;"ѡo-ͪۓG c;K`vnS5SQ wKҖa_f^d& .k!jΏ`ءy64źSSe+Nl6t0xAwgKjs:,ޖ} u~~"~ⲒXtSW=*SvN2 мsn̓QWW\imkB AMMk3lJN1Y3*yq<fd.Qߦ1yvӅ_Kݿ6O+<"\H,.4[ǧjt!nW]V.{8M'ƻ F˓38Ҕ0)5jﹿp˧/nï㵹ރKE#lT3L+fa" sϡes"qG4OpMc3J6b62N⹚;q~ \d/í5%?xt`u[oj!ÚYm|?uR(RaWJ:WDP6ckU:nߦ؂8FKW[7_YO{7xΝҬu [{RLT~R95lc%u*HE9n~5`H׺0܍ad6ktwAPMLk-,bjYez3>hpJ34f憵mmcUU}]=ܮ|h _r)hg,b@ -Sj /WvУYY2b*Yt'4Y=c?'Ћ]ڡ0&ykNhoQW܏aiNx.W0,pUdй}I`ea:5=;"A,Kk}JRrzT$cE29p -Sś/& @99zH`D2A´Jކl 2~iGz<>T}[4TPG(^5@\q*qH8AC:9 -8ҹ#3&z_D ~* ,MBE?-64F,0ܖSmߊݚ'g$kp,/#2 ;Hsb0o ~omvOwnh[N:ڟzr_#=`][Vn3+]PȋP \67NxQ;V$ibj xI2qizfw?d#_ϫ:'wqIkAuęa uf:N$$X0:&!>M5 M{ER3|@8[r^%9Zц/7Hٌ?-tWu3 Tq'탲$mJ14\ZJqy=t'ͣ'kK3w0, -ME $+sm+ϱ2!|Aw+RPR.vW~S/N#'a=85QДw4auTjO:oZ lWEE.udAƬna [UnYfZ%͏7Ӽ*Dn>KL -$ڼO@ZsخHibߒʶ;$['1&,xF"ѭo\g `ڐm /[Uˏ-2T>b N$n5Ծ) -3?H _uʕ/Z K[ʪ".oLJ(Qns?өg$OWRv>^tkEّJ+Y&\~2hv?(ӯQEС9W G"Q -?gt= IfӺ*4[ ~W"g͋jJڮg 6:nIG~*Ê9mJ=\h_L.l+c[R{؋p|xJ,oCqcq8ϛ7? -_9/=mJPUFV_V}Y[y]mMV .l{z}kr -5H9*(f#n4햢Wp(&E4To;qexԏiZMpgȱohwt,1)SYwq-uDNcOW*o]*O@O6OPV+sCjBPFu;ytwT"E@z]{%̜ch:@C9v#n?|0o ȻKx 1(2T.}=}#oWq0VH,3bCO#elF?/b̺fY,x-(SKY`FԜl|icnQ'XG$j[0y .TMRvx%X|Pd)˚݋qip -3DQuar7#Ì[/bo.FƽVYTPJJEZH=E($Fq>R8 C;mU#_`gO$8zj~\+}`1ۺJڦ=UWgVSh|jyʭ+D9|Pҡe7t 'EՁ팁k^>:BvixV+ʇ9-K]T;Z]#Jٽ@ʚ1ՁΗX2~M֞t ->`~Y\(R %^yDӊ/PUՊ?+Ly=\Map㮦=Iu%S/:9 ook;?M &':Nב1G~{IZG48**?̛^ЄJ.N,>P#`3\GCΕ_YiۤX9q*!}pyA~j;%þ Ny{>= -.=%єn/n6;ź>ۇ&O -z6ZcY۳aw\QnJLyyvv{4od4“",A/0Y :^Zo 0/7~u>[UvRdRd,h>o]uBf9x};"[t's,\cB#[ -|/(MͣktYs=PU^i$P -"I0{^d7t[k]O j/w 9>Jju5DcEjVnt*t|A%Ёl\ -+>rs݉vV>#ǐ?jFxsfk;^!q'&_ qr gvf-tYӚ?Ͼ}ZKà kdG6DC%99T IJ(%EbVIRGfbFP#Hͪl-1Tծk6#ΨXgQ.ʽp c4]i-Ctv$g&7w8m1DӶ-1Zz] sSi; oqmy?Pk@۵p? znyoBl;\{AmEZ% ?S0?+?ol[YH=c-}F@Ԓ,d;7FjczPx6ӞHw]bXe``0^ۖ8-|cHd233Le:e|'*5cFG!ZUZ-HCB -2 RFF- -K~ݾ o[kط [xQGMMʯ0$Uւ4Hd]kk3V.4MP)&;KFP8 k َJ%"1f9 tC#FN8ºZr#PzzAm{1zXf,M-3CzW3#ReH#3vLп!sV:heҥ QUzGfRh6afM+~z/] -e3T}D]K=Cʉ`?y'2e f%q;UP@uՠq(?v}b-4!s"&4n< tL뛅CMsEyu+G췝~N?o\6Z!aYc ģ'=p̛hv RS{`3¾~SQPt(+_;A|w=X;{ٰ韕ltTzwM$l'}F%dhoɈ3|,V93iG.6F{0ͱQӃ槌!1v`q+U)ɸOZa5U0|xRдOtoE-oB =^\Q?+ -oẾ2ng%jfe=䇵߫G >eG]VlyWVm@P&u;Y]} 'LQsgC}r-fud8ֱr kQq -J=^ 8(N2R;H[9aq,[L#>)ß ުmµu&^ֻe{͚67aз3{uڎA@|IkhJZ 2r [N~GMB2j-RntVemhmF}Wg\nΜR* ~ֆljrƑj?@86y>uJ n(R'mW{:lgABۏoz]"/ V@2*M -Z̘|O!`/y)<)5YcBxTU9{1#ᕶϜ'', -gj8YZrh6^T#G#ahM_r蹲Ɇ8j}~Fӽb^~qA:Ư'*شu5[?LH,l!? +oyԔ?Ws䦿 +3gӥh%_JSuM:4+@l-Dw'U0@ܡjP9:Y"\ ujvUSW m9X|w&ڏT݉+#NmX7tMYӈOӎZUD-`籱*NUV/gcQ$~1sA(!];ʹiGݟ9w2{:rvRtXE',Z5\g(%cwk8O{9uXfr36m۷Vsg3}=ݵU]٨tZ -@i:lMt kz|:Э|;b ?/D8W KlҮSKRp._ǐM@&&ׄz*RHFBX;#9}j;8޲f{71r>rK+6Uڗ -9J27 yp*q "6ݲQڅ:{FщibzwШtX~y s -. ~V|3@^'&݆|~+r=Zκ9ӧhl.7/tvX)|X{"Of=U "!,ӎM C͛x˭҃.IЅ=)"\ "„~;#6++v8eyLcCmĊ9'S{¤3_Ҝ<qAl&g/mLo -Z*+n}\w>60&٤2cRyF{|sUh/vA nb63#Uk"Xx),gTn" 5 ^7oUAdh<2V=r$[$nO';ҽL0|B$To'?YSޠsH0%[-DIXkBNiH~ENKTsދr1ctB_ -SP[_x |RZtf7>I4( “LAd6#kݽrl\YZOTzPꬹCڀ$FzV< QmɄs|7BAB\M&jϭ<:P!o -QF]A;hn|0fG{Bk9Q"<\U[yJ' 4[:+`x:t脏n 1S6 Q&QW8K|&zAmTR~Sl8=fr9@Y\Dx=R+7K`~ܿpFn *j Vcq;sQ ebᝍGM">Urf_;QH٦IE8A\6["3{5м"E'YV~g\L]SC<3>1(BQW7 DĞ(" u;EEnT&w T9O+Ur8~wW~C5_r=,5}st YBk{ @5ź؅TAgV˸)yYvIU$0s\_Iuhk~C{Kɻ-|=nmR:sc_I[GSƥOs;1 b;-Ɵg!6*g 5 $ ij')n7Z7ʩ18{V.G)s⣝eۯ:|o,xCbTcx.$䨌/omSǭUA7 ,S(qkԫ 7%ҚKI8)0Sv4wp|QٞG<Ɛ|&EH{ Cx3;ZM4K!Pyd| *No#M\В_e}sjN'ο2̯֯T%'l^ɘkQg x-94m[yO]Ȓf8ʈ%6{zA~H ѡV0C3t{u%Xbox物/FPG[[z괹n{љb+pmyQs懑 hlՋtj_ :dKGGP[î>h+ UGc47(F:/OG[*|Z`=n^VN1}'uNvJj [|Jz_dzTrp#wm${#IV2hnd8u8ԖFfځ> =DS zo`rǘv] U[C+q]QlXIἩ24ޗLKfRuFDXy4Mb技п2#&.2O鎾5EcŴL:3 5|3N6lKb~ЛYkV&73""Mٞ+u\bA̽Ʌ"z\<k.|T:,|r%__9{zޥ?Xj023oxůf hy5F #mSDҲ{6?[~ax_ZzNaH\22wV>׬ T[NwU C6ztJn_heu,%?4祓Ʌ*Q^OsձL}QyM[F^r߷J٥|]4-xlo@ɛSSH#TUC}L1C ,e\Cm|93CjUĪ;Rp3qJzU1$QkuȠsUp4M4N&3W5ג -Lܭ ]CG±}#7F[+:: =K6X绂:4mD.S[̓KZK9>˦VYgp}.Ξpx F.hlx@f)VFs9xۮr;ޫû -.w\on.˶tӺw1V:Š˗S>ArVQ 8Wo;y·GSMz>KQͨT;fM缏 ly]lSeHV4gApǕb۫88v)?5ܤ٧_jԄr܉МBU -bD#q7@N}4)a/{.7`9<k@ejg=TSxy=ltϋMmTLOV)nWXGߩ##d{ȥĸzT0ӵ/zGgog+}k {ok%;@/TػHR$SnOo6vn-NuGT9n]":ߝwٱVㅌ]VkHrnΫKOsF?Cd -+ 8clpļWKjRMiyfnVAtJ7`QӲu?X fqgQ핆0Z C/K(k!BUG7;w+PMݝ ?hڭ l׽h+_~l'޸9*b $:w+g_J67UF;ȱݛOV0ݎ#4yx5M 4`Ԫb.fΧi~ /1 -Fg Q`,Ypw.z#R~/P`^}y&9>-X[k՜Kغ;$vV7?34@z#xoItmle-fgSBәѫWv\k{m8u^MPɒ.;3 -+;TtNVF y؜:.rIz`pn;Iq<>G{dq'슛A;$^R1%dl_¯ט -@2mˢϰJ -MB5l1ղw\b< ~ ) -Ye(8E(Z,R7mq-p2 &EcKrn[?C>[1NʏHwȁB}]$(k('64|>MUn(JBcgRuF]d rAQ;8Ŧ4n]@z) ThxS.|%T6^teXNƛmѺ!^<B e-N:ɰ/$< |htOlG]IgGF .#{wzO|캰0qd"nww#i1gH)lb[ߡolm6d A/u8**?0hFG~ ^`{oD-ؕ^λtio]<#*؁*6?| /ͺg>T.Ƣt&!9! j27Oc/B9+}74Tj}tN9彁xX|\e9l]*ik~<`ofyi׸zMkj` @Ӆ/JbptO+۫P#bToQn9Yy]B$ֺISPV-(isL7gPqUf3qiT3{3e=?Ž5 SO N˜\m7-1ȡKR/K;88o.:q~os6l$iK ?dYN}Mz2=88n9Kkrڞ{U@Zytz`^[&.[=u2:"iBu0,ݞdY ֧p $z?Img@k27l+vjnpW+7e62GFȵctqm繮ҭ# sJEP*Gܽv^FLy< ~M ^a'=rgн Bp2H购3jAYfܪ^G -p6Dz_RS/([j d0[li] jjǮgU`t83U7~"*ZSsF1؊ij:dvoy7nOT[P=]x> - 7 Ѐ￈caO1Ih)$ q5 Q%rN.=5LJ!&!%3Z?C[ hRSI,mOl%^4kcD7Dʅ<&0֜7jCvoT?Dk茴K"Z3ChpT#r>G$K'":b=nȽrg}`v8~܇Uژ mdCn𥔟sJ57gl <gNOs LGgДPac4jjom,W -L7U}-jj"fMϳn55et5,PW"9gJΔ>~͔8_lH;6v[sc@/Dq8Clgř|:%5Їԧ"p9]9xѻw'P}m˟xJebݛyAEOsd>?_u/HRWn%cBy^" \:L^@0 Ңh1ڰ΋,ieM^Ct{+=bo_-Rmvfݳ`3vORkr`)krm3V3^;0˜'㺴9Ɋ57>V=Ba*H ;§aEd훧 =ާu sI,ҒH]~z !3ε18h>wTBE/zp[^3GtS#L Ic=N\aYwj73']ǐتOZd01X?V% `xp/X0Pe`ZC0Й|!k3MϪtʛ5u4h _7ʦD3pl'j9#z8`~if`RkF}"qa=>f[2E/s^ӥHP(k2u%,pGdYZs~&0նj*LB|QCk0\_~Gd\Oȣ @!X)uBҕa, -AÕno(fg΄M[pi!| jEڔհA% K7j@)T(#)@kN(6~XsVZwX&i/DkLVkۛĔOfTr"NDZ{ -,mt^,'iraG-"‚ЪfH9,oj"*} Dף - iV)D4B6V.|y;M=a QΑ WlV*aGvKjVq{ʞ9ռ30vDNZϔʬ*n83@]}4HmV6M'Wi8{ٳ+-OG|qqIg{R6c^.dL:YMbj}UWo4IgZ,֧{`Ծ]ؤ"/"sy6En,F:yYA<c0A\JMAV mc3tgsɿn|dd18&Pë#6lz9\Rysi3CZQ./'kySIw ܋ȦɺZ;NqgߡR0R[$sRS=Զʼn^’Mq/85D 8DOJUSHGpY27WtXR|Ʌ$ȞMؗV>zI^;YLW7M -{ǎ 6u2WxJw(F5ЊS~zsRnJ^ϋ-NǮL5ÝZΒ`U 6 -z_:^T+6 ,V-3OJ*]#-.Zm@֐m[Mv˸ҹ}E1;8b^xz8'`gIGzx䣧}ą7ZVMxQ^hmVgZJxRZ)텂\\"#SSMg5w/f 6h.l^&])ˠ.P EMeg9W$|4o1A)Vp5ljv)2be%92l-qkL 66=)"H}ݢ%Ƽ4`CU#N 89dε2Ddwi4)3_oJZCZ^ 221//jղK$ƿКvsFR5T٦>9eayjճҢ;yf0s,q] a JUloڹhV<)=0$E~y2}[r@_ "/uOSovH5P &1D82Iς=ny -{"m\IQ8]B؅X&P fZҟٿ7FN/ӵNҋ(ilsd;[By1C<^gGQ}R_pp+w<X<{Eؕsx^KE d!݀D7q7k6/K(% xn$O@VMP/ݬ2S|RF{RZ/$Ȯ< k$zGiT8hg>+O\v1spȘ_d$p1*JSH x{ЛEP.%XkNd>yd6!} -FIxxQudQtBj=SߓP208w܉p궕o? y-\sI}{'O_c9~B>U_geOf.'̷g.FJ̷_sߩA63aRvZpK{|n5_EL4M"gkDSYZ?k ޵<$cR -"G"6Vg6]cIzv1jN\Tɭ`o'evY>~Y| -52z -Ms q m -F}28 X8uͣ9$A>hɡf>/R;dh)qŠxAKpb4N/NVn61UEZ2*KĵZ" zTŠbBT畣 "6ʬ{'v湾k{FE ⅬV'SJD7B!w=)#dӾn/|I{UNw89,ZѱrxI30Sﭻ)&#{wE{E-t7jP/jg#89 d&HS;+yĠRB8fx4>)ӥx!5YZ5lZ:)ou˻;HEq5YxУ1>,Q5|8nL{[IŤ>:osfi{y]_{ y<>Ĭu38zR{9Wj.QSpu蟠q==RWX[U.49?'uNUT(ݧ˫(c&)`PkDKFNGp#R+q zT~7ׯ^t| T&`U(J3gs͆^eT`JLO8)|D5@Q};=J^ !X݊1[Bil'T;bW\}_.AQG8dWnIo][rmt-~-O;o0BFUw%_qc[GZt 䂚a$Qg?I|Qa/n:lޞw3R2p*؀?0_h,=s aqj< .GG3ǻZ?8bYCV.I%(G;jvlΈ@[zwk<&N[l14dqkSj,8=8Y0*s`/?$ xšlQ:e3RsB] )Z3oʢ]fDFtQ`)c'7ccšv&DI&j-8Gd#-[s7I- u~,.3*MjfD]!lekPiפj:89 nչZ]snu-X;("BRp)r}t@8- ևꃻ{كdm^H߃*ك ҀH:ƹ(QTjA׻ -ifn=y7_1/ 9s5I߼Mu0aTHZ΁h|UHjy;w6eK%ΑW0ݒkuK/o{mh m^` !$8'0/~32'0+b? ?k J<6us`Ɵg?kx&?l/hbPK| zA׹#jy9BZ*^u\OwnꨇI_2-_<[zX3Xm7/"1sUC9Ο,xW8g%y79;c=\ϛ虻oX5uZ9/P)cpc-uƠ6IŠ!m݁~xmXzIf¶ ĺh؎VҕIٕ݊C?$ƷT(EGP^at~<=H~\ KJqG‰sYI_+7h-;tB܎]YHQj8o4$&5/J|P:T48ۘ&W%Þ1-KU,n#=n^!MGӜ<+rRIHFM.T&VwqqifZ9< ~L?Ytp;OH -iFp!'փ3{D6+zn1%R'YjnY֪q\vН=7|9*12+הڪHfٹBIW$ #G5XT0ɩYs}qfpV<+ɲ"SWMilkAwSʒYshKԈUKJ)1(G=gzJ=Z~AYR_e/W>c,%› ;yHG]`AB%PcQ?.{uUd 0bPJGul {v}OimD5[j;tuCIJr|vj2ⴠ#B`M¼WR㰚;W a aHFMOKpp5MC0^*`7]lL }@6'L-^tC *c 2{et,kG[%Q Xݨ>@ԙ+ζnFwmq|y@vYUl6 `oSSf锰QsVk>'T4'J}IVc٠?) ժb CձORDZ^ĘdM;c2 VB2L>H/2ҠfCeg.Yh̺WD9Tcnƕ"E|y[exO7yQz1K) `9;;P"Tr1>ѧ)|Y\3yL洁wm>J----$v{\ț&5-8AћQ˺F=;7_dqeO'0WA?A| A?vEߩo43vqIg.QPrc< SQſlj sr&zϻ"/谺@~DK }_I8b`%Yyw\X(9ب'埘D[n+$=h:ߨ!Z:D K9AFйٗeXL8Rq ۵^y{[!F$x]+NWGc}U -ƨGUw sRZWGaצٽlpoֶ>nřo$UʁC:yLV#[q]iNRLfLF#Dk=a0{^)yF[xvx/g=oDZ7Kǡ;$gٮ7 7Io%.eZ/agMiD{:͆sOYAtdz~ k&$ݝ*G}do5'=-R/|~o% E{+>{͏K8Ġt?P3z8%ۺÖ|]%xeC+hڶf v-?r ǍۼS b#_'/LR@5հs($p{Yf7ϳޙ>:s87bky*u;띰ީ*r7tpxdLE$o^[~mwXUT듧g}<9Z])=q&*yEѰ -o9nczеҰ)\勤k-@UnPn.PR>֓5FF%Q|H*lV;!;/ă'Mݞ6ۇ/Ȫ`)P7Pm{Gؐև3ى^mGI'BcLI W?W w(B4 5})KRZ1$z@ZppFezt݆7%|zW=čw#qŠ}˩T\]7h'ǽ+۞+ፖX"*<'鵙pBaYGe2Ji'S~G冊̥õݏޠc Q=;Du -\z-,sn, T!qrb/z)M1waP/aId$q[߻mɹm~R>@`*G(n)O<>?ǔ9i -+v͔Śx Eڌ `wN2u=9Wf$o^AE zQ] @]k!l:-=&&5v:tF3{T338}1vɠEMI F2Z7jк[j|V -`N|WkaW2~wv+keXbc̩:s?'h%Y qĒ -6#8evUY򪹟v]i6I#iL\ˇ2+lř V`}%4ŨP9>T=EۅBAl1KL=HWq6v5C]usJfyeڋ$^{ͬ|]n4ź8 oCa"Ps+*S3fd2zN.M[+N g|Jhׂq0]6+Tҋ,.jS$]EWga,zv*MG#=vl"_pB -,cFO GOFZG;$~MJ'|F3*&ĉrxx0@u7ζ'y -dg${c U;c VҍytB9: jڔ"$ï~32'0WA?%b R?l?jfV2_7/J:Ssǘl3\1&]13X;Š~=Q=I*7;,~\>0RE1Gi?M>vsY/E88Nx0[3-W65 Ɵ~O=z-IMmӾ'IwIpU i{GЛF9SB [qm NWlo4bL0Jt1Zl:zf^oJ楨KJlw! "KxYeoֵz۽މik'\aCf#2 x:aWKueom`xɠ}wN6#}:] ֏Lz/Pq9eosoE7лF!jxgV 01{S,A?jv"]pEj%ÏƏKTѴh4mq:z‚FϪ>R{w J;|YߡPƍ` -I`Z4t cA VA1(5zocA.e{o۫,W}/|r"ڹU7E`>dPc3DusU:t7y[Vqd{h1k}-8[!u^^FKpiiyiz:>hu ji 1=roRХ!yD5tW CU,C8~MT~{Hw!ٰl& >bH5[ 8+ ofx+V"RfN`jNlr[GHR[=nrTWt<:ƈhA7G /rbXL$:qWË'K{ߢ1kۛAπ! m\bTi^܋V>rߝ -V̚##ߤo4P"d2+grhyUU]^{Ai=95I߼!񠞏ɞxC9I.ru -6nB%DoMBf&epK8RǦ~n L+pZ -vW|97j%eu+1t\ӣD!5(\${*7A= A{ W k ʼw 9gh3 \ۧFsθ'v;<^:,8EŠqvZRt|^%~)Ӟ2yמ½kLn:o}~%*K&G)%{6NG@IqP6&75g@ǣ~]P"0pDYC~=;; V_k? wBu7$Py_8wBT-XzQpMmE16?e+fΉ6$G=̷G}"pYETQDDe;ZuvoXgIHR{1N٧@\әsp>˖|ޟ&WPL\ 5Ϻ%ѳOTʞV7cvYFh[N)W5Fٳad8կsՑ{pit7P wOVQ9PbPqEl鲏 [r])g&V_=:m Evꥐ[35q@='_[tܙHWwo s]E߻>LI^,(3(\a.ؑ׊Ŷ{mݙݰͲyR7g=W,[:A(Xv kӮr:SNj[ȫU G8"5b |gs%Nܘ^q>-8>&TisR䢒咮b8"k/%p,s[ß#|F-fm=}"w+<̗ *ISv&2)K`Hg_$8TXl"Z]lrp`ﰕʞXyl rȟo=ƭ̅WE,Eg$JՂu>J×P7뻎]Xi`5w(_ْ-¼ͪWh\L $YF>L_JS6ς2Ab*ʲ!qϸ!^3EPIT6X|4W$5i-{g|nM"n;bAM؇֙MH6A1GfCD. -=Rǜ ,dvRJp憹Θ9ә0ݯU"vV#8WE6Źe>I[AevGm_z"9"0}^Ftr 7!\:H)rTjoeS:iOmwSIk,r 5Ik5f/v:B~)>$jTp4q+be^zv9O6͇;Q^D[}F^^1ۆ-yٱtbe50rxPܐJ6(o}/kz4v̗Q߰ dܰo*ǫ}ЕC4/?=0mƩ1n^53.E]+LJ]aúŠr!ZQ圇eRvY^{}b9bSP,<jϫ? u;KkD)pJ -ڡaŔ/=(gTa!tTϹͯzJ-nSO ʊ|:km~Wc01H!%( -SYPn4*[w]U{ց3X+)rhc#" ~a9%e@}8=h`>uϥjqȾIJn2w^( ~cHvZuj5 _ܵadUw3 &َJy7me8m0. =rkM#kX%oFYoRxۅ q,-cnz\>ҜSC0>Ǧ.0w2'Vj訓C)yCpdFrNuK=ޢDUX_Lg2~4ۿ}8K dOʋe>gN`0` e3yhjs#Ȗ@R]bK 'OZB~)yf}Wsy -E*"3Y(MREmxGAy?oS5փ9yJ$ O|4Il'jSbC8B_ft8^H(w@.*#2tįGibF9I6 CĊ'\•1_S]z#lѝVF_A:#:ٝu0X+uf `2rB5Iso1;E88Xɷ'WK`Fuutuctg>.G{e ]O/ޢZ810InAZzHW5bw_mF{>'6}TX PWWҌ:V~@{{[p.~h™+ M8oEעBkoh[%ŧ`S`ZjG Еժ^z캙G:Ӵ29znc.c*9i* ;jUsu_ >~ze12(QQ&$ŝP1f{Ptd@Q:;Ďl@-TXZl3}B4h`Vլp;u.KI%yױ;oJ:augv c'jn&贷Ex=$99Z 5Mqb=-*/p6nYsc.c냋[/4YD3' "@Yzf|u[kQ=5BR|!MϙޔMkZuڴ;`mֈYhG#ωoݢm"4j -UEev &> i<R YwFrlhY_lpm/ܟw ޸i ɞ{izRН?˹ Cѫ0ŷpD;Ua[g Q1:.OS22Oή'̻Ia3iѩ#dOkcE`Ye,X!9P8P61aH Owu6Ö&o6]1\֯xQI HYW~Z=d^)`w˥2}eu&9 ?l3/f܋2@ryjf37vFԜrtL&oo[J8ȻG%rE_{.%ZLϹ[ȟN7; S9ӏו%+kY=iuGp9nd5B$9YUrۂ{]8PlZS< vOۄ厤AxϝGˮ{ߴsƬ gv9Nt$Z0֑| --OmrObv5?h @BoQ?o͢>p29\qr*6E,Rޮ9I8{!)ar)dJkBT[[m%7\nҒzf_X溁(gLBQfk$K -isD9+{kϹT>LRF.{FNjSyE^pn9c~3!n NB0,*]jw~([8G-b!>)/ytfmo-:[^%G^\|K1ܾuV2YcfVk_hM!. y[AQEwg%}PJxL'κq>^1<{|L%mB?젒F5jʲsKv=2b1[v j]|~PnS>%fX4Y_W<mD.L{TI:עs w硢/œ˛+<;R h/dh.ZcP&wCI&fW=P y4㺰>QcO׹~NЌ%zfȪ1n9b\67Wp]'Ȗ졍i=FcHG[Z÷nFqF:.LT:-Xw59ܶN=l4¨>dNK<"`$q'v|r^fefwgW)"H IxkKa_ߔTYeduݧL.|kyWλ{9Lɴ ^Ǟh44Yٝ61gm/7VQٮdyLI.)Z|mPEgPdXɜ 49^ϑ幘I֛SWVVX\lTWs24:c/!wɾCܱ~v4_Wwk9*JTnB^Zz&0sOϚ,"jyKr"=bj1U~Qsҩrb57\xrhh?{UcFwB~W -7ʃ&vPl ]D2{=RVBuLeMΟ+*z}8n%fL=V`PKjl= n%Nqe.`0/2tL3EjkbD*89N'+Dz6M>Nj9^,\?[-]bOXDtGA‰F⦅Bz -.@{FBƮ"8s^YgV}\gȮ2ܺBi,c5цNi]AoW~q кK{W(/䗂Ptqwԛw]j=\lɓƫ\6"2;xKCqR,J6wNs"_p=UްBYNPBT1:#3wqErzY)-tz/d[V[#Vr44[֛r4y_&R9X9؜jk ЁD|GPn:Pbٝ+\'Jr[[>7yvmR!_"|} غ k6M,rY+9 ݌iW*؜T)&?ߓ=UH{b<{=֚{&.hD8ۣ7Bko7Є3Vo`A~Z! 腆Ε%~?PAn[3!&0(7a彦bSKWSKG7ONV8)M׽ď˟ -) -Tbgy{jimf$PsWEfpǞ1/c-@/I=1g`Xi99!6UJe[1oFݞP){]Kp2yhKzh:q)sC6jfa~<׼w&|@j1rihrƤ;fpBݬsȺ&ۋn_'ݞo&liP)v^L\LͽqȵgYw3m/Hw9ϝ_;9촳/C/R{p]vqIM&^zeߢԪ 3Q?A1U\|x8` +9P_3UOԺGL27ll4~gZyESoHm 0qE -B堲qw7.ow3|O\65:nN>7裾J,`ϼz9#'Ggm7UXʒrPd(A -P*,8ȓCXwPjAMsm\Yvت'#^:R3n.Bm:\L[5߽Ծ=,w#C3˹ 'MUtYI!zǫB~) 蘎@AѺx?Ӊ\G]&|WBj)}jbqwvbGFkl26g2a2fz+~_=AKQ=RI V^q/DZwFf.7 1(xf``ǥ8_8;~**Z`\9 /t9/%!,fM#lZ|ZR4z}6`^Jӧ\/3Y=~d57U>eL|yuy:E%y~6ژ6Yw -#]w:-"eX}y?W;aEtjME!Z[FS:RtA}pݭö) -`om.X`Buzli WO#_ -e8(61'(EnY'CzT;c?O='w:Y=Fsj(rU8?זx\6_5^sr2V^vb' 䰹05KmWg"5|zDs]b("茉0DzU_D2 E] Zp\V0$rlgi򃋥W^*$͋"رʖW['{`u>C]{VԍhBSinOƹ7Vovu7,!ʈdJB=m%= Sv6nSB͍:V7#w)u"VnYg 3?83<ʛ<)=xk z_7P|EUE3f90ȱO/Eci_ZwOeG!@&M)U][2($̖Ut?gu92c!ajQ uoo Ť]u7g]s[nWnh9}۳@Tr1u˾b˽(n36q6ڥ -r 2@ ;ag;P-H*[թwiҺA7%SKrop1}Z.,&ǎ˒0Dn2& UoDZ:ޝ"ob?.@-3/81./f۸wDy7.ަvHGlN[&Ws zonkj%IXG`hf]5A':IdM4o9 fg4Lg@IO@]I/76x,gmkݔ?ynRg S6Һ?~UXw]T |(FDvFeWc{u;@ 7|[ohE29|.5,ԼYP-Pހ|>v< -qtHT Bʪ݀UTP/'OO3OO?ӽtںrn _ -@N`Dǥ `hBwrMvaНB΃|p9RL㎆gW ԅ ?̚zz:{>[hREfpwpv@~ ^1K.(? Eg<@4%sP`[[PT|i"JA,ޠP\JP4}UdƒN}@QU`b g:pG,hvP)_*p>wsUe-RMTF 🿋Zp'Xf4Ԥ0 O2O65n~P2u4Mitr~z>7ܣ$ܴqJKWڅCvoFd-Rœ*oc4W(Zpu0Qi|Kd`z s[ \|ñSzl™+YUbYwko٘zխd z.M8ܱ-$_ȏ w /@ 3g94E꼊yM:GJj~d8%޴nqNH\_2k7z9#v=}vlϥ5#BEðm.W7'jN+sLp9n۫JSpsre4<F߾gHjUp˝[U A݅>_\v:OwPDPTLTϡ<_O(5kɒ`6F 1rMTU_NcdG %UrLέ'tc cLϱg6A7+ vv$ P@[}>*~FguVȖ;Dž6x!-V؟9O=}EWH鶵ϻh{ ?'TAbd[-t"y7b - -T) 2;ER~Y MPAt]Z }ύY":; V^8|Т/[>Tj['2Vt&K.;?۟Sƀ JV mSvpi 1R4/bˣB,앓L1'J.ًznG7Z6ٙ -'F[to 04E`r>(s@ ޔxmlv0ixnfSu? i%[!1YE^}thAfϧg vZMurpZ+n{k~?DAὀ=|yj%/MSt/~jRiJ#RKMţ?40\mSuɧ{g}e]=sUCe[^L4i!MKOc|hm1| FG'j^зc[%Jׂ4oE?Sb1_6]{ӛkf˺ï6XZvxrICZ= TX$R}Aoo1ce~y]Zi.d` yWR'P}bď3n -oOAiV@y>DzwȶKnٝ`H :o m"9%}Kg*PRA5#,cҷyuEx+UƒT?Ļ$k5;,]PaeP)TAMWJRu27>Ԭ /gi;׶w gp_*}_Ygǯ)I2-J>'cqȽWMDK.^rH= /=8z8Y^O?UJŖ|.Gd`a9ǽhODe_(54@kǺ6ou/9latwc͈e7ü?mZt6JBt"ڣ,j{We{];]mִ׺#BT6E[X z}PD7` 3l^hۥ-k֙i iU/FƢ l~I;8y1+7rފ1Λe.TnrLrco"B'[̟v{) ZNjomVi>EB_ȏu֧ڧLy-v OrU)!-MMDZ^cb/s k;[ݲwYUVX#8ݜ3.uNYкppJ̈[ޜ5yjT-RNY=q 8 -ڍLL;BIwׯ[NުIowՃ4S6IÕQ ܡkgOonj/Jh7c}VB:Qp^MYP=ɿ?+䣆WƯof|qP逫jө,`xBc!fRʟ{[ڡٲEi!Q]&A}}'kT1Zyj}`}f*q!?+URyh:odHr{ &$ -(^K(QȥgsVO c8OQhdvdZ:i8s]kAk`K 9,#SNԅWN@nCl'lfc/\FIBQ2]4s%8!ͩYQogONU{*bfJ@-'@>@QƒU3y|\A@QjJPTaP 5 A$kv%hf<8|onNA:)):ٛK-0doBqtR - ]'ϯ Hski"ȚZ̻:]BiQF;Td:*mT͍o[|!?m@v@a㜤`(( (lPXh h@~}쁜'Ma:0R,~exaqެ6kы`asi2P}zɬ֥=gx.O fm2! g&pA[_M Qڍ0 dDިK8l&gGi߱0w2fVp>^̟r Pha*t۟Up(B: O.]jam&<[lcQUԏ\P Z%CK\a ȋn -EYeP ,@b ~<5 6Acݳ߳קq=e¼=$v_-Ze0j+jP}'% Wg G\o -`fX7nťH ڟ>%jͽ3Yg{a4-foh[( -+6+zkrrDapPT~BQc'S#(WV8C0T3lī[X@O_Ů5kvbwu ]ܹjPGT%ͼ3/}`:@xEȨ[ A@rh,/B}TF~ϙ/US8"k#yvgfa%LUt.O)$x8rw? iD]ѵ?䦛WJPaO*(bj 4Q?-9Ň9] -dvO;u9s3#J*ȫOP6z"19:BgK63@AQ3@q~?{D~^^P@*wPQLṶ~Wƙׁ*hptidL`]bb%u^7'TfI#5^'PW^ͬGnͷ4) *z՘zRV*u{%%9i %'`xq2}x< QSsqѴTπ}HU@>X=>b`kY_V|wo&._wIh̟깤Ʀ]WkhwgU6}ɏ[t7+l -^c-җVumY8"lҟEP$lIlѦU>SŬcVx=[>{ - _irZ_Ƭz[mrfo-6'+y{cŕKw,Bh\_ȏ 韗?HX^{J(/Cݩ0NsOc3w,\bTo\oqI+3+72˭2Q:1xūG!(ӣf}?N&!QMs5σ}g>- bK%|fvj_"mF@2?j-k[k0WM<7dv|q8Z{4b zB _=ps?b >0-ey =):"%RV8RKtE؛n)OaCg --b- kq9kTgpTeΌ Hǥi >qԂb-06޴57/vUyO ag :.)#u኷] >wP2(u5>Gj7 -fnX2jhe`'D'=*b0{~8 556Y7M;qȔ\ˍ XpLo@-Al58xfFp둵8vZob`մdrM5W@%RSfߓfo`KR^"dD|3?_K݅Rf\$TYg\L5߲l syL_v)'50{&b -3gwآLqUw-LNغS>kan"a' -:ߝ*:%y`~-x|Ծъ)Kv]8լPg*x]ĉz2nP9k$ a/ٝ?*{/\Ubo"yu A_7+.s69P&{00u5DZ+ 7c8wrw mU4ŀJ`ڙ-[_:{rGuR 8 Px\Xg8Yq=nelY`3B%aŖt3= zrŃm]Ef4%[SfMbtic?ȿ>GάORndÃAɎk^TٳnmȔAa+^Tʿm8첂?Q-`pYjdz kҙW׃S}~|EBj\Ų:elɡޕeK&ΰ04X?N<=nJv|IV`S2#@3 @. - n{yNa@*z U BAoثg?V=Ӟl쌦5cp,V=OAWTR)@ugm]g4,i ׫W^לr.G37w4oVgWt61O$3_-m+}]MjJg ^mߓZQZ:zX@,`ӁRgymߘ_{@Tk6"^ض.@6 +T~>I.'R~|mGo4 -6U _v+#7Yh]{⿢dl}J)! \+SxCjg?xa6OϋBߟ~>ɦjţ0g"s„x.|2.՛m];H"#06tGΉ% Gi?\g$`ɚ$ۯ -qͱf6vgpz.3dߣLd쐾+J%j|^.kl˺( -e&sjC r O) sM?Ha$X28̟ +haGA[dsġFM S2%ZSyEi]Iy̚ Wx|W-|χ~f{p[ ʥuJit_yJ &eStp9lSVm߻;S6^/{\ki ->᷿5c sQwz}UgrGi!vM -~ը^Gmlm1Do#18ݝrW[=ד\CګK2 ]/tWL@ijA֧(Gre2,f/y/ \} B#S@ʻ'?]f7P[(geژ8[nƟUG3K~\ ]5Us}&]lNA}9x|䛇(՘u+_}\^كҬuX -X9F[[Z[)7BIV{楍UjEθ<}eC4 4S8>BeSX(zxD7Vvbr8]*{W3.Lu -,U^L43&fulYq4df -MύQiA| -9(m7ibX\)Vy,PԾO=SrE Mz;iZu[Z|a>tFؔ8v(`daIFcǨ=!LhH˷Q\FNNaBMֹEU||t>sֵT4pvwwd^/rzv؍nvk@M9G뽑LvưOh@ - wI~[]Lgroj{8*y0GPp>*rğ3yA?U<[ }d˻o>:Ҧs2Vfǘn3HWƄiCv?03 F 渀mQ 2-t7,Rf~ BW:mtg+`gǡ苻RKlpxu]-l;nC9{R436;j<^WLƝ('ͺe1FCU[T^^VXf⅒<=[ݽ?SKly٫ːh[[[N-ӭ7J;S;-T"a|v8Z*a{SڛGv??N/ּӞPfj\2P%s3铟DGo:{+zsTzMt֘y;#gcz_ak%zD_ULr3mQI_nNa{Si ۹[,׏#YLR-Vm6h>ބI!ٔ~WrBMW#; 3M#QwunSm°r7)cU1W'7lW}zcĊv<6%N\; _q>6 ҍD5;Џii`tUgUi3'yU\n7nL=oaPFJ1nPKk44t؛maҺVp9;a7dsr[s~>9E.7iQ^!u-uNBKqWqnMlN1Kj=}rIzіB5ٟ3.Ҟs@sd$ ?fC֎+M鳜=4nZoT`CmyN]-Yo]e)#+6l ٫^Gy(Hd?(I1oQʥ~aql7ZGT1b#*\y=vrQu=])T\ЬH&W؞A\_)Ҝ֖)asgY[ȟ3zXiݟ|ve"^gIϗ.Ko.b t 2 -E jϫD10$k>C ZU4K'qe<f÷ p<_KvNr{9D4I!MӚkHk]-(rߒo{an'p0B%"L+- -!(|0{xt{3sb>!]sEf`M΂ujFM ju{i졽 츒8Q ;) - %?T_ g|WWS(n[Fmd -`N `=>8@v<@HH 7-P3`wǚt4z(Ð}S˿ aw9=E>WM} `o1 n(X7 .G]Dj/8\RI=3HUK=?BfW{л~} -@qˡ3>-_! -`` ]m;-ivV ~`ymR"[4 -qŪ0Q}AKւ},WK艵@ -ȄE.`}(߿;4*u48kX # l䓞yk' -,=ˊjj5a$5 -Ѿ07oIiZ.Zo -}PtNs KBzyF}Juv6- [0ziNowMhYBe.N뜚e8FvȨyMϺ-SW;  W:EJuQgyD&T k%=нVx:JeTXn̹9.{B=v&>c_Jjj[ir^A>5F͉'?Fp 3]13)q~*Ŕ NiUֻ67v4-mPuk}"&0Q60i:=@.ܖoAqn^~snJYCWרiXdW۲n&k2e7"+ eiOgvC(}VmO+-|:'(OޓBfg>_;B:W _^PU}HL*܅ 'ֻ#ulx8kwh;W[QvfhBw[ˢʭ˖)d$}3cO"eʊ'S#Mг#JOP֬;EUۓtmf,zMw˜ʟd6KIN>YX6Ҍ7e94{{TPoBWlAx 5o.#U [cr}:ܐ=m]Nד52֝y%gi8Z-{OSy-ttӤqAn;>"R."a>y8X@Qу -0Hʤ8e^˼,yRwevTKk^Eb[L0ԅ[Sq4AJjn81AAնn-胊=D~u3(t6*b.nm6#~Tn;Nr\q8m=|d)9{F߽c8m0_ض&<ĚnZ'S5!p" /ɼ r>u.8U)yֺk=YcW'񲟙U\ ^_>1- -a|U*Z(C(mYg"JBe ;Znםy=^HoVljcy'¼,e ,)+ xujwKzϤgu7WCE-6߽:iȕkFxdct;gYM@z7Pl7ߏNP ozݏf~ry_W0Fkϩ8LW8}P3dk8fػ/_RܪIMJʕ ]ھ -@rMs?IxVŪWLJSEx,h+Sca(ڌ2ֶL9ֺ7  /Gbqʈ4V$]$|@d ٭W.(9s-TgzQ۸0s?uNWO#_vt< xB݀m̝!λ+(7G^p\WD;$8M]0K:8AFq7)} :eE;'S,6KiYp^e(C ag76'-uG@>_@vs X4!76G0 -8dFkQھRTw))̵'Kcofhw M=TyOkJ'VqN"-PTGɂFm{8}V=&d1Lc c+􋥖 ݙ?lSg;Dwe,YV=k0K endstream endobj 44 0 obj <>stream -’rq<,cF]-hhRS)7>/_/kX0!9:e#2޴k-_>j7Vm2V:oZ/n2Lk1,U{1evY|lf='%e}22ܤef$n6Q-!5Nn5%g~&zXMp{*O߿r[a/ƨ@+ל?9ULMg.nN -gTxaW&0o,Zn!/Z rt^|sjyfxiǓQ*8$ܣ.8#i9:}/G lvlx)x+3uڽzFPP!4Dd)fuݜ\سF:Q[k Jo>Y/|93&jJG+ԧǘMZڋ^gX4/:ǥ5q{5E\OooTxGdMj\NTbTx<1~鲊PlKl/û8_ahsA{OM`̇cܵɸrm5?e9i",P&OJ\.$(_1Oqk%buיCK4¸<e[>BmȪPhT*RH}hסU"?38|!ܚR뽼bϽoB}வ=h^;X.mwRdx cZoV_v HF¯Av7{,AA6slNA6e z  939񌛋!tJ6G̕cW1 Ƙg9Ko ;hCM˄u5W|_v}~M@ڪkhB[#^ ܠ o -Ȧ9r d)8bhA}oߏ5jScȁۂsP##;U ${@oO/ dd%YpDWoy>8BfalWd7w$AChם<fV"@G1@s A]5I6 {@d:ٶۀy%Y<ɸ췩ȬucZaYּwZd힅7Ygez@ sw4 D4ͧ97f_^j.F{n+7ngxD_J=y.mw=&h}kɐ*?E܄g2!{|e £~+)ʕ}ҫ'd1&c7` 6BIxt3!xK}SOPG&ͫ%\s({M=$N 8N3x:W;o}֫xڭ{up'x+&,T)X=@= g;ʪLc#I}Xb} -[:L#Q;uc OXbxoSyV -#?Ni,N?JpVz/WPn+Q:Ɯxe&H_),;R"wm'/3Y+;Q5PC~wf~^UWaq c~o=/T$1{0;1j4'Y'^5G,~J-%uZBMaYeHL0ܟ}Lעc -BFI<7o븢k3! /y );UM7r.ӆxػI"'>k̮_P,l}e| =6%bFH5t_4f-ANȞݠ-9nsrośg%D$ܒTyTv{s-ak gc^Ģs<  (5@4osf83ҙ_лe`#Nq*QVab  -m0;"ϊѼT>3z"ZS}>edyEt+8?>9 ^.)/_q~O}>{f'NΓsu_Q},[3e-Tp}Znf{'o)LҝGkAm#&y0wu6T+p$h\Z$\AU{>>qt|m(tvI Jz.ݹuZF_U}6bMj䃦Ir5 Qϫj d tc B^4%C}+gsWQ BwDjS1rUodn3?pյHLKesvGwZE2\N2{AwGJd\ -c|J*p|!e?Cjqwr7' P*]vyR.E[tH&˗k|X{͚3v剣tfNh[%@v ]4xJ~+d=s)^8A. Ĺ gZ(9̎ڜwiW)ц,2_ qFJ36,_M#7mlnO:#-[\ d*-*{xw?A=}j6\ma&޳" nG5up67f jk}²ǃ٬:YY5{;1Z!ɾ`TQg8b?Mi>ty{Ž+xY'nUfN X5a*Uj7\~7AAǤO߸jke5gԵ:9>%3Wpȏ>vZב^"-KLBu\s='Srq򃰡pYjۻ1v垁ܐf'$զi7μjӂnw\oϢn+9|>/#TfUuG.3`^N*oH̎4ћ0LVsZˮ{kXL_+p? .(QnMʎ5 -˲$w&D /#iтq96ջKz%*yREK\^ln!VGleDޅ-OIIhgፔ✴Vuf÷ʹeg Sߠ XL5RpđƩfI۬Yq֝2) eZmvSԈV~4NT8S16=Q+\/St甔_):aɩф@3ìF[O#Ȕ li䥂 ڵ!`bO6)~&8t d b lAv6ll,Lsqp 4QL绯^vȈџ=CHqbkfGGY2ƙEYgSH*F"hd ^Xi,+@|_ d!(XC' -b@ƹ ) YJkqP|? IA!Wb2|? G8gX:n%r9@ B$tK@hF#8.Ɇ~,W@!,*qO+6g)Om z<Yy4s4*+ '/{Yi$݇GxkP=\t^ @hZBete/|x)yt?q91 Kv{w0j 53Hh.ad' ;(AY黉uR="JUd _A@ >(3aT$#²`.C A*A:OOzÉz\F}fKo풆Cbay9 GtxZDvj/T`b?8vIk?4 ~˅r }w^N~YoS):u]r+Oql;sG]7|K]iz;y C/Wc^-PcwI~nZ\w1LdM6u]g0'`R/U/}EXD|ےM |w74mL%Eq>9`"* E`zϷ~ﻦ~jvۃF8[\ݵyZuKp9Ck#}ĵ6؉|UW?nW/<#>%lyIt\VKP=1Lbh_$t@fZOе}ip5QOw7 -G ,3,6A mRotKӓkLw'CBvvGX^]p0,Vaw/899*< Njct'9d6a% `YwWnn+t֖.ܱ[݂`hbT؀rZ]!0kՖh]byY?ŏ3twt{2 -& ؕK5tpZ;{#k[3+?~!\PͣxԵk1sǴqUnv0U3UF-: -EnuuG@Q9ǗE;~:{xw -+E5&DjY?5iebUJNq7U?GR󚜫]W E:qcEhb=k&M2{V,[9'N q+9%׽r`Æ,F/̓T2F%R߯9B .產("1uyR&_\ώ]3ِ|ѶyN3uwt֍b0 5uzXȂVf2Z `c-/W*LVA,f}Y?Fi(Hy6.g5Og霱iGUu2NpUY_BzPmeb?\(ۋNO稡I}gvx[e15S6XEڇ4̤BR#KG]ro{Z\WATKEYoM,H!lqP.%iIy!DAɃ}# h4HxuŒ{t>ZZhtb5W,鳲#ũ'R@1d?.uYqtԩ̐DDtTS^ƴ}vv#ϣ_y POgd)wV^%wGⷰ[Z\&Pj3Oj3f:Se^M_,l0U*wf؄ck\91: WjfgR{Z vڦQ{]{D;F5$7HٰFXW4NY_;I\»Ln"} vQ1Fl%st͏5ٱ;Ƥj4nB;!VbLck{=75&(ϵm༓+iėN!*w)E'wrjTCZP'8ӷ:#,S]$ Bic9|4( :( $ᵒ;\Xq]-"fs5={xʞ3g)hagGV>Vzk9]hT]:?TJYYz_oo^'\~V"nctŢF9qU2@r+܉wkrk4?.U[aP!Cq l R~ݝ=n:vo$]st}/~EX<ʼ"!k};Oi}G-VSbf2#3 K>M2v?L+BxX6?2i^ٿt8L (et7ܽwtv:f( fA=iDdOtʽԅ !FڴCgI&5>5x@;?,^"}?ُ%h L_ǼuDC>Gho^Cn27vawtu3' X]rxK]PxL-|,Fx '7s7g:"%)ϧ"n/_ *{'؎Ӽ\j)ٛz嫛KK*CaU+gPVB!Jn -dJ(is4B={MoIPT>~WĒO#{<#u{M.VNR:z %uY}PHNI<(9O94*0f0Z=нW~X뉧q/F"]lh\M -n#{wU7:εcPT] -de;:go^&jczF۫ - OmD\I+b6Vfi5 %/k :m6f7  ˄.d2ǹ - $:j4M)k'ҟ]tK2%h<%^ -8/療fd//dI9 bLnl{:nJb?|ds)Zʶ@LW8s߾J|)<dz{-9>}gXmV0 H>r s0Pdg;d2ms>طdl6(( A濯֑x)qCZΏFuGr 5#QP|`i:K *l<& N$W͔dcmЋW^=>ƥN?&s{FS9ZUsԿ*/_Ui@$J< w7Û4teNe|n\<pAGZAz(w.@iІ*f9WD|̣Uw[bm) -3Y ee\薧pHuӋOQ1Ý~w%y@Z&@Bb~_TUo+p\;ё{? #h٢tN_ٖ~u+X<+zt3Q~ˇN1 -HQA|ׁ>] ܭΖe{[,ά_$j}P}uMR秌%ݸ1spi^yٷYX'Oܓ;t7keGoQ# ]N`*aK){U^>+?~ʬi-Ƶ+rMHv󔟟M0AܯȿNa SؓB-&LJpGKz4a[*]aLP(}_y{8,z k<u E}/"0'n `w@ng.%V3ís/hwya?E̮rr?Wcku~ר*0]ei/ݳh]vR7G1KZk^uuY5sq/j=9cK9<)j4RHP겹Axqc9@VLԴaYAv{C%`,bW= :ik(Z+,/HWJ"ms>0o䒡3ͱjk62J뉽ip~MWn>T>֔`+W+|)\Ѱkd8A{gu\:,.wXK&PIIsxdL r˓I"ȘHjm$@NJg%y;'|n9u?B|$3K7 X2HѪgf!#"dTFe-~U*!:((qfSex 6ƤlT6H?E*]^??op56iIy:rO>yڕ>xMxĹu7^*el)[ĕ?Ȳ!"t_됃Tʺm-&4DEb6*MZ5J+ou,-%GgG}9N&>b*}9~k'dlT :fL;nЛ[>,MVrp|6OG/s -|u~#6L:oi]Ym>MNf/7A"c̐4,{}n?ytiڜFg_UFle=R@L勜҉yˊƪ(gUaJ2zcJ6ZO)f_& {M!=oܰ{5p-8i4~FU;o约tW&1ڰiv_-ZW߾xw퓰l o<Nx~/A?i롹.uvB$i2NAX%p$~? 3~ 3x^^`D!82)ۤXϖ!՟[ Ll_;osקrM4Xfy4^&Ŕ7a:/>lͤU:i'LZt^P{9^'1vA\3#J\U*촃]{ڸrL<;7G PF#FFqO>D6|@/]i_ɗ.xӇ)ېѫ+|uø;B̋qi eM3o-sjti{Ԍǯ1TAA( Hx7E1:' 3E4=2$[땛ypQ&h -닭Eb!""7El=P}.vY{R&#?̯!ytDެɪɒeI -"n+~qOz?^s׍]xy~!r1>}ƽStѨbu:tꍸ{X#,討ӎ3L6 ?Rl-x9z›u0?STF eG"drcdU&BlNhW쓆N>,t@L7{^}'}W&t{&I료RO-]B^,G Ksۻ8mnބ]ꄝHz8/WUi,"n*ErA>x-ٕl:&W##[|If2#/9)FSaAZZ!<{7ٽ2爔 -mI^uGtPOj!Ni+`X/jVB -xYnϞUIH>uDB'v_AhvP9(w͚Aw0)Ĉ;SWng\+;&J~:ϲ_>YAd.?}inl9 2F [~(qzd{k{ q|"w3|IOסL=&qD,vuuoP3-[7o!j5tN=ؾ/墡a\P[aX.{qM9 ôWj$2FwTzf>w|A*xLvb,8z6;ZEf;P](h?fzxJ}ռ2q\U_{5%NqWU!;tMeImgp4/Tp!jmY#[ IDAFQ4`z;ԳkXk #*iU :<ʵዃݛU[NI|xԐ]$\p'uj#nY jƺosa1N:ڔ)UPӪ -Vx8U*ϻ/_RzkkCΉFu}:|toxW= fU>.C3+6ʐcdH"N.)wf3PS*h?HqqF9*Sd^dvdV dzJ/ #7ia)>EURfY;tK|@VRYVXO!No@?Ddzʚ 4Сk@[CȀB`‚LJ){=L؁L0>,jr1.D'EV@ZKjAK +.xdW2dwd@G !y1!J+}r)'@PNS0qA,J$y,c=Yi J@/zZ`⻽LX<ٰY@!mg* [" Uɸ?HOy-g=N]'/O9-u b8h}GO6ts4 O*:+ukЮ1E5 - g ;M_6ĿW{}ʹW"KhX/v(7I=95p|V}ƪ>Fo-7l6l5hv/W1oF=UB; 2gCLSqh C=zbx/¼se%a7uLÛ]#[j^Kڱ<̧9y}]ff~"kҽƫgʪ,6hHFWўj+8 -\MNt_/YUG'!=ǽ+Uخmt5EpZIZbc~y&2O9 Ef(^?(*xYKkޔ urؿ?ye*>'읚i쑖 ́-TKj1cy|-|Wv/VZ#l#/cRm]鐡ic7+O7s L[lm9ܮxD{'!lQeq>8@EpGj#=/5t wcw ]M~!|ט`q6Om.Qfcm[Q}͸G\/K181Dž2|Y.TNKj= -BrηĀj{qw%>:":.@tFu\P|#:`SR-܌k[ᴋ -hmK?zs\_t2$>\석CgmlWp,|woGif/|_n^IY>ی]j8 I]G?.s:KX^HVc9GFgX*r7t bMӌTLUK `v&3}]^>J-Xi$j2]6C)6 M$/gfI>,2U4t+ǧ9\ +3(DsOFP_g̖ub:33/:R[VK}3vk\G.6T>/M[Q}hjǦOM sz,4yYJŽim *͕O 2 *%ڣ+%f뗏\\ƍR~"5o8f3%Oclѽ cvKǼv;F_glUa׵{Vl0g+L(Vʲ([^!tINfY6;%ZXN3B ^u:Z le!^%DxmjRj<7 - -&R5?z+sh㘮]hp gNDgo"HY?~!WZp[($kt"FZ7:ٮ2^_L^ZVf,,OʑK9#+vido*5ԭtSIs'Cv&d!1%zen) 9yPzB -d䪃 \ .FQñH>|KVSxʙL>#0N>Z"eLyzgOL gNXUf 5fØ0[?(+@%N,LAf#Af~A3A&?%}%ϽO)nyTJNaĔrϳ(QWAJ\PYn鑧IԣV9 w%42Rv_iN UK Jq2>ڃ*E//L S-N:iI`^zMms\йL{{{r޴jx *9f-o40 -*} [*\ !slj{ijהךY1+mq1>y0{Vwqc.5{xs}:4Ve񃄃yQ uEZe8&qz"ۺE-!^edWdYlھS<9v?=2^{ CG/2no2&N貕{9ЧuR';(86J?~hB 7tRz(&srcd>ӢwdzF$[jA t* fp -/y1Aγ}=`B:U3(k^A2Ϥ~ -O|7>>+7]H)w?ydab_"2!ϣ! O' nW9Mqon-H.f% iW+1Nkl#}؅]E0iRyXE\b|W.49Z1i = O< 0ov= M͵F'ZZyCUzmL,ڸ <{R:bJ=,BXsZs} 3wԃfVipi˼6aҟzG.Ծz7ƣ=+jnҭ͸ɧN]%G͊}TAheQjRf1kaSŜ9n`nw&CƏB4=:I&]bS-/:A2@χplU3xl_慎Pfӗt+TE]k p&~ϸCn8Ҙٸϊ,t&}MOtLvOYD\AD{^'ǰ9ϼΨ1/9C:,:Ƥ~(V/}OHG Y0&|c}s!^ -:cm\OkBް8'<3: Lởx"Yj-3l4jF`ʥa3mBD%hFhyߨϢ٠G MrڧGJ>* fPX9VqJƣaA+ZǏ<g ~>RuQwGE`¿oЭɉV\%jGVF7FzKu8Q?!Vvaf"ކXm"Oi`f3t2L-> -Ԥiz"AG^o8v֜L)uMݠ5~]#@s!Way?HVs#*"GIE7WV\nu!b=K*:4' j3sYd[xf^/li>h*6!c)=vc4K5&ijHw` dRv_^4RFyMZvkON?gkYڵmJhdƒ2FO8 MiϘpf΄quč2Nl:j2CiIߦ !z>gK'}D?2DY*S}$š/fmͩlQr cR~wDylLxwfMʠ)Q!]e?sK|D4vݝ/AdCс,77H{.zw}3yt{rP0O>J}$l"T -H)'R=j&<`%#w-kCј|qQlԏ3>ߎ6Y;F72g/z2eKoe1]$^vN9scOF_Nzޚvfg$jjtEm-^ i e=2sy nk- ե$82%hq@0w gJTnF җd:"1G EsNFa/z+wu:j1,R3&>=!q[TAe ؙ*>v8]lݭ -Pεw{b$ilZc{ 6xKֻn-{ZzD8_"f kz5ïT鼷~:;24~`coJ}+;I']MW:7Pm(L-x2œ7qʰ mC'$Y62;Ӟ4,Vɇ4R$MM򴁎i^@Zmwgvd֓`o]a }]JpLMƵk\zUUlUM aN獾mu^47d.857F[g&!ܕJ)&-%j'^K>5TNHO瓓/c0ؾ3}m2v 8 մ5Nd0_<6u"f0T5,բ %,898ƽj -y4|(j&/3 |4P u/@"O"OH]H񁌴TVV 1 =y+UY\SRQ6?dqY:EbσͽIfhpO9-v)Wz֣ݜ6Џat\Cy CX z"zu+|D*+H".0֎:akvXAmpMZ]9^r[HI8-`̷uu{KnG]@4L~^M08/%:u0f~O?|z@{v+=Ա{$(aYf혯KY,yhm \bΖ6 -05旴Pikxo\x'}+ww$ގJYVM-g9bUqѳ7oph i\V뒳Y~kyf*v`<ݕA -UnP]VG֕k -"d&|ב11N _\uPbvZ/QF@LcNfi0teeonw{[ W890K`"W?'l>|ېiB8*Gs^ ޯXtkZ*]o 3|s -%Q 7s[)'|TGr_/xlo0޾\G!4dx|=Ց0veq0U42VqmI } F6Ae4|8h AN.7907g>Qvw9<(ǷjyaCZC|+J2%Qsg"s/9v0kwi~- ]mYbnVHR-i-Y i蝻&2j[ -)Ln29Ƥ޻j _FVu _)<ʟk)Vb&oeL{Z?ܥsPOj4U/睜DyˋI=3zFV}07MXno/ -1rZkRl-*Q@.J~m4dIxs rQ,lnzqq`_qVo;nM|dF&VdGۨj<FشZ죢k[^جJITYJ>l7E Z͞SWՊ'j+3~Ubƺl|֓U_GS+.KY傴C'#$bEA&Z~{ytl?v!x֪k׮8T-m%g䏱 ĘIv4Cjr%]Dr=L%F\bKMӵ>c{6^ 1#댁 G?>oUcrOyyN\J,R*"棹ryk,ΒÁRq S )C,n1c\ҽ1? Gɐ]93>ë'S:SD!OK J;fr{MkǪ=B DJohosovB::A QwWab\ٕ9ڎ>rD+|Va[? }ݗmTv\]ۃ5*a V_c+>: z켦뚥QmMx@[ w iAhGgM\Ol!*]lUd͖T2NG#.!w`H3.cv[~I_ -2cfו?LYUʽ6Fƨ5tBǺlgR#uF sC+q>j10XSm~@6/Y+pfK8_V3m[R$Nb$J:Ր+L7c;£-}k|@!:$c$G[.9^ -y:t 4:@:ikt66ֺׄiah[DF๧l3XxsC-"\ _̄V?̜Ҟ|O4p亥6A2'* 5إ7C#z fI "jvV: @]vmZ dQb6˿tđm=TT;%Ty[odk09d*ݦGJ8so0Ufa/>Z7d>Mg>sӴIGZ*?IP<\KjDG^j ?m~A{)6U/-Z'rpjVçՆ(~ȲlV\jXXֈ?,»k -`6m9u Pe%G0Þ RʞCt6*"X?D|̖x]ڥX^Ϩ  ZQhʬ+݀堧t ʗ(oem~1>?o銑/ <(!F9z7*MX*Hz{).~ pi}5Z*^Vq$Ep]*׀t Tn,4خzW'[stSYQcY nL*5zR|qImD2EŁ?.Ǫiu.}--F.Sk*CnVBBƈ 1@-@67@6h]HaWx$,lBdPmwg?^OM\D%vN3-Ls-zq391j Hħ"HE96@ {FR(2/ٶ|ŋ!Sѕ*,B5wqA^YQL`M @#41ntep:DŽ`;p?c4ʸ2!@wRF4fی]DWH>4{ Siyۈ]@&U%.'鴅%`$ed?% 3:I2 1U3^$ex'v8NY - -{6qE#O"=SɻYj$Hқ'|f}%xގ24gz8<+A}w|>RG- Bj6:zAǷL],d6rnY/^ɧZ/&Jo1g"$2J|)usBnS.ɹ"V]]>2s*r`Jˊ:O)l'KЈr5 Co7wi~owWGY/c8=S .F^k3 8KγnRhJ؟WY3\=l-, ͖i%y>X̷.9vCW,Bݏ | q%lWͽ:e,֋^Ξ6>VK_ƫˊ/=9^07Tf2`,CObYOeV[3Cc(˩c)Mlww7Uʦ}[ev@kHVL/uq|;bpwGA Su=iΦ[7Le2Fn")wUmPU+_i0竲VxXg={ oY^WVc ҋ/0&J73+> J -ڦChoS1磓=S0z+^~qۢ;=Gzu cHXC,5_ed~ -!}컣=omjnިFݯ7_̶4HoAӫVDt)iCk60I>ž2ĻCCYFz(N̨hfmf1xuԻ!YCB}Y )'WȪ[KvfOlV85BXjZ˝tח|182p!%B&.; D {െx Hz1kGl/G[2F,<{ A]Q۪uu*垳IqQsQW]ЎkHeDž -r>RϤ`лl % JCRh!p |~tS\M8]pxJHaӶqMͦcm(G^ U"mT>).PE3,+5#xFxƣgW/m~VSr~WN\}h -뚶C5dZg=uRqg/+v|`HۓT+r z *qdrt{twﲩ%brj<^^[;ɓqܞwVjCp>ک2O ᗃGmƺtkO?%z[i} >p2# 7 2rƾg1ZrB,zͶk^+hSΐnh%/J5qdLab|V+{x-9eWRTfs.3| az^+olv7z/+ڗ?osԥ_cH+1f! =SLQ\ & s,6DϬ>cjޑuZNyy1E{wl (RTf/QMfQ#P#ǩ+gS1d:R&enhc9$%wK W"O<ϳYoo:|Zϔ:ko0Ǵ2|\ԩtPᚘw>Dh ADNP?soۑ5r)Yu웋6CjgT*嗡$uFxBY< /®ƄS)UgOJ!* 5_t/}t̆>f#JS6*v? v{M0ƫR+f}+Rָ1貛02WM5 ԑx̐lصd+a`9wC![Y+-k.T7Jl5 +uԂ^z1 -x|1〧ʕVcKpOA$=&+}|KN6XtO[ta/nTi^.T&?mcMt?ns0->ihOŋ z[`64|[z+ҺEs =vm͵QNs.`_W8\8\U!Iж@ @2@6@B] - Sf'[@f @ ?*JX*CV[fNj" -#W?q,Aiss.O?`<j>@/\/j@>/@aSFy6ylе m<:gqdBrmq (hOVʕ.["vA_d~O|;NŽ鶳Oi1srW -HY^0R$6c'%w%H`xcZ< -}i4)=G!]QtV|KM|1=g=½]Joot=fkD̜5g_~&;414;l\u_`B߶GaOc|<)h[kW|'3jam[b5↱nYH}3I#Yw&E$*ّD1.=?ކ8nv卽Vu6}]kZa_uzj~[~^QۚŽWR,^=v~( ƓmwjNĄƘQ&7+29kxy8m5ܨ6 -3/?++FK)C;'9" "b7b* wYlgT3M[PnXL?|&YZ,qlb!>^4j- ]6NέlG;ˁ y e(IlND83JՈ/-46A~UkX=N!7-eb2PҤ> (ᾑH _X8Y 8  Ɓ6r,kc2Wؾ=kQz$ے a.'%wш:GVk xH2r 'Wsڗ;˵FWK 3;XExݹ^+FN-<;:Is7[, 0BMADx_/E o|{!GZ)[0 u<'}Z*p5\~QùLf=oGx8vڦ,uMY4w? ݇]/;(k;"!YDOO%-y[D瓴OʲJ\6M&5dUaЬ~T tHc5̺A@ȇFc"סOE( w8QlvQ-g-R9m5wn&ҧO7q {oԢ\\𷱆|j5{cd B<ҭhf_6N+O6?$muɬf 7F~FQ@'uq/.됼?KYf:jӘM*7LVnnrM6Xt{d`r!S[bFA8~ƘvgeYDcwj5p>)W u5p((DhS57 d)R_%[n KZzTRw,iCMx;=C,6?d,1gN[}iVի_㗞}Jz,JL \rgP{1-dX (*uCIAč)ѼʟcK ˨dꌯM1XEר*zzcWUnl峬fV3e~^--7a2? !݀D*]Ȱ@f t=T̈eIif"3se`s(;+Wɩ.ζb4v(pbIzas5g+[nv{֋,n8aS" %pXw)~!p_FC,]:HM|J♶n8▸%O9s7+4< g. ^!c @v u - > fqd,Q !g[i\,&SO"B0´޹ BqRW~q}n P;gȐ@C%z}:Zh|@ Pf PgPOhp0fs3/$jJHh?Iz0L8,)f,O2I1',^tp^ysn?K|~O{0ǐ<]>5&q[-!YNO8Iݫ$}ϲ; We ,Q?_k]?`,OqDC~֣6/mQZEMu -o=›n;`^t3nɸYFBMXʷDiY{$g[v) Z9q@茝ȨsBy)w]Nb<CuuKlި w:m-k?9 |To$PPhRF{1-;$ -*vc4|?F- a7kQ7\n+8c49ף!hbM]w. -"A[Nl;;?dεcԏ,jDoA ~5yl#b]+|i뗶d{ (Z4NT- 㙴,tRr -xГ~?mjԾԸߎ1Qci;bGnOZojqbw -]T ;XٗO΃A֣ZvnڿlI -ʓP_\6U1qy;~yFsl?Q.p77Q(Ss5̕تڨ5AR Q"Ћ fo+~SG}M赙~侀nvVDXb/}\ -N.gYizkvk\8Le 5_Q_; _9x<J_`L"q3%"3؁}s#/}K~ş y-6*#Uͺ SQ]rIŬMwH^|_/ЇWܻ|݋y霰+3nOn&~NΎ0OhWǡH藅rjx»|XX-W S#.D}L^奾EsT>tzǯU9U&DlF3?C'#CI$2gp}D_O>Ţ,VmWY\\5JizN~͓c#}]@wnП.hlBNxQv8_rD:@Tr -mqEƝ#5m~vc{dD_dca7ȣnGOU5r—qpXSت^=X|Gɬ^E7zs<\2t]G`]lBl&m>_ۑqhЕx𮯗8z]1u5P=ۡ /?LF<;T 13{]X"F4c΃!g})BZ#F{S>tڎTjgwjyW*6jBvm|ZER -F]VIZϬ277vϙ_7n1YZ wɜﺱ8=n]\DV-D}ՒOS} -b2 ;4!:zzϧzj9/97A߈ Ǎ?[dք8[G}A;m{s4=LK_许>ؖTk:vqpj9^I`CΐG7i(=\]'߷?AAK[yJ]E;zDsR:\e&pӖ_=d~+QGƃ]!t{;|n\{ jʧ &#S(6P]t>FK{vw3S!m4˞vwZ=)MfŮD|e{|#ZUVJ#]G6<~-*7$v ;3;kgP~'"N>|WysV})_\7K_v߂)A/ZnF"7@ C{;&/ݤ~cm@d*#HXw>kc)\;QfuH[ Dn&Br.³CUfc8_њ^)bEj/JDiDPN[BL- -b +K|<-uۈKdcw7J5v?p1X@pؠlD/#$WL7@liElT#w)"ؖkb)!$5+vDۦhj$b4W6o{T%OgؕƛUEݯ_߶h@[QyTu-;#c |_yBs2f/kTp3x8Wm{fMPⱋPwqDzB-41#<_4eFD PI5ĮfDCyu# Zז&kkjsr -G0sNDJ,BgIGx/3QR(-hF˰9mПEdf}OeA7뎨?V7%R\f+%5z ]pHF ~_Y]Q(p])!M}G}v+ߣ2q}f@D(xpQu ~k85܇'6޲5|6m6DSf_+yu5j[jez,{VfQ=Cec/˰#+AZδ+9I~ݮvXaJ 6~#j [AMM ZyL]W =hECVSz,:}d{% eNc2h#ɧ[c:/yJPΙ,IGwk]t4fD/Vִ`W!U Em.?BqTv_fTʒϩ9{I BIhk%D|pGNrH1-TtvϚ-$1G euڔ_mU*Y iX#&~LJ;0luH-L=bz_Q:0{r?OBkX@b>ԇ¦hÜ |Ih[h[޳F's~z>'n8jH 5z}@OGos` -`7*? i,u5,Ӫ'䭏ڀ8B_=Šsӣг /l0 =t]EJg9E%6.W} 0v=죫\9YrL"NZ=v '-fφС3nEk`b~͓{--DC֗~3wkQoS'@^cxݿ<%߬fel^9_7ӛ3wILxpY9b702iuP0_'I<@4 sUc'Oԙ?<7;zu2&woG6ܬO IALEwMU޽u|"{y1K2HeMټh^F4q~3OƯ嗸ןf} !cޫS{;2}&Boǻ\P]Zt0d= x87}3qZѝY+Us,.gKQ{lW*FmfyŘI&!^I89Ї;nI H ,pL[63鮶dU^4cARbMgwdYyAF -}03ܹ^þ;㋧黉$L[,{[i~Yzaת8G _89jpzXwny- %9;)x -6r_o[/< *w2ܡcsHaZIV*tCQ'% ~O ;++|.#X5'לm^?C[ Mj1I4GSg=M|Gj^ZY$~s9fv&Sf!#|fs,"U5m֚v$p+[|9~):BN':OAh)*`Ŭ||+{EלgzVImi2x'Po=7ˈC&/ M`qCI؃~ݧJpxJx)̆'Zm`TVKC'S5A=.De~Kr#x2V"Z KxjMhfNsMHx8_F ˜ {ذ`5h;0-YFq=25/`dfx6kZkW|y_\yޤOwaw d\,N] -<p+MVmR[x񒏖z{(kQTBvW}I_KWZYƌ [ B/5~mE};zQ4yrw7upWs0d5Pk[Dug֡nYYoC a%Qݠ$bW| 65={v?jޅrb&E~Ҍ|1F;b3٩1VBT|B>`_܇lΜ@؃ t2.k?klbv̍}ɌB\J̡sU/֕9'{J "&eeҤWJ44;Ϭv36Ϝo7;ZF -8\,z0.`ow݌ -eaj}hU7DCԛ^Jֽiz%>y_ZKR%;Ϲ(ݹMWGaEnŏ%[basXӅ" -ڒ/5?x,C5'quVL6^kfI?ϩ/xd|TɪSFh*+bᲙQ9Cz -W&o;VkZ_1>HAMQM0:p:Rlʋy6:r`ɣuMbѻ3%)%lv"GfV:0u*I eXq $ם̭;6W5oͪ=B@[7WlٸkseXw!a+!j*\Ǘ8y!1Hё8gؖP\Ǵ:rR ]{u ۬*BkpM~e:`nPu7߰c(geDOt2UH?ՋcOM"ݨ-oGE5gC}e՞y<>6λ]:4WAtOZ~?HRwA*!,43t~l_N3w>\N_Kr|ԓֵeGbd3~i0-HE;I%CpR 4F7*H>~ 찊Qpa3`h|oL|[*4-ӻ o-MK/q@gJY g'.)+RiRObB'OgME,f7kQkȸyTF*ͱ(ty63r#+w]uanMIpO3u *qsl:75pJ~5=);^ xPWxmSbƚI+osm%:O4l;'Kg:pgߪ/~yԽOt{ |[D Fիĕf࿑;/17>%ʭQqayҜ~Nk>5ToaRjrqmՠMsQhˤUZû_-|TU{nK lՀ{&n&ԡB~@F٘t}km6'58-yl::WGƮQDM6VlWi${.`B`SytkUFJBk{iA4_\6<0 &Fdi@gJ3OJp`?$-G[o(, =ܮph=o4ֶuU1peydk-_#c;*5|k+gS҂E"b`3wEmgA#M*k_=BCx /4H~3MBn|*\m6"`6Tu{54TF•]ZC<V<]_̏9*v1NIN<i ܦW8 jIݬ\ZfoKg)k >wrN@+v2*2Ȗ/}[@ -=)*Iٱ&)q @ (q9JR0oaP"U1mZlK{Z|:rJ m?y_xK@U;Fm75^j]-*@אGN@ɯnݜj;Ie.jh"vo򜥭Wˮ$:S aP#VXh.6Z}ؿg`X>@4{wAy[[HKJ`e ֙X{blxg _ ~{O3rYg?u!׃?4?{:doϋmKhso Yi[6^_=?|ǩ[-:m8ɺJ%PqZ&v:p=ɷa^.oG;ow2܍k`Vm}IyKgF _R"ލk&lѸ¥ytO$_o#χ?'$؍+ShGü ;b˧^ZvV*V:ԇWf̹`aEG>bDf$/mu/{iG'%n_qs> #ҍ lE1s^3^myе]pWl5Y~ lbhz%8c,hr؀E:\v붠 -%ߝNlld0?K7|'^s~=)ųu]gev2좖ښ49mo:x*,u="@i>ub%7/`?ds4/7:kہO)z| zbeۘm!PswVR֜6jSwqƤY'3bf ԙ>Ǒ3_l{޵Ndm˕^Q|Kzdi&-!1;}zI#LzS| -߯x ҅;&F M=kڿ h R|rw} oo@kP{jG1M/s"v?ӼGQw]F#H -mfTAC(/G˦X\5)w:=y,+S8b ` 3n}ۻSlo42#VFծ2Y\ݷ%Q (kk=H`Nݡ j T | KC¨v ƇTWjMo}a5Uդr1bsUNa_'^L0yyɪLؓv}| X\0uOk8z/P9VT?F}h)w% -Yj CUs! JSѾ4.x׭J7H@b}cFGQ8rϨOF; q/v^\ZZ!L at0j]X5JC\7;Z$چWCy32Mv2'6|\%=;ϰ v8/ە1+ToK ik%O*AY(Rbsr:k5$%4%UvK=SM]j֛5UiMK&HxԼ<s'~ (5*ջ7 @UX(J(G߸wd3wh`K>"׻\\8m@嫤ZFyݕP2|PuK'-Y[7k3GN}c&b! U; ZQy -ẹg_8Viscg|b[\9\Éb g[%FEw`8G'\d},s޵:]ԔV"{l*xgѬKXx̶f1sxF8rkY#5.YnjM!~===M4Q󨏞n/Q;YHR).g(V{h1f-ktt)֧%lj-h;T*aUWpG5+@ƌ"C^u%|f.„..N&kvuˣ^~Yvo=f]x$h5kraI,XΖ7ׄ_&;tČIjPL7I+Z(AD.H!DiD +'@Zd/@՛-{睝LݻZ MZh^?L9Zjxft͙ F ay.[O^unq5K&ZP=yMVmkEJ6)LNJ>avTi%N -9W&'ジ aKzvuoԖuĂ^]YUQǪpUWZUtU`ýxyeh{5K}jh M0Ӄ\4)G(.pm<{=_W>JTV|N~[UvDx])PAp*W(4FNt9i揿lBg~wKB>$-0Fj)5xB-ĤB|j#I58Dg\Q(ȠtxgGR&;PzWth@pL|_y9`=s% ׺_3xl>uCW۔J߇Ѝ5ySɵ7±s+77s1E~ d~KP.w)0][0R|PO_PQd2Ee[XNA}聺m]@!KqЎBzC;]qV]vd**RX'iQ()BLhjv9#W^Z] -J1?P~,S -JSLz<( PxHV^vok0!2P(`b;Mм߳|@ @TTޓr.@!@t c # Kιg!@yE p -@9@˗ǖH 4u^Gu3zbG 1YZ9;p00O@LZH4r{PU(,;[#ߜ(=s{h J_OOdv݅ Z4c7',:IU@}@c yAu\̺i;vW!#0o 0&g)l>"MBI8&|T=8!,- <ƏJs7p/u{'osdp Pnp p|ybv g-߻qkoGC҄RlYm]FCe,Ifϯ׶xN}x{.h߈Zq-?Tr2G'f>.d{u-n pqmx-_g@'x엎sV9~m}c'aviw&ڈm|gu;͒]y~շ~%<󷄥_S5z7.f:W2P;IIO5>y|:`jfGƋ]Lj9m.[oRprj+" -Y1bhFVp4u`W2_2e[᷄EJiBeg-Y7إjFzRgu*Z{giK%/4f6ha9Q%v2Ÿ5)QT'x\hl)γ?_f^Ft>MG`U \&]\b]{-d .>}6<=sKW4Srn5ȴ<'evjJɴƘm{Mݽdoy{\8@ob2E,rl͆j9b8{?v1+ڬϋ)1=+mWle|?~W;8wxv҈NiNm t}zipDxinp+'}/nszrs|uѶB{t|/ͱs{;fGKh·sXvȀ7RIطw>0񍜶K%фxl3roACqF7m&1h:빿9RF>i'qǡ5;@m.?X -8}=Pz;v{o35[5^epդc[P+]u%mvxAE}Q° !.T[Ds?@_^ \rra9Igþ#ވN~!"Cڍ^]g9%/ /퉮^]fޚKjcՐs - b HiÓ< 2F2i4߃Wj鞥;LUČxL{IUx,aYÃH }CcVĭvF%z䦆 -vEG54DoSj'k1z93[%vs\C}n;LZ<| i/(c[PR_օnIFz5U Z9EńY9Ye\kﵟ~34tu=E1Hq0f32{6lT?.VIC#2zq$.=f4^P̰JC+) Ϸ1FyhlkNPnu,bp/Җ2yaXQ+p!~Hq˫D*Dw2VZ|؃1.w͹΁UEIg%Ds.Fn?v59.bQRz Yqha1/͵rSV'QęC%+IX{[N/l]U4G~-y2}x?|zY/W/gCW9iK(2F -'eҡ EҊڪivq}*Z5=r8ěg|$Fe銌I^$!(! CI_ qedśntSǠCo~(8^e?K*d*= VfPGU\cg< .i>GʊE7wgƲ |Vw)P3/,}lZS $Za%~Sq):vF *{Bjl7E:[RH2A5苃~w&jn'?0 HB].2:o:pPj-%zr n#Ţ#qu?^Ukh-4'J 9Qw 4B$]9H$bz'O!PD42(w Y_3:ùoZc7%VyҕŗVrlZnЍ8s˾ *P"ZXEMHGGP>g Q ٨Xm=p\fX}daJ:?}21AЧWͱ9 -ξBZ>C{?Rw4ۆoL]3ufԀn },>_]K~/>畳w8bح%X L[s4[2u.M2(Oާ]~&%ZL$$lN?7;z P沀/IPrHFi.̖`f#[MJ 9:F3B6-ÕF]ŻMuXJnf+]&ĘD:X"4l|]ܼV8s<.n{4u*nV§aޑ-g@굠,~^х[#ĉ9V{ZO.,7f?PSx-ѮDVvV?NK] ᑘx0Qk{,Fk#3[ÅbR1gʄM3 -d2o2U,mh28un}s:L*ևFqݨ -J{4 7W,})q׿`4]71lw8?)n9hF$ddB~1d^mFdR"[;W//P)@MN |T %@9d%ިjՎ-HaC=U5m80w?VȎc1*w۰pA] Czt RK -7TNZ)vu@`0~qf =@a8zz3npYWljiS3"8.2qBQZK);ɡ|!S7.r{sZB)|z6) TS8m`9l)g5@$рZT(cRsiyx\9MKp U!J4C+<7TcB6NpDN>Ҁ\(QQ-MF5U5>CjxSD -<Ҫ4h m)2O#&Y cvVv0_=YoŭO[W^̙kC<LuȤc͙fFҰ;f\'[nn3"qQ@+ݫqo 9H19^" krb4[]IJ;5JHn1sLnt {|vݽi o{|~S/~\7Wi -(((TVjP%Pm^L -K:ɦD?JJy_19̾#ʽsYZHN[n|~ŻR{ 5dI08:sN~ҎjiB1*eoVgArW fksYGv\xArGxAC8fCKӳRa!⏟P=iRR: ݻem66Nevj)kض6 G{6_7 ]4^b+t,/Sl9 ~' ӹ_m"cNW K-ف~L:liG#2W\&`ypp=XgC٣]u7毅)W= DfDtBm=6j>Nhc,\ѭ˿RQO_VK"OO? nK:ו>KDƅ!փf'/M76$vt3^ q?֒Ḟn}7ԞdWAv6./Ȋ{ryg÷w][?r+[ #vq-m4d$@Ct8ѭagGrbC. {DjԟMo>Y#Nv=: O5d{n'UIm\?'ᶝ0o ˵닒}3fS't1ƉrGCoIV<.yw77g9A' Hxj䱰2T; %-uuݖ_՛ jDi>DŽJ}έu\'d}q9O:+z.Y9f}2Qg,sakK 2a2ðElbZ}CJZjV7Gn9jL5aSϻ;(Vj-(Z{XHEUo2̯"կ҆_Qy˞lcñmguMtwhV+(Z>b3-7i43kKm\_`ƼlM&CP~ .6x(ci2Y\p!*걬q{ xF-S?RaY?|ou gJ]ZP֓3E(Z5J jcKErks,?۵u?oc+yXy;gkYnAnD]Bt&\Ŗ -(Az:|mpf WOWQ~W]~r;7hrzԘenv[x<:idaWw:`h1ҩekar̫tiҥ Jak\&m?Y-N| Y:^rH*7^*bs嗹HIwGx-s)_tsvʨTqٰZ3ҐablۣɧԩKpIbq@V@HK>zvULGy?&PRqdIU6xG[3&#,bocINfƹeV4~)] ;$C*L uenqL!S{P[TΥX{+ÍX|N_aGhCY|[5?l9s݅?;~va10>_?tIdc`!= -̥ŨB[,m?r;TVU9IF%b]4QS<2A=x -nHۀƙɸ@ڻh5Zd9I{L.wc Cn4B8MzBъEm ,͝| uѺanKB}D>5,́CWJNJfRJF&(< a hU8xTXݗngm I8So"ƳauZaJoۿP"fIKn:Pw%%4JbAw7XoJۈʈEu*J[gkQgwvGħ%m1gX}요nSvgJ{;mn5 U?J! -oq9K%GRx=Ilub?EeWKg0bR("OJ%?1y~b:?5?Sr.y~u?z7NFPԼRwȭ .53+u:*ޛ-Rs^T(9[YbUЩfGeOF7ړ37׀s9g04"xOW&Tv?r+[杼*ƫfsqְX\}-ﲕ:A?WQ- "U2d'N`pM#._ݬ5E3B>0;C05f'^g ՞?(3Ϊ nf|[ʓMuHO}2NZ!o e9nk'$ɛk[sQ]daKg}q
%CIBEս@UcDůHlѣYX9j3V?EgF[Ii=5X +xb鸸uȮc·ōf;>1J7bӢqJ%ƹC1yDޜWM/ٴs.V 2giKO*Yi*,yJL$v=Yz:EGr1\Do<:^G&B#/+Fg/`# -nZn}܍`>ΗS~Gky„ F!u35VdWg}!FEDP'&6Vqhۅ4 -\r[VַEݦ& LItdt [/SG0_RC0, sZk}xJ]N{<ԇa]q, [Voe:Jn R6M5ksg%_rSX\둷GuR[БAzYϥ׎+Iuq 0P*,'ɳ)-f e"zVAzl#rmc EkG\<ib(S(*,!c6N-y:d%F(E̖aO{ZɕБΆt>o|3ȝ\#KqJ9A oP!ٹBUˬx)ggkibJrvHmWRʈ\b[f_$}I) +|3u&gx">S"XC0Xg֦RjhfQ)*J褈WUӯȅ7RrDql |#`[Uҽ𘔻r6U؀>u!s¶>6AQ>-K*Z}Xܥ΀z,z "=#_Mq) J4WI)3;PwtktaIqb뮴$8W`Z&0#}l7_BqFPCyj-/`zK>{Q0 &ۀ - `&9V3Û00v IL3~{4K~jgJ̑ p}}=bBt? 2՛6P+`o^|A S`@4}I.de 8+ήF8=N+9@K ' K1㶎6~j-\:2& dܤ DrR[#@ TeAɩE3e8%^9 -`W8BL| y@)ND2ɢӤ.eP˒EZ-|H VnL~@_ZGBw$ro*:a~bd́@9 =?\)lV:ZRntII*w.24P)mÿ,oI_~OJ!(iQ6e}j&/@%.P*'_|!e(Wg mЫWe/G[HNZCNn1NJ]3w9Y^/!}ߖώ#ve* 6 փ(4|۽ j=%R[[*Ga|Cj_>M1zYriO93|6!)~)ޙs۽U`lWyDk-+U_+9^2gˡ~%1gK[Tr!NU|3[iVc.K`xs2w -![e_yz[o- Y78_}E&GzNk?hhKEa|@[|¸ܝLsdpϹ1C{8@dǓ-SF쁋!S|*< fsP{?ǃOT;OWq}uw:4mN,,1Nj8;N0qJxz "M gưNj -VZov'`u7Clo?^fpҖіR$bqHНQg|zn`L/v{V9:xUVW;l' 'RxjMn;`P,h46>KnD^~GpC4靵+T'ZqٜHJ6._ybٮ8X=9Z;\juhW c֓M y^ЄCSIZbah5>JQa#{בlxk/Tq1_![iOER?|{%( QRY:ޫ?wsg2t:. vu};-.zRAc bUW2kU}=֭{й^ʍ*G \UhF%I}XU,67M?-w7VjYmjB6ipJ5|=fVx9I))_D?ۜ:PkH>OweNNWV}MB:qnZQ ^*/vQAsZgV]]ϢZZP[R]:?M:|/u̠$>@Ē6{vv4P* -jexq[9w0-egI9|Fn`-If.+C;ZJmKlInse]{  %h^#\bp:B6d1B\wܬ>_ -1̹-@?BU[{x'2ˎiR;ɉ“ mBO5\3ѠF3ptӿ֕m}<Ӭ%Sr*z37nгa-3ծ/ɎBU8ɯ$!bR/?tԇ0A(i(>*_=*ܬ~p&.8]˧ȬW3j,FK֫3hyjr~)!6gf8mV(o6 ;0RFRkKW%ʧpהBuoXx3H68%},f3Mp9zzc>}3-fߧLP:3@bSA`Eh/fWzt;cqb OȮhřJ fOnvzN!?bȍyxϮizFHT 5!vc>Ԯ6]WMcƎTdV(XzF?0S]iZ~f^O~1N] l5/&frRwgFΛ99-(>S8nJro$9թ..0ZzY}Uܐun *a5٧xdxFw B fc^ycX@77 B+"J(S)zwǹpN8W zo B6q5۔EPOHl&M16ߺ*zVo h[ǐ[Ff^GosrvYuu23̝>$c7rrHK0)ED _X5\VKy zG5;*gᾝ]{gCsf75ѺUZy@ -ٙs>ͮ%{}8v]YPCI_jj"}RxO}иq!!e(siYK:Yi2qyq%84c|kg3FhsT[,ؖϱy_}"Cl:|B罞z~&Un6Sg<='^H0Ru@tb}t?>߿PMCnޖZF`t.t}jM] )aaT@MzNN<\@/qG)- I1 CBd@" s b^KO;JszT,@OߢldS9^OqU)2 @I$ z=HoAјtZF>xW<b(8ŠnQ5~a0fO{k h(m-G@WOr@Ow77'-IGx=@韈龍c84MuŵsjPXZKXa&( Ol,처V[x00#  f,XUcU -`.`*<k*=~O$`NdS7 UEq{Qn(nмl7n-goeI .QYU< ͕ܥZ/)9݃"'t('5ঢ় gJ/RoDKVG}蛐0] ۔? Εٿ__2rNR<΁5OߚSTbVf@¯qw6eN6IX~&SgidOmƬrx[a+>> V]e0^ Ȏ*|agqLiUX[iP8 ]>~z - ކka>Sw9oŧD g3l#:_ - 瑓ۨѩ&{~UVC~ .?kZׄǛ{46'#]9(^ -rHqɠs&g;f#,VCPo7bwʟ VG=䫹~nپ{p[RW[h`dA{-,vERN -w&|G'-z G2} 109hPn` ;ա>2S䞗SMd]|WvYCNQxj֞Pmu Z}Vg8oca\oY[>C>jqԣpgA$ܟARqiߨN@HuЏaQ㐍{;iWmV˶nn%d9f11RխʇEً`Y8xlEr2oӛD''_'!;0'e+pNP0aHm2t[=)6Y}6NQ#;3綮h,+8@裶Wj DbgJSI4)wX: %切<01+߭GS;iqGs_HÍ,2#/6OW7jʩS +M3A'Cm*ߢ.+0 3>~H;uMޅr( 1Tbۯ䘺kSi1w lv7MaU9gTD9N# ^)eKh8^W+{jP99-rr=3˹ieUr"nItVPN:ۗ6[|θ)$y8xPE1`z>qn}25*]AVCGW#Gt|_V-hMj> J}BQj!CSSA+mG~4.~`>Ӽ!ܔG X":* 3^`ƯD'ڹGYOSZ/o(_SlwnKFD2f촦sܕeYx UA?6g]> 988E9Y2,J -p?t /.>V~`IYly*TSSh/ڏez۵TU.C PZUjG)hmFs@h^uJ@1{ y&tþS443ԩjTSdhnлHPeہݒ"ɒi_FՄ -ݾf{49/"Pk8FUK8$`.13>ݡ77NҌjٙLI#aR$):'K6t -k%^Pxqxp(Y /ǿhZZ5;O3݃^^ŭXjE6gDŘm!EܣmZ^=R"n++]WGz"` -mt^7; ww)X&n+j'~KJz}oetZ>[rh1[bu_ZJDv-לLr׋!Yaö -^ycc:s -̘qA .߱n(Þw{9LzF;T5Gqʨbs tu{fk񵖧B; ׸;l&HB"l|ܓ}8y)ni -=[ z6,o8{C"au_!*|82B͖/$GWT@"Z!Jnp槑"]?ŮGŅV*E kyGmkv)Xs``o5`.)yA,!?+BkqP2JKzoЉõ]C{4EW&14!ŏV;Yey* -DxkM mkP - -2amZVcV=y@< o̷׳2Q٬nPH,})!kl+'>OyD>~=Ҙ침uQ+@ē jb0r ]%^l` -`f<чo;t 0{Uv3tl3:6p~oV|MNcXg̫|tkLB0i4v wZ:B-̛ur+x8lN,sH6̍wlUP5uw·،n_1hu"7E} -ed4RJ0^y6& 85=ۚx..5@zn Mr=E>ܴgWǎk -MߴC lg :8&b2%iHnQZp09*xЄUIH5oU W_S%g ^Á`$ ]M@[< -9(fQr bDL#[&oVpQ].=x!Q$j(\?B>pn?rTOsRiFsxv 8SP?JmP,=Ьa=-nd2aeV]|B~(}<Ч}*.vK?ytU'G`%32ٿ_7{NJ/e aS37m`Ap5h2z"k~.Vu ݬ9}X}YJsaUxޗlrEq{4)6<*"rng6~εGv4m?g.sa%b[..nE֜?+3Hz6RWr4ٓ+ fI;:5':[|r[z#qig[}zn̗g4>RvɲC.ϕAk3֎VE/,~kܻzwj nS!0Uvk*2L0F=gC^*<EֶD -'&a]J+sX65nKa뺃"P-^mo5~1/?^V2w;YSqCxYO|;jk[9 -CE*Q7CEm7nmƨQ؋Z<6ֵQ:sAkZU\]^7Bse Kۖ(U (ϝS_f/q _=yOEߗaņwsҪ'-L][!j#7 XS>C{n^rĜGCgֹx F򼁂6)1;\u\&:y:b٢'rNmuEbd ݱ{-TTߌZ %qm7Lg -y/WrB3yrE?o`?\̪ ڙ:_3 iU [[.IP%hzV.Ar;à)^Q}B 縿;FGA6 -hgd۽8^C$?/zf - 4/vr]u -y|d팺VpW -q&ŎżtK5zzt, up -Q7/>^OguOjfӸ/iSbY Cm.靊?UXB&=ؐKcl:vS), ױ9!yr 8薬t]ӵF!NР;%i$աWaA٧tΧ].Ȭ\5;ʹp=V&K-ּA]=ߺJ༹ oI9ٞŴo͸i"\:,[l76&oEt`26'X3ul'x?<:̶xT*yʺwY&Hry͎ԝ쉵Oax:XY9{[F͢R&%3fݙe,]ktKqb5WZSKuOSévRCui03uݺ?~:ql|J90|*Xyf*6r`22ʋA?е^ _ԾaZ35VeT)Ϩ)}~VJMF4RxjdV7.|,JkT -V5J1ȶB4WJd&oa].aK;Tx&޽jMy^RoQ~ d.HISzpIxemJxЗZ$"ŒPawa\HqKk5/TJ!*V[1]M#jք̶f_^e*Z44TAŅ2E 뾾JJ.pLJ{L5ƕKOW?n-'zWBhLNXypLR[$>v5ռe` < 99 iOkRߵ$ ES7)\Rei:'<_];>stream -pg(7&SC۲޲l|j]W-*+.nHӭbKxbdEZIR;]A.\2J:<ċ͝ -k2)^Lȶ5H+k: k/ ^ОMɳ@Wꂣ@.\ZMZ;=z&&.{#9z6X=(E=[l8#9epq(V)'# -[伨2<?(S;ëo d>g\wjh0ivf;exi.h–?9[-y(c0}񴘦GaeWѦR^R8:=&5&dJ.l@& fb -K`zX;ϒ*i+NP=$@_#ܔ{ Ke5mahhԔ`L*im\ -/frBmmT'{JKk^e^e?{^k81j+ŽpƎ(>o=i+W:E&Ҽ{tҹ Σ(0 iƓV&֛N7=4^0\LvE7RIlpu"l2%r7y<EOF>@%YzUNC1O=|cY\u tװ\Wí$gZoD6EgN.wBXBc$$3֩ei =,Tr8~N94N=oҀ*P!***-T7 4}Y/gwLu=-Tٔԡ,+6>4b\yCW-|.#`Wl.0iff .akM0 Н *HpXA)-@9A ؅agTb_wNs|ik |ބ'A}: 9ynY8d0y -z u Jи$x#$@&A3&'0A0E2aD3TU$_7Ɲ*3:!UZyBiӗ46=83]g0ǣ|&Q+oll$-{[ȯl`TS.}mx91`,;dI;V^2y{ lt;jRK0q$+NY+\ˀk5 3' ۟ ":wArk ,U_& ɓmnϦKQ$}MQBnh%N^[̆v;.z_"3Bx{•|@LX 6]30bp4>Jo&bM5G ZR7YYF-=oD>0P"AnSMIDK?+gy v,c MIH dvI3 u +b9} -d% ̬n\lw?(OwX1;/z[ۿE} -5G~[{xSm-@PSw F:@@[6qÕ ԑ]V{NHz^qE򧮯?G8np~ t[}_Yxx`0賋YF! UBo2z!)3:Dх‹4xnN?7-{wy" m7aU~G%eUeHO[i-c7y7\ݜ/կ_e>{f9Ư+!K^=4>e)ݾ۹2 6L?WZcf|}LdgaY'sg#Uqf^;9Ix[(Wp;Yu1Fy-z}.Û-Fu]p^QGir6Ef0zXQkLԸc~FuWP=ҭAGQg;֩PσRbZ7›x}sW77m溾Tg`Wkٿmyf3 ~y;s_^Qjw}t ^pЦږJ+Yvº|ս6kTmF7uuhDxuH+ CuL^߃{7.6zW;ߣ}Da]C^7MhToЩq@aj~QlUQtuP_ʼJ)8~7ayNrQqXϽU&~*J.hRrRk^( :u;ӵx$k(KU>!4RkWܫ[sTgVfTQsnn"T |lQvO-nj-tryNJ掁:p -X5[jZe5JUl*yW%..5.¢ԵQlWiKݎ2Q {vƆ615եr{ ?.fAi>Mڢ:Y ]{Y:$T;ֈ7MXMMsǽUSפ\lr윒A&734a1PCd -=>ʖ&mkb.f`~ͤJ؀N(KtN^lĚ2tPT@zʣ^)I,#=+H2'=+'M`BC~3WprP;mGndYs\P,R fQrf(nh8zgtq.hꥢ)KCHWm{X5o*l~&OwXv=anJ=HZm ^!&&WEPqrB#羸0}x/i- -3_d8v)]hNkv IQU5\R)(c YU0'0;,9noWl +j㷃6XY*x-Qw\;o巫]ƒ -:B5=B+3^=Qvm%#gbI>x6y-y^i -1XHwRN`%;>W_l[\tWE':>bxkN.*L+?ٞr9$R뚶*مhb R47( TV̗$AXdvj%RWF -/6wdm'gaV[9޺Lע_ˌBVِI%u}y'jn/ǞM bZQ-TU;jŦ3ZF.&u I;,r rr[*eؖYݬ3ttXZ@7= ʵl*5D_3*D*ݥJ ue»xoA ךʊ4)qї0 !xHr ipIu$;UrfWTܦ!QRSI I4 sȱL3L,s ̯xbYHbX{ tl|Ҧ4A݇z3Z1ژl+o%!KL 5}ML2Ĭy(c~7dv@C@&CeO}OjH%Ih&}#~#ͩQ!5L:08oxLͶޕiZf>@Ӫ*+/l*9*ebzM8Dô@Py -I:I@#U`J:>^~8ߟCJE:T:-`590id7 *ӧ<懘QtQ%ռМjm1 z"c*s9dD |x/P\3IPaڼRd93GVFL[B8M!CL!"ܮ2#]O&|G%)U8: < -7|.52+2:.VTA=)tl?zyN~ruy:@9uyzەŖ]N*</9ד43\M;R ʈNOɀ~`=%ef)@]P KT52ڍJ $l'q:<@VaRc75)N|.c:,bT\Jem¢fF+CqMhx@Z XЁg:>zhVEz Ns@9 |6 ^} 5-`BR:X?Y*ibUQyV񺯀$>`$`\dvKR9%Joe֮W#7` -XZ,[5IƘK]4 -mD^V9ci?S)YZb -ΤȤ`dl2.{xUbv <_S`< CɣܽnΟ>/V^j,1rQA':UĮ3155OI_rj_nZcw2ZFo"4gfID~8LY ChυT3Am\ح% 5+ɜ?5;˸_? =d uUHdT&sV@kiYkiM`@OM{c^Db<ĵcB14ݮ[o!@|l?;7~eovj'(u]ui@r$ Ti* -@B(7=]52K hu3E|]t~7w25?nۯorPz.7%@ON@ -9qz]_MFXi8}p;Xp^y;3>sgvb.v;W/M58}^!p{iޗ,n}O4rxh6(p#x%7m6%85&z mhL7~m[rWҴ.e%9'/0fi^424t:9o9I{KΈuopk[ۊӀcM]p]Wåx]~-s:ZXs{?N#-ro=0>C4_ńa3V'q~_'o?Bodgj=xTv}d6sЩCrh;iR퀗'm[׺k]K2D{YyZ~{#iÐ -}C&=yu^vv@omfu*j{6ϡ4{itVa_zsuy?h-c(?WWNޞo? uvGRk[X -:6[T/j_u kzu;~k#6y(3f[ITƳgXyU e ;mX,uz48wnp_|saIt#+so3:WXZ< Dle^ -LXe怦J:)4Zt1Jx)bh{I'~BǛnwHū|Γ'`D`b}1++'w7~PfR0n"qA]sC՗*tRT0!3w<17ʇj^׍lTYy\2άZn 1cPĴq"YyXQѷ2\U07$No;4_ -Si=O -f~6?VR6Hts[8\ve_/z7,gOayqmIoe Jo[d:?jTizRvϣՐfM{T';٤Lbyp/aUdk{bQ0eZ{i`λw̧2>K+epcgcg^FwD~tU:YEVIin^.ѵBӂ57*|e#;Ҝ߱>H@u҂ۢhayϲL -zjAuPJWV׎ijpV(V!5.{ Qʯ>tΐqAZO _r.#t|JΖ(֬e:MtMPzfs]$i&dk˩-_ TOՔQHBDAN+ aJI&KZ)3i{/&m(!y=<Ůh_UO}F[ಱyd}]m54y_BT[VrQPX)+ZC_ҧ4Au)=OM쥱&,3 -&o -H/+.-0n߽~Pn+uxqVɏC? T_w6U Y%=c43TaC~)6|pKJI4Y4)%]Hl_(nQn|9G̹J?pp8Iyzls./SN37njИjz<îw3i3?~DMcQZy.<߱ zir\%ul$'2{fۼ ':vLП?(c ukm?0_mQ:_A]Ȓ%Gbq"Bٹ*'Ϸo1W{!Np6/U7ʦtϨ@?q( `N"Iy bJFg*"p$"r1,pNSZ0gڈ=W@aHJSGhq+;l:|&OvČ`k vAr%R^_KNywAɅ F,R3(z_g |0ePP\QJwMyui.9*Pv !vj}otw_iN;3II-s#ZJ0,r1H|FE/"q戗JS]Ī6+&jUg ۭ˽<Ӵ܅`c7;.-T%%5r_2/N |'4؃JmJNGȝ$XE,vLIb}<\{ob]4ƪIF`GPa*rJPe*A #Ѱ 27D%pj‰8m sǓb[~S5~{rFRݝeWT[ZA=E\GU>Lo#0CoIwa{?N%g5NBâ=5+mQHUf6'Ku79isuSlw&kzŤu67W49tKBnBDگE2T>Nȝ}AcVg'CtxNJ7 - I |N|>&;~69LHvK"UN&˝009*iw<=zLlް#_ыy 'ηހ' "Op%cIQUo 4 rյ7{s"kv ]㠵+3▐[6&8_`"1e*<?!Chɷ$դ$8ဤ JJdj`%t* NN{R nك$io֪\衕)(+3eLSS2ࢴЫHrbU=@1  -ȅHNF~5D@ P"v('h1(JPH_c31GlkfQ-+Q,|}/4r{$S8c}_`PPdI F My@m -hq>g%)>:D' :ke4%OEжl<;_=2Yb2uP28:NCp@$?{d#J2a `F0v{ cj`R-` -0<&f$'w5[7\!E vӞg%BR)o{Up?"nȦ"`'`ohpjvb`qa\W] p8 }=9DzE&N2Sxhh72,8{'x xWGz!S? 'arQtf Ǿ1""߆Ef\OE{ͽg^L2;ROIj_+ }2ܯzz܁$l U%HӉXH4`I 5 C i+tikCOQUU+ʿ]|ɿǿ -6=tj/ ܖ@yc (kq y^Կ10pGdE0*tz 7 r_g״O^ІA$z-9޳b)6,<U<&9&̪ύ ~ տy~>'u]}|X\rk~q caKUFzɚbB*d女)mZFk$m[a(,[uAM+FnyH܃׵C[z |;~D{k~Wܾ6wl$H퍧XgPy3`:SermpIqf3}bnjI`mfm*9 5n-~{u4 u m1׆z?bxk3QbSJ^d -y:5}(oW^fGDx(ByA7n%9GnVm2ȿ$.Ae^T*y(ʯv9Ai۠RE1\9JzSN\/.J$S8 uֶ*mܚi^GrY$ns+mğ>YA]}tbh|Q}crՂ cfny?/ љ;En`_DsF1PQL(Y՟|sPz]D#a/fy:?8vrU}A?%jr'w*Wrnzr8Yr2vVvL%34)~P@̼%SU縰/fBϊM2s`Ōܙcc - ,5rԠrSL>۾"C1%5=T:c)XNT-m4nIfv?#sޞOͼ/MnM}'ū+44F.W3$41$s;v!s:#RVree4R4)X֡IY@dy̼|gM⌈ jRL'蛙l.^5˥8j֌`ooHh=Ac'I~ƴ_.~_zlzj7 >ήǪVӕT {➂qьesM9ΥқKy"5vmv!캹 m&r2Q3]D%[VeOD3>5s_xTUw츇%O[( UVZ TN5%IqrbVpQH%O9%Saxi1™4zY1bE0iI1iF emɄCq)F*BdL'vź;a_a޿p^۫oJᜭxe(e٤`՘Igpɠ ys9Ab:A-:UTxSŪjRK߫PϜv=OX }1QxC=dz]$nDlV<]:?\qtᎽ'6+ܼkS`Y*=;tnrk1H3nO6p -M*)ğ"H\ 6ʑ;=$:qX%=j&N#v|x[6ÊXs>MNkE 浀έMt -7#@yMP\^}F?OVɽ"X{Q7Y?$NʢvyuR`2R5aɦ9%=.']ltgA;.5w/9 cl3S1x[/)Ζqhõ]~S`F6Mwa$DͶPZIJA -d'\=uf&66N3q3V:;yɀÕO/*Z Y+m!r -[8/w(6s~Լkx}!$#ųjE9~j3N?)!Q|| ԳC!H|f_a ۍ;K{&k64VdD !vGuy;@PI}p@X {O q^=! -I+@xiT^F7{c]Y"[՛qR-֋-> +<]^g+,|`^hq\ xHz$ ;d -H cgBBj*FiԮ7i1=K-Wҫ)&j[B\߲>_G;@fG@6@~I2ro32 rB #Chx,؃;B ,[> NKg$`VT¥ Mw^LXL8/홒z{[ːIO$4wbB { STd M -0a,`xXy.TBغKҚIo7G5s܇_V;lpQEf -3O KOc݆D2RFp?_;ʸa -R|[~o&p5x -It*W  ײ㓞ͿYβ 6GbT}|{jrO=#% -Á߉OޅX8΀m$/@KĞ$\xJm 7X}buU&cڡ*ɼ?zr.QXDO p$pl˥p"p sPk (T(GU(lB -*6W@@j&>2Ug΂jMU'?]#_QyU^ -Мf -hGPm >ħk%O-5ZM\B9|԰٨Mg]zF DЎFߟwPG QTB=yF)UA1`&5-28 (qͩzF:v~n=c;#:֏OjC6e^2\ xe927_:^ ǜYiUap/<+Cm~QhIثh\xlp}SGv0|>Eg׌Q#e m}*& ͎ɹ>Mò2_-1V#t xUePb ";MT1tx xZ}%D;~n{9 *wAmڙL]c>=FuS xdĺ \9=RM7{JWKvgm'm-8"W^nЎȊQi$z{tCj <(a"E2*\whr$ְv~?):oepbݜ]3Oǯ ZEn׫;Q z*!YVͧᲓ="CW<uOe1mgyrSS+ Np% f+IsQ Zg'啩z|m rg^RCWڲX,% ~ n 33_DEaޡyx!匟G1TA^\Ebc zꡓhVk_ŭ!<Χ_?< 3+*kW T /)13OK\5֫}6igi6|F%CF#;*`ւFQ\׿REtMm+B$Nyn|#҇sUMf[Xdqmf98cm4tcL˽JЯY͒nwIf4KaDo;[<#{rI=Hsy eC(/^LҼR B& - [ռe9egYa|ow[̒X5r2klHЏ@c1&83#oB#XtQ Fut1dVd^R\$/œ7WX$β`&^-(:?USsa&HF>gÂcB׽*7N¦6hڠCAgX݌2hX̗3})d$ʤH4IzW㧫8ѰoC!lŘJ$U W$hfTeM}mi݇ }{))(c|OO@iUOe8 GFwBgðƳ`+vaLtOz.kqYKpP}':MT-9(n╌^> 1y>p,92|%oR>tE*t$ g,y X<ˮ3%1:Gkw~VdVUdudV`Hl'mi/㱳L<9TjĀ)1'V+Y[\<>+4J 1N?lη֕WÝ$w>im|&|En4@#r5bJt뛚:G%\X=\VRYČp-&nV㭰{XGNzgUKr}lbg]vYg9S8' \lzPu:Ev}Vy/y_RFikI- UėZBr ozdEM3${Ub;zΨ"1uQxV]~4NQV6[(W2 \x")?+Gw);:cͮ JTl:g}p}hCr=|J,jD{'h~s2ZNxVp`zHs^"N_ :\'9ZUuUpbb˅)V9eטAs&,GtKrM?9޺kްsacd+} ZM%8|~BF@,6mh|{A,˦K%kcF2uKMJ̆K`%|iS&};tPŋ]"(]B|jB; 5 _+:thrf'ϩ̥`}Sqwgek,LB4E6܋w.VM%viެӬ-+Ly}P^2K&ۻG4 -aoO,6&Mg_"cA=iBcgV,|Åpl36C !eB㒱SgïGb܀ O4OgW} "| `!qp9mܼ6M2WǏBk8hIR? 5V"cfgaWyIC|J턬nrC+؂45apLCR p= B48P) -G^z'tb -' ->l^Rʲ.;2{sC5gedN5&ܔfbׯ8qZQ]rc0. y 4>@*y_ru?^wȗT@: ̗EB=M-cjϝ!+/NJ6nC/ŒȜ؃OXNN7F@p pJn)//@* -C8E> :$u?8xx@ؕ`?o#wlPk&1$p_⺱^>CX| -{HLYh}K"# W.<%TP3KMAXU%F"- -ȷfl!r~DJ Ej։ֻYϗ7RoimWHZBRxk@PYjZ8| hM!4 O'Z.yh@3xԆ:&.Z4@a-WK!{G[${˽u!ro<1!`Q]F'㇨L d/:7@9 0dSLLSJP*C8/!lQ,`4! L>>z*avA'GG7ow{]xsASP}dR$*w@/H?ۆ~`Ȁu{i%xW<\P ˀm: `tlyf\?28DZ}awS}xtG$ -'Kɑ#82C1UJ:ZnpwD -ܲ0B [dyV} x-Mes PpK% -MJGNHO=9o x|JUx}TyCi@s%  XJ"?^R9H`,ɟ68ЋzOm$~~?#x?nHlQZݵr*P}(t@@ެ ׫(f%m?u۟>(*!7D nre#jm3Ǯqc]|hhBZl_꽳'9tuֹ#^j#6H>>o*4!&-r~nj-X1c6n<0~Byx£#OZ9t!]a10/+) .k:tLVl9nDEP\kpNsFc4:_Kpy 4*>OpNέ^z_f( -6j"wZOzbM6.6m =;y0,1:/"i{#fkfxbNZ#7F4" JlcWylSnu$6 rF~a.rXlƹcu*. M ɹ2Ozܱ6H{?έ8-3M?}qW;r=w ڏ e-Pkݤz(4ޯF~OH }߹LoQy{_x/O/nc|Pϫݞ:I@j'L`@֛k5 -k3~#6s̺U^P_4Y-9U6=i.^YHjQQ%{ asӽ֐O{ƪJx +AVY(P(9y:]|B-'Et[a^ )_Zx WMvarzAft&co94rBB Іpt$nz+ p$HdV|' ZFEVە126H ʎJpz7}q0(tॉKwA+˕x$Z$f[I=#6&/ Q̴b=}vv}JPəX<˩ԷǤ V -@=95Gل7d9]'wSgAOyza5:4ԲI5E`@Ɠ~jye1zJ;6։#`ǫIfޱXCzaqDbbLϛxWۏ:ZzLz":]l1}i .[r(%S{/JhߎFJ$!9CBR0kj,ǘIA 76]ZBdGZN{W-Rך,d@ɘI޳JZ eBMUf/ &Sn)֮[85_i:ApE }\=!$ipՒ-ZyvۺnRO_LMZS>$K_\UZlj)1'V+݊误79Эg}$V%0+F>|bGld^. y޿乍m ;92]%ϱRʼ%1џvh"t!%4J 1؜|gJkYt*4]7dw!:waG׳+#ˆ֌8ڳ Pq^g V.G3)_8zWΔ*Cc#N5_kNZmzWqqk\{1 !gtF=v 6볦X!0ܐ5¡73{e VH-ٝʿ8ENu$WuBdX@l:cTb1by2p9C[`pFY-SO'nr:d^zAEgߏyMjZn"ٴ"ՁPڼ7&jaIM4r˓ˮة:)a]1m?ܠڅ@˨)syj vё>\,Uʍ(w\v9mQE~ o[4:6a4[cf7Nu'= -*j7Y4.;lzȨwy"I)+@8|tF+&x(_ -b}fm?]3m3/ޥp8h -v3\6oYN+uXYdz!4v4{hţH[I: Wrh\U=Lb&/J}Lãp% c9D7o4/1MR4 -4NOeB˳FDdL{m:fjyNѦKVvP!vMعiO&4|XrXq׃dc3 l<6QRnXgky{-DޚSr)SG!ܠw\3WW=z$D4񶞁jMt&j7w`)pBB&K%.E^~i+H'+=K~8>܅X,"`t x$vc))d<`ww~~gV#:{P]:}S[o擵9|̷DriK|B$OF#Y|n|>Q^</7,pQzE\lsKD;/o7OIEzrd$ҏ9?">O; m#6rL z u`aXRB̨Qpv_S$r|Lʿғo?HZF ?UɶPN\S?lG\ٟm.NZB}ȴó -Uc .<Mw(a4PՉclll*(OBr}#}?noOgfCD䧀3(ᓑmH$|3H8]==!&]m}+H˩ -mcKglމkK k-og&%Mt|#d?*NSUүީ}3`ֲkp$TJ~[pkV #-4uᭃ5u:ՊwO{f`>= )b:9NLkJn24,nI-C_N;fAv{I<8[cו.ׂ߲{c3OT ntվ߫?]/OT #T T T ~O"@};S.;2#èv}nX -Pz7}Cjk颏K껊_~vf {>PX̝n!-;g̳~[TK -%.Rkݮ,Skt,aD#'Cw%>v,1ezFq038}8P̧ʝY,˕ʄ;nyJ8^CoφǞٞ ]3t6\YmG܊\?_n -篫\Xpa$OxE:/MLθa*G8P \Ϯ}ur -=|^_iS͝3 fbt9uJ ׂp+Ytr6O2R; Tqwx23uKj7$rOcg=~תWNAqnvL̑htYګ❫R~-h߯O- ;ȻILwԃ3睆qWx֑hbثA/{ֳeBP:|F0UQcƎEf.k@lϼYSv00}rЭ{ 6{`J&Z8.%*][G<:/9 {B#?8a*2%z"u_SgzuXmOux.;x -ȵ:Ty>KFkYBJO:Ve/ct`wOW~t߫Β6SpgWw ~5;6-έ\Uf!.*=YWo[9eb,YمĒ;R79/m4MOV/4a\i~rz{/7ˍCvm|D t ;6> 0n^T=˹Wt_i+}fOq ٺCҜb~МBsfb?.$BE&}"FÄiHn c{ ^ygc/Z[mo7$]Z {ګ@8{ lElh4f?./ n>xdKxu!:m-fCf)~NLxVa)Ϟ6?\'#ڱz]ƭgS~MY7WP:ўtǓ֭=?_߃LYDҫT^+޺e)4cgzQJ힤TW fٵ? Sbc_6Rv)mw)sj?TYO]nߎJv߇tt[<n (|, & x\G[ҘMzJ\h:)C۪ dN7mw͸~LA£|Loi=;_\&\_#]܅QM>ړvϡmr(-{Y/7/Eؒfnj[N_ʹgKc?4ÙR~5ZM|'8m:GuwY%V#,c֩rsmؒrS+LgXY|990?σd?Qj:l{ږ严+矆/|ÔpcSw~MTh]r\yKzfc,jX  -v=3n##58k\w ƞ#zv6w8͸;_XN&E|Єm10: |D/opwp6/wvm?&<ʿ&\%+Ϲ+h埪';\ih4jDSh~nQXO C<-\kO>E?t&ݳ$*֕ zO=\^N[ﻻ5B4z۹x6W18:N[zh}NhN݋e%qL8f)fK-d%3 v=R#N{"fRo#a]?&R4yRRo~~MʲoR_NI&܃Pxr?08$:a=y<{8H du KȘ i#ltlH` iF@8i/<|k/HtR=XKE*0D`Ғɚ_`ĝ=`;l #g /7/)׵NFGZڕsy4kx42OpDHtnQUI[c1CWFe0-ܳ8?_k.=keݏ] ѽ_5NGkHR&ȳ;^kp>yvR^xkΩf.F[)x8EH4]s%J쇨6]$L"Tj&2Ҽ}pRW{ﮁtj#ko࣏8Wb9D9 b\&6몺4y7 w8{*(WFrh9<żدП]p7_gkb`ᣏwϟ .Ob#3`kC -eJj"\ԹGɞV(uEDeW:^ -h)._\&g; 5ڹ-2R/VG߈eYr;:ULy?&tz(u$;I#ה[<Q'B٣dlCQ҇&mm3m\ܝ]&=T.%5J폛'Y@Bt}2xR.hDp$ldȶq$^cxID6FcLԦ aI+cNM3X W9$֙whoQ "qgEoO2KNw1cǢ&&[ _.7u·l_jD&A-DRVaVik-mqߨnS>Ӎƺ><4W~j.WSg$}/էA0 -Dg}gy%eS^ڬFl!K:@mn0̗͓ ]jf(t\)hum'&7)B\[pLϧHm<|A}? koBlk=gx?D^5IPzjAk#-fPț𰵥zn|5ML_y"%oj,Y{fNA<ɹD [DZӇ4[j^|ۄ`8hpqޡ_=s9q\pNSx'sHt&1藇~'/ē\LēPZ][kMvf?׺vd uΒjWsf&ş?=7PRT/֪ߵVRsV\{%RluCJb7r΅dЋ_k֕8֙˟ȳ(\G,cES$%%,汔$G</'L홰ۓ(Qz/%\y j{P>噎{mj>XsLrfl:BV(h; ӭȞc9 O$=9OЙ{>|te- JY\#kYe\z_Qo7AFU4u4@K'0{ OA]XmwWM̒CUTb۟JwFg:lr+M7'(n*v㏎4=:ǃ_{?/CܾQw`y)3 x<+>.ox(ؤv[FXq"'oN(GX@?V`u'@,M :єPZ% _gu -7ު$>Z e6ŠݹD!AѰ`Oix rzwL}p -]VPr< t%qf^ܡn WEA&N?O+\ɨp g$:_roIrKm\6nS\M8ZŽt9zٞ|(LR~;Hx!1kL{Ʉ2,уwQ<='aSJ JjQ-|P8"yi.ŶZsljxckuT4f&YҶJi\/(GN7sgS!0aY~)B[8WJTSVDXc :Kl@귯N>((1t4`',JwA?4|C3kzz`҉QN AJ哘{nok[H*`KKc_+r[@F= ! !>#ܦD}H3êlnky<2Y m0Jh2^dGKtdWJ[訮``xƪIBv2quz9+SZͣ.it9 R8؛һ|,&DZEh5 ; -`X"?n~3 kƏv@k}* OJ ;Tʑ2J`x>5CB`/Vu_"౽#wJeA}mzl 酝Zju<ˌ׽-sHK4þj_.ui>-R"9S`bKߔ@Nt<(=t _g^rV:E*Qt Z=[bp*awqM^nt檷c|]ޱHiKH6!Vt >.Ao-#}{蒿KX|E&!b;%pu _A:C%SUWfT31\/̾32*άEY=C·EFm| , <7;uـ- -fۿ) d0D |ڣ -WCsC`A&nN[nkX>kQ~XcR~y~nRz˿:nY嘯_k\FI :)FdqpAL{jnU۠*ජf1h6ײw<ᔈ/\ EjLsdm-7Ŭ.5+F*5#n[FjWקYrpꊋ1V7i7gGڳ??MMr~nJ=oZpڗiAm_'pb%[,+M}"l|=*27 p-_fl!t&&N''-ŒԖ\7i_zʈܷvwޔ%PXAs k-rXAßRxOC֫ŗ Qgi[mj'(7"] BJ7zߗ8A?C򇠟@ l?0AE? `L̾jh -F;HF(#[ԫ0 c NjOJPO -%"Q]WOi(߯/aʂxR;19Ffy3{t4FWYKÝ52Y%cGmutvwD2=})>$}FēZaeĻ/\lc,Lr7xm#{Ы$djDUn巣Kő۹9@ت'_wrXCYՎ~+RJ; Ŀ@uM#^lN]kxF KOa+?|ܑЕl,aM8tB82y}w̝I?(Auq;If:Y#L/*q}X^",sʃTÕ,F/ŃCLa݁<{1Ġ~)_AWJAm|ms6:Zs,z -&E>"r4W^Tә: /gn3 2-$}lc(u@W =a[]ZZrNG˾kO?/_ -QU5k -#%yq{IN Lx/0wWJij?{.I|fZh3zoHURF5@78%noO:%Eן 1; +W"[g?Oar^ԳLʮXCU/}FrL$ݤ_ҵ T -J_AŬٙcЫk]d5 =e|a~?|OAm!=Xxc)kAs+uYs& - g7aě}:=a5sN^vj}Gf(ȷeRpI};?#֋Z{#{a -Q$Ї$Ʃxxv!Ng[׋CM0] Bt*Q` -, -A~$u'F8 Q m Ė4@XFo ukQ5H:)R4M34*ׯAk|KOITBqyQkx0O9ÍPp*2g, ~;L^)X ^iԺtSl XmL$dשGjHe5Zn(ݯ(]x(%mJ%<84V p/շs;3HMd8 jв4ɪ)+ʗRKylTF .җC aiO}5AV2yХZQu{Ӷe%08oWfpNTz#Q-:gxgddjj -+3~_c%pS.2gz/G$ -p^v 'R+kozU?.5_@?J$wJz8*Ҽgҫ%r0vJD>5@N-j6) 8^盥e W៞IVB!Neڥ*If~wvG_a`VP.~[@?(+c9Ћ)- ڭ9JuLB2r_-{6 w,*v -Moub$ĠoIXT?C!VrKe9! 7#3nv2j=-ˆ\ ̓ZLToIF,K_$S#~ ?y@!zc'k+=J{2R Hk{x!ō f:7I#0 -•D=Krb汝Z]Q/l> ^b^XHz^ @oI%|q&wr&,&ᲊч),1 nqiLf1>ٮd[yNWg򐝯QX~H qvPS⪷.d272y8m]1T(d:j^M&Jgwۖ7 -3/gWjy:Rm,h][vi};J CB}r%` U;8KS45 p#]q66:}YuNg>ūa]/̧9'ScphWYɿZNcRPyH]B|fMݞ_rhrEZQ/r.ON"MN>9mƘPrq.1臆_ hSO'!vY4JpjC:;-Omg+2Vf> u 8nR[蘘ظ"ѕEU[9Dɞ4)=uoI (b׃LF:90.퍏ޫS,Z8=!\ufq{wGʌ>9b0nփifp߇5'ߗ2ayIK[ۺJ5IOt[qYS(^G]/tm VOZt>^yU|zK!㻀6L1A?!A 'SdM|XF(Z6](7yO5g+g[jwLGqkM?^'%6G K ,a&O-L_{|.T%)o(ma9'/ I2XNb4k?06WSuQDɕz`toJ ,W%r6D6"a $š̎aF7/^wTJU}?nX(pRs /ѼslE"]mԕQOI>4C/ -<#šE\WIeEXJlLlԒHQߒƴAVf/L;4> @ 繠=%r;lf:ej+,k-'+{uNjƱ\FD;RR{: -Jچ#ڤ.+M#~5؂KSjSN(DbTK\eR@xX8m0))~U:ү -VTwoIXxvӽnMz6MD ${;ޜ!1Aγ̩a"EYQ[;NczDfNΡ'pCG+V+]g$l/hijþm,5*|X%T;oϜZ7*z|< +}qxTL|0q4?"[Q殎]Jnzߒ]C }6-ja`{RIL>S#ꠇ4"K\b9mLl>-RGlj쀀8m}'W¤F \:.m5$B|jBja|n`rsR9LN".M*2>Cm&ָn?n8R^5DjO]*3d9C}Y]~2\hY!܌5ԴbL(h4<|lȚ+bO+9gf a[KD[-/~c_#DFrv⥺zm1Rٶ%&xn0bꐟX[U:/=2+?S~zZp-!5GHa_{&6giؚI\8: jT1su# +I} gO^A] Ƶ9QNtFvgopn3˿CCXxNL ǎc=tca=ˣICӹVӾVVʵ|Jakxt -#u<#zK1: gtBP@_;JTR얋c41{tw_߂ٷ7/=@ťڄ' -xs5//7LRVp|}Qnfɱ| ٣͜SgG/@W -OG%r̀ `tFG~KZc8Ġ~Da鵌,H?{˺V]a c!H;K:gVJp_iS ﰆGUo63]D(feXΕAD| ˍgpx3㝭^0)r}Lwvl G h>^)JDb PDW@ok,xZϷUx֤DR&YmT=pZ☺03/ʆ#Ymr(MR(TCy9,uWiVz -ԫ|gTb?H Y뇄%O~@+a98kaX`DžC>f]TE{\KVYl3BegFȼU^GlՈXj+!{ ! ERb ajbqvVG,l-8N8¾[*XcT;s]7TR談tihs*'06RKh'ΚIԎԂ|TSFgq(qA$j0qYU6:8&-ƂEF-WlztI4@f8lܔA[`>+ay~ˆe'ȍVQOt]e g2 c~Poe)ד YovCY;G<(,A[۝)_K w.,ԝQz*eUokugHPp';|'X91=qܔ2/cкVKlWM Re} /BXh a 1=Pf›opq-0E @¦LVo ="ڳnȾtj͉i Qr}hTyh݉Y9.cQ^)pS =[tqn-A\w*j^~OA.5q~v2$7fϋȠ2̺hn޶[{hTSXs%(5 -eˤoa*VnwZ{1\."m l78z p|KIŊAU?rgnȵsVپ:l]]/R9͛zV -: >DG)t0],+{W,>9ce7ouΉ4N.mZ@q-r̊͛BMfΚ^FrbPi~cXPo"HVB?’J| - CT>,K]bУ*߷O*et^]7\1מ0nnyݖ7140Aq]wA?4|ς~31'0HOA?62u !:,vXH aa-?rXWnws W-EOvÂb-x&7t U:ºJTYȦqL -q?,eg˅`J;؝ka)nTQ7?>! 5" XbFiEE𾷆^˵CkW鹤2+iF厝ȆDcJep|, j`%^n>]O=CUg~hl"fbEGBXŹ -we_ܨq+.C$Kȉԝvu8 . ~Vھأxoє^I' :0 ] 9 jJ{f>O[a*{sEَ@k(;] fqj o++XbZcd98uFy tn}P]χ qRNbaiO'u ^>}E 9rza\X9Q*`P9k -$S陆93\XU;EOz&Gq2jBL)|i񿔨~X,oπ@$`uÒ(azaXTrerљ{FҠcSg~0 5W4iOzU¾guIP~_&D<4 +OQ`N HqL:iオ_s2b 0bB{Ycy;>b=chԅ9X$x{u?+2nI%s@'C4k,[+st䕪y|s(Ҁ^p<6/hE[Q%`ZU">kSnW.0 5bk:ŖwA4'_g8sz<ʓ@{#ǡB]\W?e.ܿev74e&Wu4ᧀqµD=1Ln/Ûa]kTyYe_ wbM׉Ñ[<]qk&7Km | +},}Ua\-͖SmNB?2 t>`6oT7y'|S ׋KQgyS2T6z~q;m`:gcv`ڶs[~mhϲ> '= -=}il]Ό ѴH߷ѿ2^qʹh i͔tLo zbF^Xއߵ2G W|_NRnC79xJmv#Χå?GMa 5͙ٗZ{\X֯׊Q2^r6W6/2Sj -'-\i^Ny -X`,uk!.Nڻ,o9vFӇr̍JޛA^vw1V8_%m3OjС4u/\u5Bg3y{j=M)w/J¥eG= w6'!{DZi|HűhiskF7KgƥaݷVk҃E2w/&'147![|L7.%FfFTMQu-x ? rm ro4՘9;}WMoZ .}\z}tLN`Zt̀ߵKoK5[lz3{]ӌ D^ZP/a@SV.`4uukǬu-ܾ}2aWIMuy%qW׹ީFWqZ襳`j;׿E1VnH-{@f=A7ԛ09F݀v$?.?'nڑV'k ‰knwUʮ9H-rwe-AU y4 -7U KiR _¡NQp2(TH{j2:'tURNwj 0gޯO]Q+;e}| PJ;Sz3%eQ2ɳ$y0k8#۠G%0_ynDV㒩Y.rtSnɾD7|vjY~TUZrRm+M2Ce#B(l#m{(+,\oƎϿ 3xtۊ=Nu-;7K)# -*=J^AjZI z$M`hf xF[TѮou8Z=y:׍Ӂp<,R[f#֎_zcgU;؊9:|렑@Z77^?ϾԷs*ڸXzBiVK"~kD,׾6Wգ^-_mOL?Hj\&g5Q,K&y_叮]%(_gm\4ҨQ:/{nnZNc^~؜>L g9n&[;-tӢ>Md Wb`doϕhA+_FRz,Hoqry.sʅNJ*hVϪcawY?ȯjwFl5 Fb/bBߑbPg,y1;,c0u -ۡ^{t{B6. -8%&.t - -8,b;x|kgY&\ -2B VoV -dd H4ojů9y`NL&v ZNyڸNٹ*ݿ*&szwS>{6Թ h12SV* !1ڀD;@{ C8͒uņj$TחyfRO|&%w oo2^L5L,)^p<^Cd y?]^| yl(@ k0~Bn~bυJpI+_6*]gLEx1>vZ:0Y]3:*wv,h/W[,Av,> J0ۤTI7p\> uMXVDƫeK;&>/ Edo8x写t) @VkhȊdd ΀rD:vj"_|xr7T/^g !#kyiƽh{QZGm ǁa:bZR*=> - ;4j (-/,% By9aFǙ)j|)F+sKv;uZVD*yPݍN9o}<_ޏd!7/ ] t 6 [= ->U^瀶&pgɳлDߙ[ݯ+U/sT>Kr04; s')>;\^syeYeKl'XjQwlgH'NٻG.V݌6J3t2aWr&=emufxf=A$@>?Xa|7yO@F@nQN?o.q)zZzr38h?򫰊okNlRv6mN;c{ܥ>Q;\{eng@~ߓ[h1$Fi}hՎICo dAA]Lr. -ƶ͑ 5g\.p^#qFA~(L(IKu֟o1EJpoNɻH290ROhϘ?kwz?]ޜDXUN)e }򚮟 ѭd:1)^jn-;o;릭;kkw (|^]99(SxLi×6OusErnkcjÁrn)W9yR>ͲDdtkx=֠@N4%(JUk'[7 -|˵MvƧ[Mmb.̈JLN.Ei[wMl} 6n3=O W5'k7"w-Ty]#HJau4HLD~Xɢf'Y.=eTlѶ Xqo[!Nq5 ELb\-\i|JnvpeM#^8F~8`4U}bqjtZϴmt񆑹FSsN?th'q~G8}2+fP8_i6O짿UΩz;~ì."ɜSKcI~ Ci{niZ'XFW!jP|(Om ?"*w~IMe~qxK I֖úQ.po4sk/uŖ֦eC/ V>,[3!MH|e>1^e9ʰs}왥T5q^kj%*0,Z%l{굶A3i}UOЪY -fZK&>DK9k+h }go+=un.d)n'C}8ΛւuNFqE- jC'ZTrq4T_3k%"ͬuƎ؛{xZ FG8EzeT]So{]wyeS=wRZj׳vs -&. ; 0>7AՇ-[]Kz9PZ4prDT?Ylפ7S2:hkމ7n!a~2Xg;YzBwTIv0y5k%~ݜGҞa>J'ݯHnRRBH\|OەV -oLW.] *+=gWo~Z8q-_؞l?QmB|F^E\rfZGOK{U*Z(=F$o7~7e\e}NDp3]1۫8`UjzOfM&z] ի_U(+q.xi4U u5XՆ%MS5z_BPU _Avf< zx֟qs ;Vh.gq+?:kWUTiTfuPO% ->[)WNAl8lW2]8sL |1,r3F>? vo=zf\VK+n UVvN$"Bi9tTh~>\ymخp r5[.t$Y걯T(ih0x`^2"Ncc1si~Ix'MU{kJjd֛kqfZDQ ?nܖP^a^ -53(Kg .B\d0ckBj-`I+P ?+ʫT;Sin:{b~8tD kjk -T;C9֗b2IZ4 -WoՕҥ#ޕcf<}DKp//ǻ`h`6YzgoJ^3+rZ jB8!uE>s'^=a8K+^o<#{D c g!m6# J_oAxC-Z6 vH@H 6 WT9A/YK`ܑWnt'=D v=n=lܯu~ }yw\[rSh-ȄZ ErwvFb=w-Vـj RdVn %>/ŸV~{> G>ݯѦ{w[v|>O2i L;DZ '}(ԟA3|jK.z! o, yf 8DMv -.^O?ϙ\&G)8T[oܞ-UiᧈD\$.TkyocotaNy.=PО@mԳpܑR,=:Ww]s.X9xD vYPn[ NhR}c>ca eF;(s>2+EP65yq@@7=@SvOJd$U!Un/VFԯS]SZx|C0uatsba*lJ ;8:'k@У#1&gU&>p;.PM -hjfh&vu9@/s\}/=OGYc~j6'kyNǾ9u h8\Aҩ~Ȱ^qx.>Ư|fuX>^>jc'#kwv{b8h#'ƿe z[2͏ rs=#ϪseǨs;G\d #Z0n Z ֽɱv~m8*V^(W[b=S(**nN -`r1M{iӘH0>5I֑ʬʃ1|XM=r9ɲ)|Fuj X޿gA dHzTR1;r(kK5/ N?msky\0^(K9khN;25ait]xErGY粃U{`KqxaPf<-4Lg2܌8<~ԫQ'mc񵪝 7oXϧo|3lFW]ph`IoOo RbP|Xg⑆2Wʒ Ї.읒ER Q.r"=Z("s=%>Vml.V)<{-7KT ^ -Ĵҟ Ua 9*A&e+#}b±zv%lwFefN{e{5·LѨJnxLj獙+UwX+Z4kۍ_Ç`7^ Ae藥`*}5]F8nw_XMluN}Rΰ*1Cy(PQ-wpbMeu_MY#ﰋZf8b ˼;pùݿ+Ü Öt;/5=۩v@zeK:3Y.Pm;4|-)u׾#wͬmAVZ*dfVO -s۪= zOZg-;4OgWX[e#AL!-&Oz1q <;w7錷^f>ox{ 'ݓzGs)گn,HK8wdIgYn9uYKU: jep(iשgT&{Brs?*_B+)ib'ŅT)A$)m16@zЖs"l_̗JF|rs5u%65Ծ82ScUHp>NV:zZ3 wP{ =-;XQכ$F|zl~H%4L'>E]hZtUʤp@ٕL ֱZMj ?_׀|60=vaZENu)Fakx0pe7g:it"}I(*eKRJWJ6Ӻ<$^WAa\^f!Jaxɾ2y>0vhdɣS r=)ۜsz8 ^%i7X&~JBkD:wE k -Ti\:`NYa_O;79)~z^TX?vJӳZ mS)KkB̛ ÀJ+Kf+`ּ)oj~xykӥνjsSCk-eoJ"V -UssVvV@oL>l%jHֽ<k6ȒNRK>YgC3w"rDKưKUNPѹ=+K)-2gX@jۙMǹh-rvszg `< DÆYS`ٌkBhQ ǝLt\Džc'''))v[dzEWiL΁E&:!}A~5Ű^21.EXKp\=ԃB|d )!J%D1EWX.sw%s,\4TsIMM=vI5 `D|.!^xC$?X0-ҟ}@ ?0,;tڧ.R@42҃H mOA _god#1W[ -P|4Z,LS8?iP|@VUPjBi4TBaҜ硠\'FmtYN9:߷*.z-J-aRxv >P.T:Chf <7(kטvJ*>|j?Gί13;`T-,DTZp!,pj\<<8KAq`Dh\Z~ݢ ϯ)O% -jPIono<8 mogیzG7-֝Cd@0 -$gܙFf15V}3Φ!@;+T|4֠) h3f$xX\vW7er +TN+Y_A s.t#jy#I+pxkLju  j<Rȷ_4>ЊUI6E=`𢳕m ;ڏTZ~ ?oyeV"(P_u[b=h],(j ] x0~9M#uMLi`/c0sWৄ mc)uZ]B}Uܡo02>uj R0.@z -HfMbPP-@IKCWhW]>e}Ȿ&vs{"lY\[NkBbmۊ~ -zh}uLY(3U\` -`d@D \q~\Q\r{~C,O8EN-5 -ZGy)s/Nqi$&g|H%)m B?y(^yfhPljXb<.ᔎHOsƾr~LvH Mv)LL2> -P\2}GнУ(F½8!?F7}uի\uv6w,t$`f\ |?k8v`| ̖c88woAvxG;/|8fcީ?oo"D( !l/W*JpN&ƒ9ڊr[K pLX]eCKVǒӠ}n%v;4_j_-26һ -zəPÇtzxF燗>N8mOfW9ҜXj]YMAp0 -L6ǀp7ȕ>` -oy9dyϰ \kba:8~L88𭓝lGWYNFw"ͱf9}P`ֻ92WvZVOc]G)Ef5tdTdl 07.5-/<:&evSW?7(a,ϜYKyֆƫ䯟5Eokm$.dxW9u^:FgBCV+5A$yWo!WXC,# B<SaM^)AX%1#XZ@PBY.t:cqN#=l-d/{lꔕ}e$FfVj<^vBy^PXBH#%lVRF9r\2zdk#K<|p~7m4w_]) ̘+Q Я?0RjZnlvT*Ns}EECi^-E=pC[%t-ҨpS\g}"͋Fjb_G$IY%2* -nn.' Y؍:=**JHKmh *a=U bC1g<;MJvS۷Ta5z_-1F gW@_Bo)ʬrc4c15#azzmMa;=lnUGù. .WV1]mW? -9Y ufaҟt4陋snsEkg4F%fkhfS -@e ][?tj^XE_ܼ;9No+V?LOm9-nkR#hR"MIIĢRfѴs\@]Z1Jwn.];v^ -Pq --u(pH)4 -O N~+f/w*wQ6#- QÂIZ|C$ݵ:iKMޝBYTA-yU"ys}W6yVH1;+f&YqJ$1"s+/HX01wЮ \D4E@];RPm:Ժz9 .@V5vP[Ȗk2r5a|5ZId[#սȶ[\ʼ,ռ( Ж(r;2L"aoΚ İ7db|/Z PJ {ؿ\~Z [{Bg 2 ƽ1Ԉz^eF.@ w: VYԇ`'/m314: ]ѵ`" ^jM#h#֛'YbyN:+\'}vD՗&x-T7HuK{ɫK`d 2qݔod] :L݀k9Z-cm wztx3>V`t(1 :XJӰ#F @zJ>Wx A6!\'s Q\߃3MvZ,[zZf1A36:˿xpKX433rȚ&ͷļ:d=6/*ݓx]׷h|)xd!pP#}Oh@\ƀSa*xd[:^%=}R HZ,8vuALXU ʞq4(]-;*/3ؾ2=ۀ8tπ> Yݕ/to\g&~(PEEWf.so_]VGa>%,;Sl؀}u) fˇ MUp+A'b("("v؀ bz;Ω1Wx2ro6dI23lf[=K qs*w{Jhzˠr n gh miԁKPcA;h4D?eYin/#`K(ywVy,Α&2WB&|,=ߧwjlM$\Y?t%lGeZ0͂Zx9^KTPh@6@QL~f7L.#U3<=楫{w!M֭|OٝE:P'(]XoG,Κbt^驨STiOz OL?x~m}-(t>{/i}ѵ_K4+/$=J;wzrwn#RZr.[qLsf:'< 5dO 5p8gҨ~> @2GguA8A>V,# /B;ͯ;u/eG>k4^ Ӷ5-]1>AMֶ Eӱ/q.I]3(l42||/`d:q:nlsd<8(z{%-MLy.e]Eȵģ?֛{ZWo@z kdŲO$ܟ4:sȃիPWth/Fq7y;gNl3/S oZcmI P3WSX+ûo%]?R.T ?Ŀu@NP)En\;C%DE׫c<}ʕ%c-o7 v#xT ճ8<`q[і[e@㱫6zs34RۢCěʁ~3e`=/%}wk!11x*ѣ?؇B` -s{qxN$Es{ԇv!uw(T.Z?,Bۋ7C'5Nܺmo,O Eo7wE "S|TtoSt5Ԁ)D0^<;{3%, 3oni-O&1eQz-Yk~R$It!Yi!eOp;my.|?m©ɚC_g=/G=nbۈ㍄4$c5IsskӡYyl5'٭ȟr⽓3x+Jq-6rltnsvaJNlwd]!.@d IwMQouY~0ݣW~ X o`*"_?޵kKW;}Ѧqh96JFrozڇ "k2d݌KgK^xs=nԳ^s[N4{vId6 -0 ZR9._춶yEŘWg =^=V\óvAL8-d8ܢ=E\_x'e+: ک 2_Q&q뢥G-6J6i-1{|Svm5Xv''l+]ˬˬIfE6SPl+: ĕ"m;IحK+y*IQ42ft'ޞ=' 9n?DF"uM90{8x2r9S:Vm5VXs5= kg'#|kVשoYLaij2^{Q;*ee_Uɛ/ ͺt̀Zol=Lv\զT54)FWYse oVp/K4)Qv٣P-mؖ?o,]>~03w?FOӟE_tqoׁ6hDnՇYWZ]hwj+ʔPľ'fWE! -cM6y:ݾb?]4=O,4W $ҋGr۪G&O4'Q=M&Sy靪%%EQ -vTtdosբPB7OS腏5UQQeQprCyz_:^s'38o]"ZreZ+HKLAʍ"Hct$Yǘ~r}82AO2Oe[S*NT׏Ss -BO3uyZ``LXsn}2}j -@py: Mt2U@u`[咏ғL -\o -W.ΡLaܢw$FZoy 0rO(G08~z `a"Pɚ302/YG\~ÛID7x+Z.g Paj@P2⭙ U7HUH!)|%AxpN>8z2 NIm6s9 q_ɗs. $n^΄]^>L.C[](@f ɭ#s|@?k m؂D~b؄A]uYpQ᡿Z!:|ɽ_(=5w?LZ9(XգIγR̒M/ :&e#@ݟ\7AߎuKy+T{kšHjb]y㟟?OS@P]3O12R|)T5b-/z)n{[|r藖 Y:C0#qX7@~é (W`$ - o rzH}C4ɺM`Z߳ 8ogGv{nn..60֜>Zk$Ǭ= y&[fs _m'4uW`tY @1| cΞt,ߣR{%;d3z>x>-<ʯӼ:ƽ{Tokpn3'.9m\r㬕"sJɾ@cG?~Q/HďQeuJ48oWuBկ) P^~끮..!8>ln"^’ת}15lKC@ݔNcu>\C/7oƐ+C&[t8V8pCu &DB?ތ{߬b=8U,0dLo|ܿ2MGkt{F|h'}7p<݉T#Ũ)#Rq P9Ex 9hy(S}dS7,q.t<gxt>g6&Sq#vn l; mcSwmͬnJ:/alڂ/ q(f|<..mq3dsmhC9N P_RX#QdK3Jf`\2Xf'ya2KۜW> ]_ ,9s8.ϻ{B -nמg. 1;ΗQ.߷)4v mzYk| ң̔=Mfn+߭d,I}',{.͈^~GY!cy(g˧*Qxs##| |4o/ڏY3哔A7#41};#z*grVA@#]l 6/@ NQ8gTl}kᶚ溛em6s]?GK{1 c@%tPYˌ–ZվdgFYt; vy 4m嗗^2}a3T$ٌЋ7Ωk+ZNlޫްԂ̊h+zUd`Rs}o=uȣn0MlLSjN>XN - Y.;tPSƃ\kѫt~ܛW@0efU;K̵VkԚ-{ 0xzQ~dddՏ~_~a7kJQt@kBPCM,Am'vmW%ܳ‰ѵY̋ơ6..^ -ҵ{T ȴ m刾o*zOR9jq/*z- -1+mMKb^Gޏoؐy+ע=qЏW٦TY$v3b-c -r]Bu.թ_!jY,K[0jZ3Ȼ},i*?!IL2v=KK5RaVW%[{ҟDpkCT:WR ]vKQbuI R/k7v6OO`\z+ƪK޳zkcgZ,c-5/7]|ŞȌ~AvM'+y.ʡK1WW?L.>J4g,WaWL3n)宪νy \yhqې}P:Ƅ=EZRZЏRZd{Z,i v,hLHZR:$rJ9,Pą!e1 ?\nzQрS!N|TN͖.Y?2ݫZTKY*aiǝd4*RX&-1)7WX#k2uud<_mM7''*T *"ӒrK'W.ɕy~681Vxj֚9M֎8mZ>QJ.X* i1B"j -V֐V@\ -B)@ Џ]X3i,Sص:*Y HB6X}O7E@;@c "ho!@f 1CO@ַ@L*s{C2 7+4ԸBQl| ,%?Z-1kP24] n - uܺo[n ~*l ElCi0{Z]92+*tʰ62g`?{HX5* :@p 91ĝhXv*&-T b/ϝ|A|xIe$v6roYC9Q̓K7]HMT%)` /o[W -}E@sr9lSϞ)!}ZxFp]PR-e(4 c =5W'G<,) -Blf7!p\^>\N}} P#cI\ YqVaܕξk@TOm}Uf{Ը2n\O-d_l~lysJ3O``g{9*',n,6\^/ 㘺Zx2$ާGe;DM787`C-%ov&!v@9 b~ɲ( jL7fr>zZea]\~ۏȊA`q0{t޻z6 Z ȕ}m7k;:oӋ׍Nt˨yue "-?M Cgv[.}XHOig#5?_̼dl+{&W/pD~*T{dkVVER| + - |My#<}w%)_pkn -CMywtL*2jչlq52QX z*k6c/U܍Fp|2uC<Ŏ c^`f=x2ʊlFteICQ3?VߚZ4ykx>6jSbtN&]?K=چrC.~7Zd79BUGTӟ3Ʈ3rUU ^W]/=F*a@/.w%4<'E}\j_[-ۑr?Ħ *]G ٞ3A{Gy4=DPJT7ܦ\x;d4b-yvBNۜ u)ڋ,],o6İ*0]fsڟ2)̦*܊Ny:h&bSo9=)-p پ˽(]Oc?gn3^HT3;EӶ4MorG%\3c#y؋->lzn)}䳬lP;!j:Y"1UNNG-h endstream endobj 46 0 obj <>stream -U$whѦ5jVt/xjbvzbj\{Kn8=z[D~_JFƾ1zPEtñp޻1"5]#-lSPoHlYo۪a*|ܨ3rQ'.gjNcҠ> "N˨gUtBf[ɂA(KW6Hft+vjU:%֧4qʲQߔ7NPiptQTBB\/_%x,06oAg"NY[8O)F.bx+fgu{1cjT\xz;Y~Eߔ?D1 %d<ӄ1{ZrqiK$IZYĹGܶ:U=ꤽ? -.;:r?3eNAcXM rse՗T䒒L3{BrWrـδ^@HKU%:#3OCTqQIɖJ<%"N~- 1#S.F)C0Jk~THԢTvPyQY*z/gDw!.Y.ϥQXL I -KbD8STyǓ= iq"j4ՔNu8/4_9fU_qRhNN*J)n,eUS*T?Sjk|;0&/8Z^#ZÃXHIc2bKD8GZMK7YTjk;!62vG|}1gV.@L8yS^VCgԬC,GG S>;b9jE"P.el@ "MtK߶dmx50p,w ^Y * /L |Zn[TC~U -( -6@ר -P5ZB p *@BX&@&5@)spo[75M|ovd_G6_st:}Aq1ް0{Cl6&o7] <p/&e -9CЊX(v!Fm6g9~oqn\OGemv4:QS5"9Laj|`w0M5|ze K$DzC 6"3:&=&tx9Cq/x&|Pep)<Ҁ@M@T* r xp|O!WvE]_Us+unw#:$e/zvR9j+\\4P?IJ ɃApw2g7mhy8n[»{Ps q.ky©҆&Zs`{ Gښ:aH'|LPIZ;z~[|vq8{חXBR#ZC|vQFhr -7y~vg^i'kFTSџS]iu].XoA\\siz68/ i~χ!2o(4J @ ^^=ոxʧߩpJe"]Sf'O+=4ovp:68> @R݋orSnH SgFN5 ,HqTXj|Kt6};IH -gv^_~C7WVR;Ck߻坲cbnڭvH5Mug~#%/a>20KV!,}Z7 ?Phȃ-Ґyۻ NorvVSǍPi|gb E7% cu^{}ٲsQ b}{9ޣyR[5MMf gRQPR7͐@)\9a#^]dE@e-oWc\OC&U_[HUۑq>b>?EexkE/1ᨙ06--&d8߳&Yve}94|^$13ݷꙓUfJl;ne6=;u7G &;צJ{҇E7*#mJ C -b,K9l}14^3Y};_)dm{c$V@qM̮;~䴖秾n˛Q LS%2^Z\qTVScaJ-ӭ*[ *⬡(SD }!R -U$4RyH/$c:ӜT++8Cϫq1d49qjFQͰc,ZxRSq߷eOVJVS^V+*=/wOˉ Lr =1'VtO?6N|`!6]ldN8+}sP$Ɉz!C+g#0snnݏk<=G}l:O0͈p\b5bzؼ%եB5r!^RbwKS2-T9ؼp毼p1X~Sꕮ: ;L>zX(Ԟ~4N1v}]fٞ 6gkl}h˙Lļa+*oG3K8ƎO>j9ϻ,c^V[谭Q/'X'z_39WDӕGjLf -?7[xJ- s[< 1&n$)EH,{Fo&E xz(1eP,!͍ޫ+d8Q %1 [ixibe/s^D)ޏq~}@*eRQ-wvڊ` szָ\5k4ʽDz'\؄X+Zw8."|vv}qZB+E zݵhb@j `%R,l%0Y}*-TD=թ*ޭuKª659Ʃ-B3QRa[brrtY}Y$3(g>$|vW%{/+>(IXeoTn{JiD.WM#Sl -||Qr^~Sh>OVcJk͢F]~P1"q{ob<*KrLDU 7p29|zJYWZ"&Q)iԞu^h媔1 lh>F#hb z@LJ9 1\qEusө f 6^aSdhgX W1 UPJ=G?"WQ槉!d.bzfI#T݅F+!dY]bd -jA]Etʬm"*d@%>GHc39;y&]TC/`aIaZ9^/?,Xs_:4_[j~"l07ѻO -!?uqjoq:F8%8EV9{sΗ=`wQgь?U"ŀ8>w_>k3~{>Ni#߉" ;ss/%дL%P)1h~3 $NR0vt.o2)dq3S]z^i;|isYl#7`uJ~ K2ۀ@WeulzsK6X\Z&9]ɃSV뱺=.J 2 VJ@ 'g1:<-rQ~)^gitbGJaޞHp4Ax`ij:r ;!'G-+u/(63@jƽ1B-xoCT,'ڳ4[~GC->jd邍QCą< Zڗ]<$) # "0a8}i}{k܇ϣOHr.n݊jy<9|,}_jAѥ?Tg[D>WXkU=6?`_'H&X# (_K&4,j ѱ E7^{Yvf1G;oE P4 Vgu>S7'1P4Q6wc9JuE8z!|x9q$R&oqQ+Z, %E{h[zcxͩ6us58Få~ R -Jq (UHpjM/5Ϛ6-_G8]~{* ^yl(xSddZaA' Z -6ӡdt[ -tkemnkmܣ_@KJ 2AaZo,J_2}}3cSO:{w}gwU -t[&f׋vFn^[^pb=Tv}X򎐝%9 AR-ZJ~s`/ *wxʏ3n=!E%hDE `ɭ™-,p~[C|{OH#SJNhÊ^; %y4hxU~47L(jGM>ϡh&0(Lx\)>s-\TqٕWX5Fw` fi)5N^Ze/5#j]T[YpšQq<%ck<3QWʳEMOG{*_䴢uЪ瘷} Zew^EG87 \/~@mfbnl22vLi(M{ehcxPq J2l \($7Rv _"׃QzhDY.ߨNQAQya43sɹ k}S뼦Fm`A,?N4;j/$+ y׌fʷ" WZ9; |I'"i}!Ey}pXE?3,v=$3~v~]8=h5++7IWLEPSulds=+{.R{g0Qj-+#aمB涬}ߖj\85#(! -J|V&x+Y_P,<"k:QQQ3TF< :OHK&?vA(op9|hZsT+˙|A kv2/2/ɵ/ -W+v]eksCK\%jW/PD/sd`0d3Z}+l{]jx氈 aeʘH慑2h92sPEVrvwbrx4S.7?ئed|\{V::5h]3O;-s(LP;'1ea8mrܰ9KF{~T[T#|hct^=j]((DhY̴ t4mYQ.MTJm+ A) -O?؟_.{iLr,lޜ{z 횦rH'i^I"oqajBx{}ZfOa131ˆik.NnYg'Xs | }2ۣT wY챫>#3|ɑNUzCjY6}7{srV`aNӡҐbY` #Qxt#t,'[7cim(\lFoy. -U'ݨ\浢Ix9O q¶u'anCHGʫ(Q -J66/V7fK#K*iL_|"pT-v-jKi5gQ5zm"7d*)Uʵ)2.ΐPCnX:{/7Ҫ=MQ~6󈒡s̓ ZzӭbU=OUo4 -;x̽QޔxqX+/vrN:X~V9^P[%кZ4*\GB/}\) -yMD͛G$3umyAۨ sW3{% -UX/{ݎR΄;RRsA8V\pXCp[Ha*/V9 u#~MbP3eϐQĴFJ:R^6y5ٚDn92(^ \Bay5W=J& 9Z2;e!I?м^Yy=}n5S:|׈~!MVCj[ۖJA/VkDYe٬Gf=$S&f Dt49Q#Irc堘Tё_h}YݝjB\K;o 1m S/SeW[.$7/|޼,W͢(N(D6]Y\aartB/D4K)b2(v}'a1Qg2`:J/3mt|HL-΋r -e5,"~q ]*hE}iVC,idYWiCWw$Ĵd b;Xh5"}7SX[jt4oDè^s/8U2ά^rݳ.9yao5S5m9CgX;,'"N%93Q8ykLUy'. h~؟⤛y0`,?#1N7@T - -?,/ߤV%*4^^籉A'4re&N.8N$u :VnKp:m\8yP:IOa.Nx_|B?4OFD~灇8T/?H.@^ 8SP])<?G~|뀳`xVpğQN7,dOGK57ޘiQOa-J`Hw> beW NNjYg;NCpoP9Ұ -qJzqntc͆o/Ϡ向Vɨl@]k>fi?1=/`&4M@/M 'k T(k(űsh`17J?{s߃yw'^hV蔝7(w3zPIN+$lM^\Xi\?vA\{7`PKfo Rì߽vr&{kx|ndzDNh㚬K'Vӿxvvq<\6bT?Vfʡ1ˁfAK j`=r)Ab ~gߘ=ЛG5aeޟr{bڥeGҙ^B -HaA:ƴyXk".%Ʌ?˞w[Y2/VR4R|W\wz oƘՉŊanB -%H\`.!'{T}]s om@*)Wp*Ѱ7+kwdԐå>GYwy{IZ[Ī4.򱫬C{O:7.> )xVPt6$[2="5/{wvB[J47xvKb~1n cU9UP4^\<*4 D??+lTΎ'E:#_`=>?^'onمW0z\{T(m| [anCȘ>9144ccҪ^(CWn`C5B|%׻.5E 5^Ĥk 6:M| -/{>DMyecp\g9[G7qb]YᅊcɘYd .,+5K9fBp&3NRVdٕ4aL.!wWsq`;Va+ϗD؎3_c.^`;nhuCNK;o$`m c^Kt,!?آoJcΠ'/HJ2!SdvS'a}F{#77m?u|c%G{u$[l^v vv-nlΧ|,&hWV5(3-gh̬}ȴe'1W<^6 -|s遗 -J4{s+짿42yw1Dyg*HTheG7'0͎‡Ֆ=Zϼj7"{qZ9gk$_܍,8죵Xw,\<-a.Y zP=C|f:5qID}%˟pxI>zrO9.rȱ'qH8 -9<_Rt>E|4}G[svZyO`}Ynj6FH/L3+dA|ܬ]qv=m[/"ںlw1K.!J*"z<*{}Qaw輦jCBJRXT2ܶU"09ii;&Wܯ=}5զjWתhTuXѥ, -3sU֒mu:h,Oj:)w&{ZA;։~QUHc2>4f֨5M}lLGH_Si~2.]K>)O}Qαy~j{߬cW5r|Vd´%dȜht̢^he-VHm3,6Af?X6W]G JU("Riz -SZWs+#WY~vr|ȩG&U 2)k/A/rFu:4w4#SW<\vøx0?q v!tLgf6PϤIG)X b،51h: -ϭ3$E޻+5?+ KL>MqP,^t +o+kWGD.m3[$mc!vXݦ!XDuzE2huv+"? H&2[wx[\\Q3=mKRqT[R/U{(K8,}^A ?N2NpiCL"`A =tufG[wտ1t} _ a[%\G&8r [UvQZgwn7VJoJ 6ߨ ڒeA AuY"ƟCyP8??u'1W%$^pGHW1h83`>8R'x<`])XsD@?s4:)A&95Z6qQC6V/i8 PDZ\C|c .TW|`T ?Թ4Hw@roRK @}:B}#=l}'3r8p/<}>87b9ؠJdn-/?y乛^ѧ9Ͽq[*{oEDˁWzgfOş9?Vk2`1+b5/p~lud_gcbe)oq,8*{֗ |ZlkP;Z)~P0~oh'І/$K2ƻn 6=}p^zWwXvLܓ9>PmUr)m)=})*+/],k*k^{Z}8!xy`_ 4L6#O_eɤĭ8/كI6{coTmy?F釸j"5QeZߏY4$dwZf-y;A$r s'm>|}xIPnD>fL&(u t֪czα -u.up1RN.3Pyܠ4<^xR{|UԜ`cgqc._Hd__@lN|}*Gvګ 72a7mn[7ݙfDAٞngjN -eL5NWmP_KeIzWs:7b-a$;퓪>{lھdZJ=KF㵮o ,ʯ]& XʑmڧAVk/+&Ļ UGX -gkm5lY:gyX)wU!pĀq;gSnݝ贻Wk$hCor\ Q -YTӮQUFBoQ]Y[ܓn,BC=y._%^EJL:LwLg?eglg8.tJsIqR7slTjܶ>WŰvYZsū*vЯ@P+;T&jkfeğM,mr`.'-<%ڃʁL78[..=8Kjs5Nc,iQ}EXZ5BO%ӯWVW`)6+yC'L1[±&#[:RӹO^`տGvr}d>j_i`VSkf'Y]SY]2 ~q3.3jű *iz 03x~vD/>Q-N޶(}ad1|>*OZ÷ a l{>L1!Lx,YX~;dУQh9?RjG _h.KnvJl.z5.*Teγ.2Upb+@.Զ)V\.9j z5`(iQ(]AAm :?+urw.[W&3/}ub'@ S,\KOz.^p ~^ :C T."%\KW2Pcw!:]_`_hr܈0pcgWh57[ATWXg1!>w{JD;Dw+E | z z!:Zi ]mbпwf?cUX2>`ei+ P8Bͺ߅z|}(F#/ӯg{fHvgc2/>K+kj)}O0]+Ovɂ(N)'SO8! 5bk^Nܨ6Eh.cQjо@=|WW?KFz-NM@Q6ӓ_t^У~ -"Һ8feTʈt,oGwEF%|4]H1w56 -|ƨxl]Ù,kmn -P7j C7A]Z%N{Ƴ -{yS-oV;} t74Aq^{R?. y\npaiy»=5͝u;Fۇ"ej78mΪN7EWprr_gu'e7E+ozcuupRFy߿/bgђ5 Ez|Mi07ӝYWUr6zwIKӆ޶zQʹeְ0 ed-|Dkp\nt_Ycf2߭T lc#g}K{d5gVUz+vU+ږ;?+Mf0=^-k9;/6eH'F]k5:KXϽZyF&Zk|U.VȕXԵ`앜vKPao旜+b"Om.+ -DE2ECCZK՚tZ/EىѐX*ceafH-%YҖwy'y-V71P!w{kЬW3SiTRF:'˦mo#9&m a:/ߧ /޻ C=.Dr ̻?e{af;!Eq4(rq*c<PέQ4I{ޫ<(ӄ]- Cb/MIWQ{蘒&99߹daar9yT(c>1sk,j7y/<HcyrpZU]󶳪Ms -<&*Fo:9!weպn24dI8]GnwioH7H?`S2ٶ\Z,@9Ao ?ǭt3+Auةd8:,9t[MMԇBe F4/g-gjg,˝ũ)DhFe-{Y)JOyXLDфJrh7g<)4 -\8vH^?{9bY1=fc:6;/ishAHKdvh8`ZpiQPx0#XXUM >>5mbdW@Cbޫ#nQә]bE>_4 <ۮZ#dr}m$Dm]k4Z^N -Qy ms" a:/\c-="{StmׯMOlMc6˒,0S6 /I6hw腧}+"u޼\E8Rٵx]a+V6uJΪoXc /Ndqo=-?o$o3ZTYQgIS]z񯄼$-xiJTxk!μx<94,Z4G~@f_Lmڎf y1m^}|d_UW, |k -CQY/*P~^+7P,|@tn9H;}V|t||hN_ȵ>^u2lǧn9ieSl5{R8Hyr;}uIcT -{GAkz-KAESk0rR\={JYJ夳 -:Y3S^nvvE>*<$?5 Oi#0ElKsfsFJóSo&ޝ-^/ϸRQ²g RXyTÜfip6s:}]C>EќYZ1>yHJn訩1{S+ɦjsvSʆ\:+fu >(u7o]ꀽSFٳ0ˤf~@GƑWOy^դQӗI<<$v) , -rZh $ AF 6{Hs9ui;Kʾp|Iosˍ횎̔ ڋ%yND _ċ0aK3. -ƥ@g\2i'*g,DsvU8;68n5$%W2o_5ۋV+߻®8-cIM×0.Ϸd=ΰQ6DB=,?j,k%XjٮVue7??i8c@1O -@QY̔7Wu"jd[;\^-(r? >{"0zkj)wQ+duKŪ^$jm= -*NfWҫ|e2NC8-E^ZX;g[2I|7J5sh_[Z[fTymfAzDXB\ky zxH.#|.2VhoB)0?=uymbe6K0W{;/-LJM{)כ?p*ϡ !zg/j3Wx[7{mjJ<*}I09S.q9+ uJ7_@?2'50_ҧ5nKy}nvt1:[a^7Ÿ+mx}q -󍟠S: FF - N !P - 4ȅwȥ3䥶M;1/|65-#[ -Z-)̍I,( F*lU|:i^ Ú~9D2@U@q@i'ku]ZcV+Qʱ -ݕ'~g%@ρ_pN{46%UEWKTibFVz-]TK/%oihh 8&Y*;y`vfOjɎH,)_T;ord5Q|,{4-1f]4xң- ގ~q8X)5m$hemrZ64 ' h ~g͗$I0Rv^E ݧ'$)̧,>,SgJYx\)y]>Kwx_b$yܴSJتgǃw>]{ϻYn9SRgϩuYMGn]#mmp+gֻi~ҸībևK4NA[MrH+AGZ@ٜ3s:5U\:E*s97gkI̴"Q$R?],h1w񁕼(ebt<5گl{ԞeXoZ&j4h --yŗʜ[Y/[i^oY8EF㫱XtTC - 8x@MZ <@/(E62Tk4,4Nlͭa~.34)1ZTh4!kÊ3 (+7wȖ#ݍL~f{%ɮKpu*ӣFƷ@iz5xg`br:\gaU|4dMп֠F;޽hz63pXYvT ZpBvsLGZ^tV9s]okf'ѤġN -^?H"~ر  ~fGQTڽ d;%7Xf'D]CY.w֫3/a̬U:Zx[ET.Oruamެynۺ NȋW~a0gEڞ 7ZE#Kr>\vCC -=@yTȹ[gKp8XKvj)|ۗ&p ~zDƹ+N[z~&l޶{N[5 Q.kNfLc VEL(<}`?0hE`m{kL$b1f>(ė^ljCj2p<8hr:9#6j}fjoƂXd3pn)qCvXe҄XQ۲o)'o/W_\2yv]5XAH|8lhĴ@) ˅fĹI8_9%qڛY26ϙ蝮'f_ X`Ͷ-Niҁ -a۹SSwF7GSZae x`-n,m٥QMR|}!\q}3Xkkf(ODa{y;+y5A?a ZV:< ꘃ@J`8ͧ)g8i=ϕst\{sW -brNg͒-e?v8 ]a/jtVyI]mO8~';R5*h+#IM[\ѫlۭQ>Є2wL?[Ɍv;l)},1hk~@ZΊ>_Ǒg,6ۜ0~>f?[u'#,Oú#DCWZ_0~q$\2Iջ(JԹciPdy#b<"v',O{ϋ&̓ VbvSJ9+=/Eymm(<2OH;bU[dM= UBn#MiȈ>m@uZ Dz59kUVVgŨJG:,P-e|rpY~]I#Xy&{ i|+F&Idfק\vN1:S6*;psYF.tNROKx)B)) -IT:T`EC(}ByC Ɨa\Y8tA2r~gaq ld#3ߵ*N-X{)oKmJt JFI ̭SU"Nl]ṟ$ٺ}fyf+ɕ_uZw;P1L:[}C*[>mV'i}n~HebVKmHp> Ncc۔B}{:I3}n?t1[}UKF}B^(Wr!.qȘ7;/ #aZ2ӆ ,e d::ĆĶ -t;x KfJYX u яPըX/eW[Y(BL: | oz<HκT(LTj<6j+juu?[ݲoVw֌Ҟ玿S`s6dܩ^'&2%d{``){_ $f4c+y,v<Oq1Rqgg.7-ўQc4;x-]z]oyPD{'Ppg >^~I})ڻNvh3ے4NlW&/l!ʓ-PLaE-a]>VGϯ+yP'ǷQ(SreװCuU5s$ z^7[ݭcE^e}K&}.޳+7̯H^׊S&gcH.v'W0SX_R#ZwmMl8msu -K@i/ދ_3o,oBkYyrH˴R jƜ. *yC2yO >nKnON+vy -KkEst -_#x.Mz Ot56y1IQ.MF V;B;!޽nP1.nL7KzN}'_<{MxFfLcoԌ P|vŨ r&ܷ|yn-xf$"I0~ ڒIg#>v.d}׾W)IުUiq5B%~M3ZjT*E@Az:KRfVC7+fE'n5҉gap?Fnc>?O[}?l@-ٮƌa>5u#P@ T*2^2H^I\Ci뱤`UqY/(|0-Δ"7&)}`U/.{-gCgV]L|M;*;Ǔ^ jܸ \YWA*Ru]sNq=TcYkm/wWt7J -bSH\+ FeZJQSn84-}&2A:+?/mLCBoCCv6M4{l_ukVv?*;U)(1cݒ6b!`DxnHPj|X"Z_40C`UHVu. h勉K<)6р>->RN^%4GF2/&#*s 4y&}͍Պ3i\t Q o] I~bm"¦jvACk=ϙ/? :o/M~TߖveCE1y8鼐:Ƙ@KZ1Ԇ:+؋OIf|!| ͤlTr HZZݺ#:Uh՛dѪ1Pmxٹ2ak&ux 5Yt[8riO!M69t)Df{ <%~^P)Yճwvw:GdlE,z -@ kD`mLw;a љ}cl lcg@ -10-:CMu6+F@wP,bg&%>P[(0M ƨxC, ׹ͪsǥɵWc -/?w3άnxO^\רk{+C!l˕W?E|Zm2*"w9ytǏ!D.E#DUQQճs lfU%_WKMqzeom*yxVKP |Ϳ .u(rgLF;=Aa y6'wY*`2YP,Ɩ^%$e*KD?ښQ8 Ǟ\Ҡr@^\q||.{rjd;!%s(y|t>1vͤ,a~9P=2$ B5' Ɂ2Y3_ԅs"@yڵ2ʨIYp>F\-h.2\,yRK"V܃V)` r*+V,߄S/ndzZGP:td'i, -tKN/d~+s|mQn^φ.5~+)A r Wn.@7@)_gtM"47V eu)bV|H47@H vn$qHc@e@@F PVOy'RkPns#YNif5ʁQ{&P8"߳?`^Y eߍ:VmԻ@nןu[KZ͏u~V-Eu^x3]-PH+`eYW  F!<=9lYT*DEM@eoֶ0\JUrG - ПB[X2M9<%0$Ɋ($\ -&t$3au!o&2 ~?MQ~λ_ =qOp|. D9ޛKKzxI޿74FһPz,>J}"mǬ?ⱗ/e.G ׂZB,էi }ɴyo>Iq^eKH%WW~ۦ7nUoF{r>*hqE&ݥ.͟sJyIp|;ˉ9]wES^{՝8;sby8J1V̓x{9!' .I W}-^='Ym&ϦהeK)N۩|onYqrFyt|bő?w;KuW پ6' Ki+꫻jH/}n/}Zj/aBgged(M;39V1-H!>o߂!gw^>07Q !g&NE) Tk?'b3:nr3z2>nqgq"7R3wam~9m{c_TFU3*Ztr(%`罝^`6uȭ{{3]o%d!Cnɿ$ⅹk3/pzn!&Ѽ8 -oUd_QMwmX@;(o`-xj1Bwaǀ} _TM}aWuYNxN_ru2n>N{ҥ|Vxg`׃x z԰ك곏v|UGlvvrLĻ -Ձ=W-5M?YV [ g'o6s,i~Q^VAfy"3 oаg[pdҰimE{Qvah2AE)8ʂm/Joscwx/ڨE ^ML4u_~gdUvE\VjCڻ*ߍ`90KuhU46\:٭IeԆ޽蘎uLg]3>-Vmzꛇ&*?k4|PؘKD0M]7D_aW]o,]hTg(d.?)rNk,FWQ~^=;w5jlUPsS-+eˊ2Jy%kt|jit!:| %ro]_$ˮ%>;,`w8t:r_5k'(ut8:TkAy][݂Rj -TT!Yֆp_tuފzs @@g37^ 6u'З"ҿmcmGMɄf+ C%[Q[pHb-EXGTgR 1)mY G8u}sMuk.Dߔg4._B-]mBfp7N=:&C{l*%z׶R^s~m^KwkEF|_3sOhāۆ DWh5D^>?1k~ޡnamHp`wn,utZZ eN2x_k'^ﱈDcތ9HNyM_禧^ìc4[һ( RPgC_&evnǧ/*qlfZ5jܷYfOCè,z..9m -=C֝-r7qZ {L)Sŵ,.–&{y&ӍMҥ:9FPLb7P&}nҹج}"{YLv:SRKٝ?вؒE(=62­:_;)}nL>ްxf;a@.Zmj -gq%]\pyHc3#×o5'dDF5 tǓւ*ʰ CQت ^ڢX : sw -*l%*= -S:u_K@2}H`Đx*e6GUXncy˚b9/wrZaQ-V1Vk?LoXo>s\ܑkТ9 qu1N.uyI.٠ODŽ5ٿ'"ƨ;1~le[OH7\93L;U/ a[Y, |*MꥁXթgׄ•uIB}!7׾A{ֲ@[L9JrbQ-~jyնK{Nن:ruצlYS.Zc*דaxB敵#++LPMmq>lp*^r2OmJbΑK(ܕ`~W;6#HO̹@'xD\$0MabȑNY`˵iW:9P:\:ZBx?س A~g8{*P?sZ_\/X,lu3h>njŅ!}f(H$92B1N (x~T)ɂyO` -Rw&Owøh)Fǒԗ6`J )wP%C8&ʖ9kodKlOv9#W* -GOn`Xb doPlP@2vG. 'pB;ۖOR19Qp ->j[-}g_6STZ;@ngބMXe'dPxczXY`BdN>.;6^g4|9|yI O Y@a|8P  -ۺZRveD0v_LXzp_QzDXeO=y6~]~wzqp " -v#s '\ g q2OY˼,pFs!a݃?*_'^g?_+݆/ w/IM¶n۟M>(b`E;q_`ybzE0Gm' az2_yD:zh;(OG=˳*Q?BmH[[To6²pu2b?FOX<ޕVr>EN rnW=.[Y/fp>?>1zU?ńvk8Gޤ|eyj_eK2U=ѫv:։RUc}wdv> _l}!;uvh?pꦥZDgp#Imy]Bՙb;ApCé9Xd-և 1|*kNW^&][mdbLd!n< _9}BiąOq6 - P7CUeXԙK1@xbvf:"hxrqsfۅaay˧{o[xdxevkc'SZ5"w 1rsj߈s. -S6u?y(59˳y7^QEŝ6 s5+};?VhDyۈ)ס_=۝ Cɴ#-:܏M8\}/s.׹رɔЦ>XyVօL;S:Na&mM}/c >I$3#p!0f(?4׽z}Fݝ二pWruvϝFZK#Ӿvd,֣Zr6mzX46h4hߘw˟i7/-dBj_YX}p[^b Uߦ:İFj57k&_k~{&uݳ5npkӳvqsR_Hݾ2y7Ϻ9WoN74W}8Twum1ϬX[xp,*YFN4,mH/ws~ ۴.nz;n6֦ߨR3w-[7/Sz֎ -Tk m̹ǔөx~iv֬g~!g8{S2=V%ңf,V ,z\^]]x୫`6f`^F8ߜolRsܼBUkK=r|w-_/kȣxu\*˕3T2WlLdnάIѬCi}Kf0UoӀLJt 2RmEY^oVli GNvj:=V y,]UQI>T `v]^tpy~&٦uyJgq/Zٽj7^B `7ALv07jt~T94ץ vE.Y[EK;QZՄ2w}AQr.ޚMbck)p|5%-^ɺB)lF փUaQ&;eSn=jk4CCՏV[z|;hg[:UF&ӡ*YréIuՑQGR';ٗ ż:ꏰL[KI؉(- rviKkJk9 m0`81op8]ϓbԳ-ϾK[z0* I -KyZ[z2[8%^'=%8^ԊPX-9 XaV|$㹠)Hܽ+'1"sb`bAXOņ'|܊&PvY֓-AFXX6wd=VHڋ5Tys">X(3+^*/%-VFCSZܛwE,,p'=&0;_,ߥ.Ͷ?*Tn.{ekkmbeT4``~JglD휖lƌByGh//'PT&F'dsOHpzZ2=v6/f[Nּ4zH89&@,GwO[uUDSgs5n#KWgrFZꆼقheRjH ăZ^FoԘHǹ,UX -wY믙("XqDB"fN]BuG|$TwbTV0y^xDrrTcƱLirX֍5:\B> S 5΋f g3 c,8߯lrFV4WSDMrElOsif mG̰\!`Ɏ|IT2.w>,272+.n O~ZLzߋ MѸV u/m hCvԑi -T 9`vZ\9zݲX3R'(JVawcv4L#,Bp9fuX&#jqq_Hkj\jҋiS]PRD3SRֈboye8:uTdq:G9l7X+XueZ 0J_-YqAD - -Tj<ȋ~QIM7ўeў{KAQf]OգJ>~5K}{k2qIqe6SjΫ4|'2f!W?? CP 9dx^'n#8wèJqPe9& -E/rT!4 s!θlov*<6S:D-7cQ`;);0{:PRg l.[nr*1P5:"fgZԀNŀKe@P/>2yB9,=*8W" -g\4<vLL)_ǠR ViP8ad7 5ln`+E@X f -hx@qa;#qH> `bOɼL./5se%ŏHBt F e@DMoy"B ,($(rKK5ή ce`{ʼn$x85@ZbXBAqP$8\8Q3t璦fAh4 j`/z_"lZgp]@ҔEѓ] 0S0G0^֓C̼.普z9``D6Rl;0*yFnM ow Ze+6 93V&A?s"~~gQB#q9IPY'U>or9r %+Sa |ٲ{>NQӃV{mOv9}ιmc?/V<]d78HFǞe"Y$84٩cЊ͎o;jK'6ჼd{e휡V_Ȓ]FB؆< -3ӉFZLa7FA11#rCC k֭#|ıGA?e"S՝uXolBX/NwsḀqv҆䘘C\ZCpro  \A -̬KZޤA{s+_vW!y.#V;?ZƾuKv6sK2U̅[sLAtlósX nq {(n~$>4xbIfQvWSqIԧľ}fҺ hIK^<)>HnaŘެխIPV_ó]Q2.Pmd` :lھC[ߓ:_,oo2mCVùXJ^<^;7T5̷4kZ,rqd_|!L9>6nf V]Ji`Cψ>jziZfNS;Բn[˰u?>f-y$sR̵9Z0& pczm_H##">).fzmSnVk\Fεd~H4*Y.;^h:Z>k47v7 cRbnְ~jP}W:j-%2rvD^A ݿ8I{N"RT/iۃ ژ/9>|݅R+s:/ o߶-R?㽥 U]=]P17H\t{f2ۘi;nP6H10tנpO׭Pkt=>?O֛lXJuoO*yvc{l Eݭ]7ALrJ:veKqeDws~_'t((IƘ_\,ooKֻ-:Zo;IuRl]fm'Q^-`JP¾wQ VbdJVUƛY{Of>:f93 -ܶtj*vzWdt~~o:Q5~TRBC-KVvlլB9G`k51+w]svc܈;^}!%,[`to]2*BvRQUІʕSɘݤ_$;δ}>d0OA"=!=h[xZuwJc!U#{yyFNf}FD%rͭspZ<8Y{wȕ*tTd*"ǯԷ.$ǪR;*HQI8Q^eSM'}k h( -{ҁ7J[wN!2*\<&5ëW@ 3TP,P7IwtuْkOn2c9[W['qtb'nQq%4i_T+{t;n4k|/0y_ WuxuʖUU[cUXx@ӚSgHSªy,c}TN֤Ν~W'Mٽ -%2T|ZlLX( D5u[||oXI+-]ܐfST)6tLȑYhO "d -kL$շzM;E ڔ1ڸ֒QQ;ٚN6Iۓ؄OT<mBmg X}K6uC䥑Ŋ6VovyH;&@TM70{q"ތ[nf1a^JyMyڦ续ꍋS'YM>9U#1H L<9ELXK+D7OM*KyPyJS+C}L}=zmEfҞ#㆖b 6d4ͼd"y[{L/9y}YZ N̤3[B+V)S^seک*u:: ^S^ި% Q>|Gy@yQy3@ד#N 5[Lqv?wȮiC$IR>%kndt-WzgGsaWX -I'nqTqQKcQ!;g^yI7Pr*Q 8$CIFtO٤M&%+X}feRgW Zy\_Hp} ̅ eǘj# 3G:w^juӹ!sCaJ0Z.eZQZt<i݇7x&٥gaX6z2XRK2AGvN'vp/ 黒˭R -_:Y3,]lҁN<cb 3->;i9,bdӠxɦ0JMLu y S3{c o>s -wy,s7>2=tL-\ɏsmcRNMnJJ #% nz#nv2`oYpY=GX3:`x=@7 ); -oSBy0#&>j׬2\?⟂l[NnN7ǡv(| iH 2-q?)!D@mw@5y+#}g_->qU)NHl>&p$J.ԧ(gzi?ҭiZ91 @e@!7pg_C(B&"1&PW |]?nٶ Ϗk?HlG4eC[I>-QoLc @q~\ 3#VCğ~l?`uIF0dŻۜ[X6+\]{߬NB^'rl;_ECb_)#~/3*h:{)ǽ,Wcyl="t -g[/KR>hyk㲭\x]]R+JdB4:#mj4vzhՌ0e{uͥmh<4Z=ߗ|!ë[l6DPs&ƕYvjqkiŻqE0/w6;vut T|}sҺzՖZ{^{oQ Mhu0I`wtΛ< g#0C_`ax_{aJM%>^guÖ/T䅧J%ԑ1Dj{.V_5:q~k.okڠ1[qXwŵހSǹzNBأhwmۯv6{dZ"NRM]{]jb]d̮0{i=<kvw6;[5owH fY={jWMjUjy$N]5^=r3v3f.JףjI0 t.Ox$ũ2S3e l5O&d"gwS Fzj*z^T'Re34ʕ7u{mb46Cl$hx}DaYXR;BIߜ`P"]SNِ!m^RUd\hT _gRXL;SsmL nƖc`ҡL~^) s}Y6ZP]V@1Y/4ǿR_#MWX%liX21Hr]/WY1Wl:f0ǡmMtL]%oJE_v >͚ -Hq~ZJYyYmzF=ܯڭ3MQ>B꨼\W;Ey[Z}s4 V:VWd@~1vk5_&}uLTwh>$+aIgv(Y$M70f`} s^OJqΤ2:5_Hi"51fͻM:^^5z*I:DCArWHάfkiYCnP.L_,zN^$8i5Sk^ -ǃ1G6Sw7rÞE{Z".`[Ģ·cU'Zk:Zb؇3<*\i&eiy:$B{-LDͅCfxh3Wyw2[pˆrXLLN>B>e@V Ės6Gp -U TSre2.~^KPHn]8%xB} rsfAa3;3Qybt^KLT5(/="ւtQy,YB[|K9.)`>1*Rx M+#ɟsVqΉuga*t~2xKiV)M[i{i^ Ysȧ -# /9W,Ry8MNuw&[} ;c[V_<ݫyeU/"LEv_J,63r-uim,Ѝ^x.F-vOBU -Q+ -OP\Zkç ٿskV@R'}&W)>rP8VK}}VsرweUdR3鹡\̔kr=8Bm5?S|'OEF.ٺHi j6P?_aۥn}*ِ)gm*|zX}{ *6&Lis,_aچfY> -M -Z*O-jj lf.p(LZ~;Br}oOVY=VVI_Lhx˳T3.gaJS݋[MϸGIcmC/Mi]n -8!!]xQͅRΑg|Ɛl$o"^wf.V;a.x拈Nn >Aq|2#Z{Yx 4GcRyr%~L}B[찙0(ф% _Ć$y>(ۼ_s~΅)E2h4 ' |2wt6{6KZ::c&},Ǿq62 Vs OF1LfӨ$Eܟ/T/sXe=0;;jGID/ OO+ﬕiKak3^iHQʤ*A;BK[eEJ)}e\gɩbz "LCގ}wbNE{2ȁ3,jG٫a2_H0w1kˡ.RQFԐG#} !k`nj!fJ?Pҗ/p5 k1& -C&_T5f`U==1䫀׀sJ=evUBPB^=1H+[Jq{49MrZVN>V= NA kϻ0P!MʳҏH^l wLA&An9\l@N7uelZOIZѨz"̫J&/GgS۽hԑjun/g8G#4wK!{e'J0^? ȿK ,'r e@ *,\K$"@>& E?2?KE<>8&#v8oҺ=s >4f99{Hj L.|QPT><ŀ28PT2ILv@rGoz=4BNuʑL@A胪mu/jvD5$Y3 Gf[Gi7{Tx&zB{A - - -/t`- @˪} -w+֮9nVMAo7->ĠGȔ|Ty]25'Hi| ۊQ咿<;v?VzA1P IP( -` C ư7TIF'G#`}޲BϠM% !1\3jRSTa*3<`G ؛GOoݲ+ .loEIs}%x&C\CF>sB7sbǣRr)OD{UcD8|O_cwm c t-S/DG3m Dr.`t # 6=k-@; AuQq -85Yiv.K~4W4n7J74c) 4kYy'2PPv4'x(@*V[Sku= }|tOXCCXcD-{ySf:nB2ۤ }5.4W^z-Y[*X0VcXȡӺj,kJRs]c'6CMeycreMHgEPw.߭E@ĝy|Yļg3(ֿcux^nnG/$Kdxnm2cX CH=Q}h3ꦻI~5_oty6vŭz9=/x:Nؤ6+|!gF/DW~-j!03GJЇFWDS`^Ղ*.9>^՟#~>y^fW"\ޑ)ڝTpk;[ Ðw\}̔TN&\NζQs|wdfc - SڏW543?}ۢj'sNfʗn CCv)&d^%]>}f -%׫*SR܌ oBsi$]oGݝK;uY$#:q36p(hŭ -|h]؁Hh_w -ζvG3׺Ϣ'ӥfgMhh@Ŏ[%}*C:qv5 ΡK9)ؾۄ[AyU垩+FNQjM/d\K Tw}C~KY]\lZmk֡ [ׁk搩o-wmuu[a5ޕ1U)t{Wt'ʓH.M5Ƭ^ބiڲ_rC<ix[WśC#cK*]n\&%c}/es3j>X:h -Zr~֝t0l3Y\lSS>v^F^}DJs|3O|!ڕ;ڻܛ } I3z9h8ڝټΪ/ -TS zNmU&Zxa.Arh"V5Y{hTm1:.nzՄV1bVBlV*O@:x'ndj.7uY+[nA6"[{mR{:$!QZsLx.=[pzan!9vrܿ㋹]@{JL]4]畫NUoұG*6-?BD3Ϭr&qhun|x_Gʩ7je7T-$^7=Yni ~x/9b<׽q젅;PlX~d-,0"sc,LGƆ&)0' #t'&͗@NVni)v|1؇R\Gj\C\t7,,in}k4J=ǞT'ol$3~[9JCe$W| 4d%yZ&k}Q5jka'9Jw*f3jH`|f[%:2Ē|nAɬ"%(cʗ}*׎~u\?e~m+/MJ+-2l+ؑ&359G-~&$ez>/qATkY(Ç~~zvy\fxk -3vuNՕ/\*-C"3q*Ű"aMa1EPdV?DFz 0| wKg 6*WwVkհ?펦'#.U)XNjyDٽij|l)-4< zwX+YkyM,#6?uןCs-zbz/%Qbܾ]f'2t;\i@% RbPD Vʠ0yqAԇ8)x(lY5^x\cyrDZwkq7U&kˏY_z[hm-6ePHǂտD^m #qZK1lԾ uYhVN5[1cfZ"=i1YItxŵ|k1Vuك=}f4iFӪbQk% l"lj|=~?Oy F}p]/]lk'YLP+qۢ3M}'gwhO!ʗe)#Xjl+M=ݴ -nOv~=OKm l)`U$sZco9#fx4`U<S+ozh1KgVG2h@!b X!Ʒa.۝5m8A- QxnFtRv~BUEdj ҭ:b -`ox8c)i;[Q#3)~%dj`9~`'*ïTf܌Bo7AAP,mՙ(S3T$6B, 4iL*r3  - L h4L@L%ng%$uk$ y@`.iЉ!GպR^EF|}buw4jPT%Jq5Y-"_B`^E`;3N`&[_ ǡ 0&CKPh0' 0=_) -ٸ|\3- Z`>[ "*tB5fFl_+cYpXf׋o?2;z_[;[<ܹqO. _4P9kR'[Q#O~%=78sMT eiq8"HŸ x=O߲go@qO BaAIzaZn&,0^@H2:D zBd!w@,-/A8b[Zq'Rfआ#v&/ QHpTm [&X/h7a%O2] o4 5 E7]W-S^MQ<0Y_;OA2 ; /2 -ȩ TM3Rh*@AP J'je @y93* @%,PIg [rD(io|iVFR:H$2d7,}u\}x23?"R_k1_uN@dPO&ZWMmA-`rTDSY|ɒTﭼ蓜!FXniGjw*ipï?|ҭm`! ,K 4`iVQլxU֟_?ziFl6M!֟QUB# VpݙFzrn46E*ZWGW`TM]G kd6/kZ=?d=j9@MG;7v~_oj]CGu_J'z"эnѨt~s'}!MY2 ^"-M%ӽguSK}iͿ--CjYc?~1:V)W򼭹"#÷pPE'Z쬎g%k0ۀ{T]Lē&+2w 'zp {Q;So}ԿYk/=1)k?{>ZXCϳY~N<'ǘ[?#q3W/ݾtU,rD!VָUVEh-2cXu6csh܇?q OJ kwDhcn661 }T}ۭɬ긓G:llqhU9>VXAbe[6r4J68Pju xsg&L"E] =,8M,Ypr'}2fM Nu8Ua8*G2-*:6C$[16R*HTka6b9Bhs榥o1z^@\۫loYea|>zig$@E2_0WTS9ݖٰ7N[6*bܶ[ Zט99=Q妱}ap .Bˮ?& g=y'(xO^m1yTG+r{PzA{ێ`>ыao%w[W]Nj_o! *!<4r* -`-˥_^~I޽ӝx{rP ֲĹZ8rۃ7_A}dM2ǽלc 1[0V^h| KbZio5E.iv껑~ -,e,D~y voYvjGs8zXu?τ5㼅\Y՜rI Q &^.Z 4l4xFܰMp6}1pt7S֯79Tvv%wɴDfQcCabeg $bk2bгrۤNֹҎG#Vc4SwS:ކP $rqlF~ڨZ֐C⫕tih)<zjd}Tr5gͭ _5ŊI|XÁеMzs>kWtFƻ,t@1r]@ YV9TcAęęN:"ecmZlvXV[ACJ;U> ߭#:M^E:>ִS@t5))~ȮT)ItbwD4FvdK+"ΖPĩQXdDo1EBla 4 ykNm۟ Us-RANۼ^PzYyi]_ߙ<8g9c?3Rڑb`b koC/=w NT\9a|qlq:[ks+ÝwUƩlC(3VA g+O+!LϮI0JYb:b!k/O&zG(Z߮<_&w8(yNmʬYEk}vݑz]δ s;w 涪g`)[\iR-4&M+(yJ/OZ.ui2{|u -0"JPI/йhΚ/gW{00'v*o0nL%m]o"515.W[35t &-I&5} 2u1WOV߼{E4y, qW5a,v -> .2~: gĉX^Qh|xw5POhc"Qp.ɗGq僞./&oL"Djvx\'WMf3ԥ?>*P:A -56U2h;*ԪǷU/!vKfvO9Ѧ!+=D'#,pIW|m -z>XmgCMbž)G y +،9AOMJ]909PhPeP!wgA:ѧwW0lY6>ލT2h }B=LM[BSRw5Wq}߆Wq0RCK*RZk9CMzg#^p2zZp` ~+ ^f o <OnEoH&xԅik:)ȓ8]*Xi71AgۖTk]`ucPVsIb}pw54A -b/* w; qP(k'(Ow(]Ɉ25@ڀgk@%PPHrcKkt>i*LA$%A[ŢcfЄ꫊: BVq *nڱA>d,p.hTjvMBe|mJ6C2`^0[WOtZ[Lwr(`{ 0 >M=f0lDFG;%y N}❀vasW -k)m|MbMQ1YaU-o(R+ϱkg=Bh6£?*:VekG|`vi$i-%#q AҋL~ {F8ثZIN+Ф3 W{d$( o'EPN4!5ߦ9(=}AT6P&1*s 75;@0P8S4%d(3b * Bo*Wzr:$ܢM? m\H6@^݀F;.,@vdO浡~+t` F{d΃3GOiJӀS2wHGpJnTM30-=CK:7"2$n,H*ljj Ժ.&#jY(Aa ⟀'֚9"@"?AFwu{gNgee Nb8AlTMםsgӟW^f:T1=<<r9E.s -=Xs'=過}bF=Jhxq N֌ե -ȁ4f`B*M?[`V'{aD݊gƿrO@v1x={[na#*&V=|[X60]=%$ .1:kԵ p_;kOlvuV16ƗAϿ?j8]X2%F1`r?VH8'%RaC0ơ?=/#=h*UڦC-W-u*&^C#c]}En~5iZ'X4;2s|3ՠ#LWsz_uߝN 2㤐ʎ\JmzZ cݫ?jи;xLJZ.΃G{ur2k$NL^ -7HsrsN\X)bT5c󺴸Mti^jƽqwC8R9n}Zb̓Z5JVeQ(y< -rXAwFfӢx3 -ObFBt ?Rm&q0V0NzT,X7:? -./Sۖvޥĝ'a}Jz -Cm;+X~;7fj"li2"{d7^5WÎӅk#ǘ@ll|8 $zޭ 99;4c -vz)7,!ݵzhҺ d öY-^հ < Q[MK^USB7+ΛHSM0hK4N ??H~ -Wy [V#6+MY陭g$/-)쉕Vh|nz͂Q2vj3z-eUN1Z(I.' B1xW"66ea -{";f͗x3:3=M".wMF%ݭ޴)t#[NcQe,w8[ 7~c ,AU/XWqCUƅ%sNLEX.>@Q"_j5իm9!Y |YLޙ^B?^vhsדK 42拜Y0a~ґr\S.A+k6RT҃ f&WJYNƊxM THkS~[j96OT.SFGf[Ej*T湱Ȭ-UH:_-! _%b^Î)|a=LM߮{~Uj;whKYܔUye%O` lrHz1=r Fb&?TiiQ/5ܑ u"X/ _N1V\.ךt}yvn^}tZ[9隴j+Tz}Ց!xaQqȲ#yWB+R#PrZ,Fd Ģ3yaGV -z-6U'/)^@{ ZQ?`/b NBfM*'/o2X$@t -Oh6z9  -I0Nk|Gހt@6j= @Fz -OcKynl } ְkC(& c Їu ,`&⯞ m*nw>v}$Wlj!jlO@\`jX -LnN_UfSOv%dS8i,v}LwP׀GGy<7!H x;W36M'c`;Ft|e0'>#֩{R l>H?w zr"mI~o\nh|q Vg7R r Fl1j=-_SiT`LJPfݖĞ!<)G'揔SW,MsT"7j2U(VJWN@ܓg/ț1 -"rO%8SZoťz}w}v’b\ŦCq ogP8US6ՎSm7+ݦ맥QmG%Yzhw~ m@k5h-BZobV -6Yf8 o/ S6ү <ސ࿌xnYtAu LyS0ͪ QgMwI)w55ڦu  Kp?+YWy]1,&eV]=3t/U>d}N"brǺwQJ˚O(?l{y[ݵpG9/r?uҗ 8)988@u-4LfX~-xvdvB*%jm*%K" 9GZl IF\[`.G/v9B?@Cnޚds%+e 5^{m&sf7t3czx cn F^st@PSK>Ƈԓ,C{'Ox3u=l$kg9wӅY&k7wNŒ-c: +&o*o bmeXxI,uUt&u ˳d\~yѧt3ڄ^pNm빯ǫ1lAulUafX}cErc.4nZN㚇? 7XAf7V5:.(/OWV1MCJH; ׁv oq[n*z_uG(_J*jGʢXTtU^W2}X^Lpsc^rU;r "=mZYXߝF_N9mb4B2F׃C]\3fKEͿ/ݢyH10ik^ʙyǹܾT\襻YP]$XM.UkTX>t f e2 c1FϏУh3Ux?R.*S2~ܙA]|}#?j6akT}(ڗaV꩟6i-ʜm9+53x$ci4\w]tyޑךVzZ3Uf[Ս/frw5ih|F>HPY&עeUCcs(p:5yJ}4oR2Vo{'VRg=ӪqDSgUf.TZ\hF]~ˮîcӄWPQOR~pa2t]j VjpZI_F$Nd뜘[jj"a_[*LԐ{5Nl]Fyvccu5 P6V ~SYiJ'יYCH zn͖+XU] Fk7z%,_@<4֊Ub=>[x52tgx [8k&}z~_bQr IohNٙ0b-u;@S=YyAOJ!&buarYfL'ϰ (3b0-2YӏfJrzOWjCvXiҧ81ft;JxA|L_ئwq]=c \ysmsֹd&m& x͞ܓ! 0яAAӦQOf0`QN.$FqH35goW|(NV*Viz#z<)A+:mć݋X /~Va zPn3 5!qd}L򚸃^ nbh +v FQ2o뙍fXUł|yU ~#{޿0EGb^xdW*U5C]g`cڐw0v $+t3p3~0%}%6-Ň&Zc`E`U}"fq_D41(Ԛg mc7m40d _or'TK/Dõ XAf7 rNY'g7Kp+kG+@8uL)ke~89C)T;.Z+| _Ao"<NNOoKʌF4 [6dDF_ 63aQ׵S@ɳh}'P~鍒N)P>z|k*ɫoc( 򦢘`!T -h8`~2hdT2ƁbِZjK_w$(̰   ;G!,+;@Ž#M%mȾ"q0~*|3]m(QGsUPpqvycpD!hXz?sQUF`Ds%,f1g1W2Zs}6GbFֿ=SB* TLf%dMʀ X" <`0 [:yYLB7eb:1MGQ(i;Sz:]TrUÜ"dy@CH#lH:`uyptα_뎇+!+( e\ƨ.KoP1Tq$f}R3-{ʆ9D-tNpK*M7VEUWq;!aQS]7l,W_[z#} 3.Kz{ u~֩|4Ht֗I_7a9K[uB)vlRF ǟ KR븞>rKLuMe ,ּe8é@˼W$o@3y ->0*.AfEk;D귤@/EOzK=}  $\ -Β  ԙǀ1XX%onL+Z]Oemf65Kmc7m[g|=f~X.^iX<ހ%7pդ>?q 2jm ;(+7 ^h|7să_{| zr5XC OQ8NԼG!?ySq_2bsBۏ.= 2h} O)r~T#ty﷖@o=R76kFyet^b'bV7LT]&;w[?}ijE,shtP6؉\ Nz >KDy/i] g\Mb>*_krv(T&o?_qy17cw'/vo)ơqNYuXx.?hܗq>G4 o|k60J^M ~;LMiG g>^ݶu3ZKG 9}z_jsnUW>^_:9(vy3ŷo}f$[KO/^,;:tNo]ܟ?= qߘi$U*^,ӳ{ZU@wmUkiCŁ?xB'HvGo-xC^4Ro$]U輑lɆ_|"J+ժuժ~Ey&厽۔).0N^S_A=0oՒta'Ym[t͏WESVh?j]U+vW,OeВT(ϹTua¼ -y^f7qv|FٺS"Ӽ~J<GJbsbZ[7W8-R֟ggaU*Q/ˉưF>]h۶֧w_߲A֓O}gŦ3ANwrY3T}h,v}8ԑeӷ?8*b1ӳ%[^ۅ\1C-xϿqzn<٠$ckgNS&YQ1|Z:QZiSzpaR&:&rrm8\lOH J[y]Q,"eL|J<. %+YLVrW%vJrP2Jك`i~n8j$:;Unۓ;#ܲ6z5ȪEȦMC~g$O&#Q2qZf$+KN; ҫ|w:^fV*˷<CʎbTEuRggi-;b {".KtcFHnw-Y1D0z;z%$TpiL7qZ(kNQ|Fu6qcBr5O'=Eu SG,j0O}|KH%5Nw7$Hִc'n˪:ŸmY #e$Jf:ɋȤR t%"qWxŪb ]0l [U'eeWϴZIn`*$4.KHwoPo[ ܢEoJ\}]#^M'a(ӥ(b.t,*LǡTk'buz G e5_Go{.%n6փq?x9I~tLDԓe3Г)ىS75I^6TL̰ - -)/>/S,m )0% k^EaS M7/.ԓ5K<;Z-=aq0YԠ΃0{$R7lVKCBx7S! */I.JYCڴѷĆk&F"qQfn򗽗۽mדDܚk;tIf= NzE-7Z~2MGmJhu݋bsND!(x\}\^\relyE־ufL:ܭ]ibw%(WL'ȭw z"CHV#ȫ*>-jl•8;U7OVtL^]goi{b\; -Jdpjܸ~51af`{1Aa&S`2g1_#F*^r;$YYqFԧ -!Rs['L勄dm=><MMI۸EsFɟ8Lf*÷xx?&xG;e<3fJc!2/h(0J MJ:)rW.YqhDCtue\U\}Jg'?8M vk[YJcIMuB5uݎ:My//\FCV_jYq endstream endobj 47 0 obj <>stream -1Yjőݍ*NM.8AV2G -zJ!a"s 6W{~WX1Ч,Ve3DFFŵZyV̒m{Ѭpټ[FIQ&-&WTloAC{ کG&[8iJn(^ړWBKݐh.mdkzC X0ﮑz"i`M||]+ 50K^#RE)$jiFBVd#@;滽kYNs!hkG -ԎP o -7\UXH>Xo5#{P98* Z 7M˜((AV ԩ񠸝%c*ղnB)Y(9s᯺= d^QeIҙY>F{]tjqþafΝs˗'K3nZ'̜e.|< DTv&hP)(2Fj p5")ʝң/!/[N`dYCr~^v/N>vY!7^;Hַ΀@MS/Of4S Хn- Q2]QP6@gs -s-c Z%.u%vCeRP48[~7;{F<'ycOKtZ}ڿ8bC6fPqW+0Mx:K/ƽ)-#`|OC^P^u})6/*sDսk܈V +8_l1Mm) -p!z,WR2H7OC$!-3DqI5y_+Iذ8 km|'v+vM%B _cOn=L/o:%"ߠwQ"~G'涱!9.$!F#`M$L;h-5Z'fԲ@Ks$PG~ {2$#f3-ljZxr<:1W8*o״d6|v6vێ g\'|ݶ,e~1.h00XtpIu'):9a 8NoKm[~x?y qmw'qzމK` Soiwk~ll8#݀rh:Bمq<bI=}OO$P*S}UDCE3ơ|[O}V?ArW]Z]x.Z wdvk2>380o۠N{n8nOmsuKuXmS9S^Twf>1cZ_c7ݴ}Ca|L E'O:'8eAzoON> oXYs9=fP֩l}r*Wk;ޏz3J o x`S1 1cƵoڀ5;3K9iӛUz29OlFac??e~U<<ӫ,֫0TS\v'!uZ᰿ni`Yc^7 b9]^|ֶH_h6: -&.޵+䟕?)Bh%,fkNu̯jz -|wKA7W2le簗9 Mӝl66..ǔI]NEQQ0sY{6zy'۱kgfk\%`Lk y6q׾9 $.}T8[)XKNک_"SJy{RJvF4j6zrCUuqs}sfl꘬#OԳu)!៬qZWCXPyo`5*%Fy.wrԚ29@ִbg˚7Z/PsHQIVg"ɽ;lBBq[MC%K=Lt]0CE輐T6áy'P^c1I,j|~L3bɔ3,,QO -D-/3i^v2Tx_+J -ZCU_jtmo*YQ%sm6Ig9P2 d)χL.K!OP{ޗiKnx_]N;_j=saeYʙl'`yB=I_ѳf׋UׇH[\HL!Jr2;j4)H 苕|q%]mF{?]kz!ysZb% wt"crqƺl5MD1f-W55A4\l,[d.֢oM;!USCPSƷmW3>F=5Cɚpd^MLe^ù˼A&Ik;q>C̓Zc/Tf,/@FV*L}u8H,aŪ+]EAuB|9Q >`r-Z;,%f<.k f ):R,wDZ{0HT`pjnQOԶh %Ԇ{䭢t.דT.b-Ǭt!4> wv{b6]{D\f.Y-[#Io.=0-_TZME22}3 j5-+6BʉW4r'#R7VrQkNPߞ nTg}qI*fŽG7fELܣ zt.4X(Z5rЅw\h 9kynp% -xwA9}*kuLǖ'_=B/Jnbely}Xr}1g#Kv=qZsӹ|uVZCѽ6Nn,GoJZ!>IW]ۮaTFx#^#SMr? gb%[3I#c}:wYQ ly +9J W"GXBRuF,QPHcpnR-ğMMfV4k3aW'4.3F-%Q'-ZKf2f "xq}rj>W{sXni8Aqs#+ح:11чGQj!hvɂ Y&>7ؠL[P\͒x1TOIPe{u?5:sH>+y@|!*PPS;פѩf..A9oZ4%AYd5Gj_+췀Vg8K5 ƉalAqZx#Q; %-wP:]bx@o#Yԯ-ެbwMӀƀc*&R=%|r%cJU^Dx&MZ׿Eu:a!o;4 J?Z2ت=6gEM^"`0:|mYa)\q\ULmK$\ԮFbD}g|Ŝ;bqQA,`A Kp pOkO^qXu=fiKzp֋~; )5k -]Z9gY f_W-?O -};ܘx|ss> -pvjH@d!B3u")NoSZK",)?8{1O?p(?߯6|۸uڭ/#<Z@@Nt ^H -H -*YwIMPΐgi1)qI1Rqw* nwtwm)@h hj@][@-_ j4/ؖ?nqfCnBJv3oJ¯Iv$H8 $=\5߫`90 - :6 w~u硅Lg~8ţzRfƎMo/}ă5dRw>b3]&'z??8u5Έ[-;JvmG׏hi+_ez'Ǟ3nM7:9NLÿܒmbZ1ƙƴ6. LtVi9SaLefk6^y8Iƃ3W֢O/[^nAqꊇֽoˬËOB&yZ m)\n21k8ԷOL{brN9U'o(7$/݋j~ƷT3y\lG*=uzZEwպ *^Ql YNM7P_Lfh zGfp[WlQq5h3(~;Š6_eR?~;Q -W*_jG/UWq yWP%(Ec>hf% `WwNTqx㠁Y&~7ș$w/KR!!5(Z@aޕ/LrRsij?{cOj9#=K+F"\ro:TrGz*IG` "M.!1?$VG֍a]'m1 -HYZUGMv ;Ɋe;&qЪg惾wSCVnp]L ~I34afcjt?(4ghWc|Q̊OD -uu#[\<&$P$ -@ C%P;ŏ h3> WQfUP>M_t><nP9d DvjR$le)|.\x1Zo4w+‹LR۩~2C _ʤuJF{ -b<7 U&^ySrS̗"E"O./BIBN  Iz J&zҎ:CgZnO. kxTSddI+}MNW ico*2.߬>vvX;Y@л^ʯItsCXGc"R-&'8N#qq.GAh3QԈ >N;t4~#Tzfe :'je |&D~' WX{2+!dD"{!vuaVx;txs5/:i8'b1kJ_ǚKRS##&霧F)ڈdymM0 u7o>5 v7C [MB˰&>fJ'*Zy(DVI vn7bw0©^2sݧV+ Vhmۄwp/`:kjq<&06ƕ)`HhTu_ -'XŗfTxT 8Fn/(,jAk|ڸW>BFHg\g2I0tXKEyog=KV=m[+oY$l!#@MsO+p7񍀍ULy8V^OnMk˓ָ /|R!;EJ} p"8C( I8e;P. ӍpO{{8Z_/vlebwft=T)`Fwd#QOp]BjDL6vk]tP{h<;Ͽ چLvc3'U̼Ѩ8\ ޞqgV(`Q +ot -݂#% -.-(Е^-I\$ɚxnc0Bs" -iiZa.Ij|]},xE?%u#c<9{!8|L-A& omkŕ!`=`NISLy{{Nb+:7ͩrER1~V:*s 'EVͷ1Y\1TC;ln,3+RC `q'l+%s1!.FnϘsGl{)k O)/,pHr&'_j8-}en@q L ՘ -R5*~ !9QHUyooT?=FqIqT__-;ۿ1K|VrS\'| 7 -oSH:m4w .7 -K_n;0_ppo Ɩۿbe1.8yZ -~ރ V>޷y̩my-\&\^ѹxɧ #ڠjAXߞ:uV%]uA*`MiP S:^ U).:} [1s^pY?ơqa.8x;q4mF_N+غ,u,9ۦ4}?D]qGyGk'=Je0zUam+x TWxx~CK&ʞ|"ǘ66]m~8m f; tF}IvTOO+^إ٫Pv7(o]1XOZNr?G3 # b~Xim jw~㠁tGVbkcwd8AݽhnYќ1SBXSJeqf;!N"ʩI[Ij\2 uTTy8W &1 3*ƴ6Ck?Nr{TPʛLPY~/~S1CWnr۶B wz6`6*/dTL<NtJ&ƙqnf1+{m(', D^RegxvNt'ӜEmz'/on=AvFּ=KֲmgktSo2gk㱋%z1~;JCH{RMpP -0502mN[ye5]cW5K^bD0Qzr4N ixE1Tcם;wk᥿xƎQr\/ ]{r;^\4<%|ruIS(F67NlDZӯbRYv֯͜˚)\Z|UGMmtS&=R2ɝ/a$ϧrJk \ y؏t<1r;TnP/WɹK(#z~=TGƀ2i14xy79oIi5%7*K v<7+yƊ<*N ׈EY3|ccki_(0@' -}xVGP3(* ^Y(ڏp+Cq#K+HtCOۅ{UPU!`ubr7QEbUtO_7#W~tgD(^zk_QzU3g+W'K[PϺD.P#()ZWFּQ{gS3҇ C.kDae8VeO>AO&Q%=wU(7Hr﫱I9Ѩˑ6''kT=O1M nu=JNlaDuپYS}&SKIwٻГ&31thS^H;| nHH&j0 -=NȮbnhA R :.Qj>1on0#zsT-d=N}+lP>3jA+*Y)5=,"Q .l0xp6xp] ^YycΛduDgvxWd/X#Xkk1ѠFyZ<8M`տiQ%c@1ɢͿK"v JT !P[?l9oTE\v^XҘ`L 8S91{=v|=,dj]<9FPvሥ"ţנF- JQJȕ:Yd$[v$vuZDuBBg xB\pm3X+tn%h"ό!5G`iZhKGE -ƆLS.I8k3hRʪFn -n,SnؗQC!̙1FMRjc;2h{Em<D Q&ETY4w_h~w~2;ǭ ٝW܋V.C;RZ{:{Z7U$Ë潅~M$|ٙHő_5kTLo KSM:ٺ%)gqfC+Rh&jQ\pIA;HDyF F"NtD:\ -tYT^H$nV|{Wg {ٍԘy_Yee(Cw$Tc,*V5h9D1'eVU("gL)M\-Ֆ}?5،E-4|t]l1-=JxbCL5ym>2*"lHS7G _nvuP|| - X*B9v?#gll=dKG# ->/)W}rcH:-k1MLQaVA^#d6 gȸ/H4%+Htp_#eE}B$VR|kV Q}"ѕ b oHPV\]9I'wb\?S-=4RNkRApDV)r`[RDᆄD 9=y* $V:Hln!#[e* >dر܄"1 xސ䗐#J}U=y~dHnG2b▮%2]8/ō^}y5t^RIQY2H]{4t_ħ+LH"!B6$R\ʥ$7:WK$>PHR߇LzC[-t$gboL*/WJí5~7TVRDd-5R ɹ&NoH^d(P|$XVo2-Ad?N:m/F]47(l#8~<]BI+` ) W:O$rt"$DbAQQ؟ߧ k=|h7\wR}t}ܾӓ-ldΪl I"rd +2qy&FO>ϑb*w>wO~CyJxHwf2ۛWOHV$;^'fE#sBQLh̥q+:';TJqGKoYwNMy_}ۙ շ(}\wTh#fHH$OZE$XMHs꼾-Ƿh/|>osoc4oY?*~}'KmHw_c+eGGʇ;e,RYkHѩUxߏt~- zyITۘھixmFCkVmZ a1;FdžY:Ob]2Mݭ-Cgs?+G[ZCmJͺ"ON+XW.>v썭ퟻR8QSY Wm εݞBPQpS׷Zo۵3߾ZBG>}58pu,0t:<0s^W_ǘ[b.ĩw|:_܉I^j6n5 -RT;fVG|EC#= -Ϸk?Ѭ[6:.%y;b~z#2|2הFBœHX%t8JvfFNta֜iyQI+x"}BwI,7axrav&lנ2ÌIkmv+8cXWӤ8ޞ7ډob7)ƺY%3@9Zƿg.\a矹~?\.&1D8=C3Kh1j7 U.Z4p_^"qzQ.$QNjސKDRNϛkS09"Ĉe^F7p9K-CX!R\}Y]&xkTKny7<҄گ2g (e=~fLEqXOf --hDoJˢ6!;2a)_>z;Uf s><,o_@ 5)\gӏ˻q*pi}F}?|.] a?w~Af@\,Uތ|2>N.O#Lpp68p4 -ϊpKM̈́"-oL*0CؗA?¾ 1CFkp5gmc~|O?pޖO go/ a8Ӗ?_bGɶ|׶3fÁx[ ^l!A.K܋"\biuI.G|sFif?/OiJ[+譧 ("=uxsS>Wn{&v^iic,]BϮ~J'\ {O/}m'ςws֋Qh宪OHbTMK0;t2o2&xnZ9pl*mgE\Ufm0Pqe^XYw}NĶj7 WK\{=cpv';z^*s*#p?KxLÊ?xLB/o}@SY!xkYusEZJ -s*y\>9kGKBN浇(L =VaP5x.XkK aA TdJxJ ]]56vlú:Rt+0]*FIM7VEP83vD 1keJo|Ɠ尃su'e]%/k79cԚ`|*3y/\i_[5i]7^F('ouD}Jwf1XM0ꄀ4zS}IBϮbڃiYÇ-P$ ݡz-.s'.ٲ? QpAIߞĘ&%ѳxMܾ]% a{t|l҄¢1g',%κ0+c|EX''%}&DTj P{d.qxm]G͒`z$ M'`z{<zA2_ؖf|&P0bny_ż7O%e\R/G+ӆKp~H5297ݤ'Te-fK.42Zn,wϪm/[Q{P>i.ĶdHO%gi:'LTˉJN-L#6 ~OrR<8#JQzݻQVS _uCi=:%?Ԋìl+}XȀg' X_RYQViu]Du{#d7B(öXo a?Y@7A&n@&uQ)$Y_Q9lgoaxn?^Qm z]Xm{#ucz/A?¾ |w+~G|-8+ a8j31_ pg7v|Ek!WzLr @ [$\zz]^qb k=ս.8Rv!Llt=݊ CלWdKRj8H)fA;\'ڮ9]c&ӥvZWՏ,*_+$/dzR;\qaHX@+fmmr_a:}eH6嶷y[_n=R*uӥ]+bvY&}9^Uss%æJ-Z)cJ U9ΧX+Mp=vfsuVJyh-Ppkiw4 zٷ1+0@bЍ3Lwp܀Y1oؕxqz70٬%\f|ګϽ1>8j+pWJxS{~֪D\Gu a?$@`:-l2 [ۉw/cނkUHϐfo:s.$?%-H"w-e_XwQOH~n䗥jUGΜdXϦh5z$6$*!n7n;|Q=[trw5wX4r -;`Q+"<S7|JUiݣ>PMLD#dC-O`7ygG٭ku;}H,':Zbj=Y14ܚ#ܙ3\:OpX7o T3>JB;g-OZj*l髅^SnfKU[DE^{t2 rT -|";t?ۺPUUytzYv]02Qk{}EL*0CؗA? Ӗ7&\1_{?gÁ1g|E!mscx&f7y~\dxxDDq]P():t=)j[ptm Zۍ1zjZRB?@s%5^$#p?96jw0r8KcuҺCpӭnRQc=^LLĜ .{dva}EcBP8 -Nw@\u@.% dJ~,zVW? k'ɩ+0ud5]Sj|P?Ol F^նu3}r]cK<ԲZ{BvGZ7O+1 6>Kl'qU4Y/I3k_el55ي5A4@֔wV:3݄[.ﳁ+2cp?v*_KQAb )yeGd+eH; #wXYG9Mm\<6fe}zJPG_;&˦[f0,[#ѫ*bjtWdj -d]2!fK 8Zjj󼷪Ýco;TXVvgjݪ8a9e8vbpxc[1.pJabo~v2= -2 ͒{J%Y*[H$9>;E; R=;Zal"*yMp2e {b E혺/y a?ރ|TKȓ9Npvn\i[RzBhQ,k˒Gs]KKL3)V Nd02?[>NJ^+UT7nϳ~vR^gխ Cjlzukە]Z)#hӹQ &\q\G<`Oy&8 mxiAJNBςz4hs$WF|UMr|Z2ߚl|DhGeě~\[yiz~u꾛bgt! qM"u. אȉ&Zx\=FWЋZ .n@;Xm1[uũ+&Պ=_Bƅ=3mG-+ ¡7QʢmbG*бkӚT9Uܨ)7n aVJ+JltAFd(ÐGyJ8#CIexk -4ku&EJ57D֟kˢ+Vن<'YV}qL4\ܙ>#Y$V9U4re -WY΁ew_Jf20c_?+'o|ECp-1ogwٓeHܰ w8 > D̉7/=<łtqESlK7VE.-*0;mL$坷{QxJLրg -b @ccLm vɅ0Kv~>jljR^Glm!^d9Iwq*}YOFO˺C++sM3)tVA? ~7]E[TkCB.|:[]:p@JB[J.X哐hpYY5 ӸǝQv>Z_d/ o)6ŧ$xWPesz瘀VRCMDO cA@%8}k[)ֶê{m$Qde\2.:+KxScJ<FyyeH>٠LI4Cyc9nr!$֫חZiˊ,g˩< 3!MUK<^ ,f] F?^+`τvQ۴ZeeܟJbR\EnTde̺jt=>)|l糅6(1 R.IspƜyΊǭ'RXCe%`G1:>)Ɛ6"׵3 gUƩ2QȰW on+^qh){&8̪4w^ffv.!e8s_ -kĥte"ֿCJF)ۓW%=f:9V9U|ٙђy45S.sk[-1\\zt.ZF C_ -mg;`Hպ*h5U$sjnOB|XkVNTFex17]$۱CG+.vF\Ix)OdzkLE[ <V -ªfWǟbe'r,6OKa49\g֔p ^!<.E-N@G,QIv/8s h@XڽX\3ꜘn?;+ -? -2zx\=^To-aVr<޺>(Oa|%Qyy:s}zEtՅk[d5$aXLt&JY(_2p:C#p/*S@Ps&u3ks$dwuF aK'Wvb-MV3~ ӖN[yi>$Oޥ b 1z`Ÿ+ ;w+Κ5|.OE_|[ۊ9t-Hw<hwkp#6 W*8+ -[<ȿ[A+U{^5<=1Sk,R -#I=Tԧnڱ=f,s睍4Rs6 sQN?/ =KF^$>2 l <So_n燁}`-ݍZFխĚz8ZE徐'UE'vv2B4hI`ÑA&]cm`4[U}"Z7e -gb"^ڳIͦj]LOY'Wa.̆a#M@>\^h=#5_zDZdTnS'uNB1Y5FDj-F=s1/&O=c3iRo^?/1~E]o9Y ٬o^y\ˆ71YedTi"W"wrE&3ͧ(v\'}2N'qrб˧?{[v 檃rYm -mWp ۲bI䄵OOy}%8usFE~q3vJV-Ru1:\ǓhPH}XΙ1@wjPUO9iDmؤ1FD_SZfiD9?9qHT/a> -r# -~:sI3ϭwd}+ -<ѕU=N _t\ǮQT(gJ%54i/vTGl敱7.9d2"to a?@@Zy8xJ, 2 ȦٚF*su5Nym:,aƃϷ٨(1pm(KIhu6!kY=c ?*!{Z¾fK -PF*emcX&&Nu&qM̲&UrPn  c=11~YyGbֶ jU<)ŐX冋}#lr\Vtnj–@5ΒReS//cPvpy9vp,B{ۗe눴Q\D%3o9(# XGǮhUՉȶC+m:j78h >dcõ{<}q;jm6U۝B-u& rUiV֮ʹ)Qj7 a?7XLU$bOLx^[HؗDR8U7)!'Pk9~0[AE h{3FZh#JL/A[wVW;LŠ{VܥΣ&S,weN`:X]+ںwjo'oMCtSĦ =/ }1mbqzƸiA|E.&aͺiwPþZ}SRT/9whaWoA 'D?[oH -C{; I'뷌ek9JOȍ+SehԚ]>mR}*4ʋZEUJcV78oyzluo–{6g.y{(W{\FO۷H̘qrrsxBX⃊?ZVK%6Ƞ_H9G=2N_^t=g;;!3lM)MqN9z;~Q7l"^tDV -~^L8ϘK49*5V$۹KlV^4F5LOjM*h6o˻, +wJG%zTz'$;΁" @Zh ?^U˥ {zc'Zпʵ1wQ{Foe/;s\1ܞ%tȤ&__JO~3}б~} -<;oL?xMGut` h] *V؂W݆]ÿXUEבWsO&]_zϮ}tJ1ի_Ka -H (@p9 Z7 ͫd:g\*$if40{Q a2JިA&QށK_~Gp5M¯+K'iNF9H.\^X:`vڵu aQp<@[nJ{٩/i1#CW>yu~ۺye>&m4;qEAShf1on\pvԼ_ݩVTFW[x5!!o~& u`o ]a!$ȧ@t;8 ثpYt |\Dz9Sf֎05//u4|J,B3+< D.laYM@Ɯ1 hdct-# y)~?('T_uqүC6Xu0J&Ytjz| -hE60E\M޴+b w=: Y8Ǔ?S{t~ߧ[29,%c5>٣H㴮ޭ]27noL? D) M%PkCPFg%ӕY#aA'.-Vou]=f!!~)q5黷Ο7r'4w)N14Xoqy? |6M -J"WC/Gb{N6n`"B׎=i僜6#qj]}Rao.V]O2\Ys&i4C/|_~ -87+ Pt'w4Nv̔V5 Ph`4G' '{Ǒ[/;xKH7U4;k7rLh0U6\3>YO>nVǶeX0c K3ոbr -q>Nj@O\gmw09pksNj&0QH0I#†KbM*X K=. w!u:c\T XUh_ꠗq.v,:]EXm\W=nl(<i)6#UuwpboO}4s.Q\NkIv/Ӣ_3Nl4/Q6)ۄf$c%+$0#/+;za߻aĖף.Z8372׾X9^fsg5ڕppfu`b~@B9␤y2E{5(l!cc5x W0^3rV-9F[*0ԀnoS*sNAm[m&ĩ)*^y)5 Kg/|5W:ƲKtg2S$y ٣qx}=P^).@[-+Bg;,>wt3F﫣Pqq)yLkzV]C4p9 Z 7a@zQ:øE0 LA~cF1 ۽XUkOB.aMD/o?ˮN|+)~Y?Bkh~!4a5a߉.OO]&I@R f~?}Erp'.b"XQlUaͿ̃Rm$VtsNsUmmؾ'g?y(ow=eV)@3MShK8ME/ @>n'ᑨ%&7iν*^z}{лnt`.[ݮ~p6'|!Th;]#QyOͿS aIyk"VOhw=[a\uӸ\où1:GW8gsHf-XI)\.0F, +s;BU7fn[>~6t/vjz~/D$5'X2 -=-߶'2u] `\UWC) ֧A>a`}ݯ@"GjXnO]aYN}B'c`k37nd{ȧvpnY땶:dDA2OaB~+5:a.@vT -sG.H@(4+sC{-j)nsf2:u)[f󩯹fS$[QOw[@+ڟ`52Vɳ %?׉/aǔde{]u';Z[[ud9_yLCe9Y;qlRouWzk -@mXw>p+zd$dky +FmwWhq覼-^UQȅLTc%1GExmdyifV;WX@`Lu1I D%pU% -lMv)f[;x&ԓʣ73~g& פ`}56b~ftK<ɾ訣MPGgtF 0ߗ*8{wbƈ,Oj*ڀ -Ym#݇ ~Ԧ%Fo%uo|r16=:2\q|yu,5Eˬ=Aiix7n7L'۟v\e6r0pRށRI#DcAEMNs]Y(upB~+h$5PRǷ @sEz Y1h#yY)nem2wvG8M wzV8:tY(SV+{~l-m7)b(̙c$~t˲y2v޵vm ji:~ _`{^?7|V6MIΠ<, -SZpܠ aUPZ'a11])WC4\mu.vǗSb/1ItE8_{ QOgTD֛Eu1l[o&^NJ>ԡ@&db̈ n4=액7ްbyg?k 3]GH5u'ȇDZzpraǚRFMl#ۻc$x'@?|)U8a ,|헆ٞkァTm6~竦b^yOԨkʌGسi jQu$Sɰl$wNПwm+17wjß"Fŷ%>{`avI#shkޤbiYΪEA<3nG nΪo+=vyrf'̫/\)cQ2Y$lxv陴%4mb|4ᖅqen79A'lKFS8N3u:7xzzsFO ]s:)ۦ W׭] -ͨ9M齕yR]x#Yf$hp/;e'~$,bnIXr|Ln -ݧbSKzTw+u sa/=+%^ޣb:71mqiJght~Xm;quvdM,y~=EM:!? RPpb{`'*Ӌu1;_Cgi$a/S:꿗L/:qϲƶ~_qu 1ՖLl/9{mԩ1[).tQaBBw BԵ3{|A"ʅx2Mwk1WiVg8[ -K`@9b$QBvFe -붰p)v2vIM$q7Sʘb!0~j:a|jXDlsSzݣM:KtT?Ų;MMʝhBD5lkʲ;"}F6zHAoyj5Yo$OVZ}z} z*"ҭܰ$-*;)J: {]@vBF<GVWD '& x3 T>;زs {r ;XRmlG)j4VAck~$/GhgYc?͞AvBKp! p͟r>~O^jvamC`IaddYMlAch8Hm<}=cvn*;s[7s޾ѩhQ7hND|4Ѻ/Oٽׇ WGa&6аvJ sgՇPv|nچ*o1C/j1과-䞷ܗr{4Ut;2 }!ߗ?`zH(\0> iB&ʀ(w説ʇL|lp gez8 u x~T|| Ѝ='n\POsj[LY` rƎɰvF: 6 mO芪fϪ2a#HzK.\>Br=[}zW0]<6l,oʍ5+$ȩ(T4oA]~2 @rGy<"lSz,);۶yp­doH8utSחrgp<1vtRuEk\ B~v&hl>x4> u!A 䏅ITڛP]1RwZ^E\qc|烓yaQ}~[=+a$@{/ LL+(Tl>e& I<+Nž/dbK2"Sڹո&QzŧX|NFyCF%?(M#6MI ƓO>'qWb{;4/6LvN^H5Qgx˲wrf:keV-l_#^ -~6 hr9t5p8FwFj/?2}+ҸmZGbN:]bKP -a:n,Ҕ7j`zӮ -֘NP3: #]X?$^@IfVv}+"Sa?C7>8\5Z`DrQ1'4W{3 Ig~n%;]|7 -hŋ&ql>P}53r,΋n; yTo9o %zQ143>Vޠ3E"?MMn&/J^n؇Pàh?;EEwʆ^Ds\>ڪ~jW%ljJeJO߻F I~󹧭cvmғ\MΫ#A+W4V%S]vh>(_'1ߢƀi߯223;b;J+KJ讝ōhzu~n_ۄ@r'޲&1)ju)6˓DI&J*'Pt)iOZygQFYTUN>r'.z{.kk#֬oYX7_'x\dFש]B~;ں -J |f ?tW׶W$Fmh]c2Hձ&Wǧ)Z8 ;(S/aܞ3H4zA7k-JH_js+) Er Kq&!F+ZC-Jn{4-5Ii\+x}c&o3?*v7λ?~tRRQK? -NL>qj=Ni y/; nP-6rk2W5Xy^yy'SIdV'tm!c^i vk_/P*(U5!\њr/a;zU5A_zxi=C.5?YYrZҧ=WLϤJeQeu4d8ɳG3 e'8[ P/YX',vv!޴ c}4ļg+poʬdѣfs„{=UnzzMsb /J IO!ҡ0ui6.  (ޣƅwQkJt3fkgUb[::;rXr\?榽L:Ldraw֥iNT@[zw-j]*g*$I_aI`c$_Duu)9ٴůn/~8c dkGG·z|*jwv-TiCFC%1D;9CtFYHbPi|eym}{ֶ7vnE0ja5if[ rQ1_h;[ {svpj9`L '4b:E0cH*3ӖCvZDapE90̼WJ*U ^].:֝^i;}RhW+}ՙk[OLe N.AeYh4/?حX -zx+RC$paŖN$Z_Ð=%sh?g0Lz{׽NOOI*sIaA+0j߄Q<',Dl\ YӝՉڐN(UgGB(tV;Ce@i{ -&MNeR]X7*B; mp2ɕx+),wdiP?#%, sŃ*E+*H݁v ZoiWtC_ed>/&dW=j5vRm6c y@͐ Z fa`*5y%#=\~_?4?@{('Fe+3>8 *CW9옃[SBWs {-heq71-cz"q ьd>|(?;W2)md{ p sm )p Av7mUV/l1 -R0)<(?/BAyn+]nwm`cd) 8#[3Lq]ϙ{Р{Z=]JiA۸%BwtZxk >i0姢㹇rsV }q``dm[06M&uV z9U1۬tT͹fݫ(WgzZk2{xNO -U9NMp\2Aǖ/կAs1EʳԳlp߈a~ACp:>+m,'^Ғ~5簙VW]V%Bra -k!r r8I TzT&^Gtg3cE_Ӈnӣвyg60uq/;UW|!A]| E'trP/ɰqdapt\?8,Cn@zw0N޶^T+/2'E9j۠fx<;5.X)l؛L縉MYZ .\6!5Q#v~x=/^g>O?u\pJ.+=o&slu,m Z<*^#bd-"(= 1>w@PO 6|TG;q+V p2^Q}>PJoځh"뤙[1 b<]R/("]`-6Ab"Ul=x^nGT^+y|~[L8M;h5'/|# LLE6*RK9``,Em?Fkg`рfHO7כSNpMw Ԩж[{#I8ĢYqbkҘ¢?`+թlsk\hq:ȘaVl$:ޏِ\+rBenZbj%m7Tw [FJ3gHY[s4Qx;\_-3X h^L[޻іH|[fIJaR',B3tnd H]5Xs1{|ҭBB4zwBΙH.tid1䞽ukČsӝ=GnQ؍n†3I"jiE^-\+5[Шȓy/?/Q:O'1AД}B^ޤ/Om7Iio!tpL68YK)qRVDnvqZgJ[\|:(0lu~Վ񖌮䬅vⶲ?ZM{Ր8[~+oqZto38, .ۣ+{dȧh;'b ׶*#q)~P3E>ݜ:c='>i91j`ⱙw7`,v^u/P@к9- -DFf<~g_̾ZӢG؜{7L.NvڰK^ ~ &%*?gX`lȳgkqߎJ $ce%nE=\`{g1S^Ad6JgU - R8*l,('!];Ub::jӕجùtn&>-BZҘĪC.29,]ycJ{eFwlOYqSxL>/lh=& -Ot+b7TҡZk+)#<=)Qړ,{ugŬշ.ɛѭ۞:]Xz%Q%쀎v+xs3x{ҩ}J22v0Mzu_ebuNGBꍌpu]@S%l)݌t4{K?zx1>ڕ͗ -k #2G$M~R%A}nZ+9TBWJ\@T/̫%36i eHB]sώM۞޺/vf`Q~$ֵۡ~_jZhVfa>CaBʎ23i4h,%y"E3 uo词 'ٳi~/LM!q:p G]5ȓ7VbNU vQlz< -W"qL$KxN5JVAl~!ߗ?yW`6huhPIlnUztT^6OZi|bdd2 B́AfaAf\LLsX>OP Z +uOKA&*nj}H# ;, 0ow>{Yh!|D @W]@$hSAr_gށdo+`rB;d<Ȏ cE%v粸ÆboX̬=8v,X lԝ%t5ɟy.>݁tL)mn -` Vy@QAV K`E @n> P*lSߜFb?!o#ݬn~nEнA<3*a}>vY3KT8)>+cg~4 8Z<ڈkzO|ab忏,ؕ瓋sӔc ŰZɌ `I<՞x#]Jٮ̓zL M4>g* 9[ 44G %yo@ǃ8`K˳t^yOs'ئ E.R!zlIjv$Gr4)oE)ٶБ :^@k2" -6WзHMp~7 ofW)6әeBLp[}΁[)8.ZQ._MqZ!ڬg@Ml -jϗr :RC@S+.{,vǺ)}5b,??镕Yw_\D6llf7Iu AY~rme@9X[πc{֧] _~>G5>!xë|S/`awPB<4 EeCySMuj\[pW2{]nKۜ{XQFUYZ7|!k4>Ah@x\di|y V@ KcFNeC;o?Rbhz{b]CO#^Rּy\540ht-"g I -)7AQMڮ6na]=Z߉2Yܫv鵝+"[x̺w8kg,DIn+M|8q/uπ}9XE `9/|^~`xd{+XLS]بqumrԨŵ4eZ1՝1{F[|P{2! 걤U.S.OB~݃,CCQ=k\%}>|NW:)\gvtY+vȆ,.r - -;G\zpPs`r3/2EP=jDeSA/y\˴q 3S=(pأ;NoW#w]4`nT/øPW;--:+p*%,xfzdT0,Y.*1 -rv3>Kઈz`aX8$~aW?/~wϵ-ӧ'߷PUI |b#d8<сz=v'>L7>NJ6vv gt؇[ݯzHS8*GKwF-YG cq]_9_&Ɨ>m8{lHe[6fNTlV˧whD6ֈ__z98Bi`{܄v5j$zT6{'iL]VYMu'y!/d8&A풏{Nvt;ʓ3LLhǢ}6bjr)"[R -ğp_ OoҶ Z\jeeq 3r4bָ%(Ar2t3W+x ̽WlBX*Y -S e1N7e9f Y'5';Қmm6s z纡gEW[o1H{@@oN2[#>hcsmRO%i7,5śf2ueL_ȯh>F+I-L%e D| !7UwACaF7KPUJ?~Tl;Ꮀĝ O {"~ZT*zZActMs0́N4zxzV ٴ,_JlԹ΃D+h4v~N5<*Z An~\uͫ̏. ;so:`"Vd}3}!ܲh'a{!# W,e8O߄asMj+i[-UZЀ5t[U]k>q*5 - -(`(ܻ}p2͝'@D,smh|kذՍj~n֥!mLpA W]uvւR|^,N;fuX*_[-PT֏MtVy^mxA Ċ2&Dz$ћgerbi K+NfSӻ-,fyU5_'6QƁC$|J5yE#+ϭ[%+yg&wv}{MUqdz=ZW]A=oۿ`(vߧ~`|h\R+hGH%Q7~)^q~6KGT5rڟ L3*mϋ չ6 0v}- -={ BУ Dll#A^4XF˼ hE/0?adM~}*;<#YZϏcXMu%1,Ӂ9vj1Y}X\OX<+wnPh{:@(DnX_)ܛHBSǫ"Gon;Jb~[f4Tth p_X8?膞u^[BfήE/B^}b; m$5K@e:[ "G~UD| swUd#^qm)gZmU[d V354^j/hciAYI\ @ Pݩ j\亅N\ZpfV;ݨqu ZakliYhqv-[IqU!uFW2WꇫLL^ uadn{v9߬Mݛ@ZI  `ed-ۗ%J.C'-?nƨFYc:%R%OW9_زtvpe-[ }k;C׷5z]h1S9c{ ]ГUғrSD:?}lLhHDKDD +-8&eBOrygrwesx*.UsZX/\oI`jAK+ߧzpLNX-ΏI2`yz|*k,DlcƲ?A YBU7DėnV!*vvVڒ X'eQ\n?oڷ :pzz=vO У`%B 8rj*|j pL4njn#stkm_Ǽ]&U7,<6lKѓ+^b5IOD]$cA eF Opq8\5H o1:05tӓܛ - -@&3hC[ dO^x\x GOe*. SC:󖁳r:lYr+] kOl5j$Gb1Q5zwoRe/엲/mLj'NI6Gih0ό_f7Li֙_i6{*w2F¥Nd81_zѕ\S|v%ֲZh-+h/JTڊW;uid^o>'ErI é)Ͷzy/'7iݙ(KO鹢Ιc vWodQ{5>-MzO7 a6lVge/qK ? lv슜Gsn -C(y|n/\-/Z61;*[[Qϊm7+ -k}Y6z8{MjWTH{GAQ:>Afjv4C[Wjev9A{R}lj팪V0̖ fZeXƣ_߻j_+dcNJq)5Y5$Bc>x׉WK)ܛ6?dhE -yUKmaFxʟ3/2{Cd/R9]>;~-ԪY15tfMOzM~ȥC/b=N&GZݛ^5lHt\m`ͰҦ`M)gu}y+ZVn!>JIy h -s%Q'B/5j_@$L7Z4jvj41TE)uǰѰ̤dѴνE|xB61ymڹho^1Xu Tr-LlP͑3#?wl+c&554ܳwKvɧa -|Fz˟c*q ]+N}M˹UrfYhmS퓟5"{HZzN` v<{ArXo(D[S`D,G((@ov+cOb; xP. r`)#D4NCe3?dȞ+2%t(t!lJ@ottW=t2Ph(C[-9 P!8"y!f -?Hz.nEhuh PU{TL'Q-ҝ @J~Klp@EX|h3 [\2KpS:eזKv,(sŜhp ,A`?ض0 *:[c6P&@W -"Й8 ;/ [ױ}rOWW6U쟸rq!yk69tp߀=E 0f K)CD{Ч$*{_)nfA%WTpwn Ż^! w-.?_o(l⎥~Bڒ++H9kprJ=B^f<k: 3~t3C`Mv p6C[:0@Dǭ.uWW#Zk4WudfDIsFplrW â;zWt{^ɋ2`ƓRHGAƀ/??r^|n-J5nWLRkv.ESj~C饚Iߊ. -ԯ8$~PlTPT_qcU~(޷}!,\s^=;,'s]pp{гKZ͹Htپn,' <5xϖ<=(@SrEq(-Yh8򃬢?.2:]^ÐYWȴ;o.tEv~m+ 7g=c59`viXwpq7PfiHIN=Z8 tuz;"uZ?^hI;ׁE!%vWb=R:DujX]au8p9z m$~V(LY7v|@:_goqfw̦2(k?U@YMWwURVTx,]М$=VߓݠнɭqBȪǽ-it5Lb׹;$sw:k9_x -8n ܺ (f^I=9r װo~䏓FUs=w~OZCweㅽ)箄7Bsh -Q0'0d2l߳"C8 sM (ϥUɄE &}/SYg222ێr=;{v~wZM9L6ftZV3f?ј2k&OyY}%/ʬfk-OiVH`O>d8W -إ.:0s=~6*嬹<: L&$ZAO= -H@ڮp+#@N - پ~K _pqL~ȘHz -zOv~BepvW3fWskOa8yq@ޏO?dC k-|Lmg"~s ق])Zzrƹ:l~B,FKv2g޿/!obWbW`dutSs(+TS741/NXЙx2M*dWڤ=.mE[cr=^6>pqFpNU>? \8h#YInK򃮚g9[|Z iNzJ'RĖ -n%6n?VodT=[l5`llq ,PgB8f=vHcVnˋڤ ˓z蠳Z'ˈtjҨr&RyϦn;"Sp}\:I#v'v@̊u[ 4a6ސFzF3hwpɯoO!kWS+6gŦ[̚eex+-N?%S6lxlX_AI2x(buz>fSr6{gm=zogm~]&NvzjU/gFx(㗏(b3F*YW}Q lEhFt1س}\H3R!6|W/2,ī2Zd)2gD]/ o~r?K@ -h FeRP踷hW?Hedd6XdttFo߲oT5: 亂 Y\=7`u"2+:tJFݿ| #@l PQ$ʞkE @^@@*y39o@W ?0C87}H&^&Gw|QF* (ӗznP=XE &$@O@ @% :@k frVmC9 vN %3etp~U ="<7o] )?96*` Pvnv -2@Z aȉs,bڽ<ڜx7A r^&o-^o8"i+3Z6plXl[uq(?tBbI~q<\gq"?gvF Av F#n -M؜pt9 R* -}~}1^wje'5{Sv+Zm.|6bżeb .Wo~D*Ϩr{zE>7D)XV\]@@DH8CW:x{N/<(47fzf|@xl]Ý7A?=ހxT3cI9pu)u1n;`UN\PDdRtJT2~jϲ>$@9A -ݿ\etVp_xgk#^ 7f; -vqS8o,e+!Ļ{`GzM?րrP=~V&aGTؓCa٥UjM3%s=Rϵ#/Y[ ];6w,};fMf'f= k>˿0xO'%ݽb3U"أֲWܮwg䙕?&K)sW\{n8 -LZu/se̔0 d-o9Wn4:PmX|lh&xmnXKp7{n %P 5Lf喌 ffz -[|o;F$rUay/Y# _u&ݾk]i`LAg 4M@uz}a4vc~b_OiN1x-o`E;д/_mF}WJ\]MIwU/?AI.x ~_X<FU A~>:x؞vB#Ves@ǛLWJ3XjFek;--LkÜ|sXhC6 F1_z[ߒw}$vB9/\<ɾ}uY)Ѷ=hw}qGC>tM?se/]`QП4i.ȋsmN˦uV,qβPKʦn^dnA `^-{2cJZ%v*뫶n6jͮɬ5|q<-@Oi8 .@]Z<j|3.K?YyCasہ1IܜjXElꮘ-5R$&͊=Vg{aK,85;VWN;q]hCuv͚?6oĩ=A+2KV9pTǷ,[K+0%VtmeR0=Z4&^ecA8LɈ? /e*;ڶşomh4TnG-[Wļ~ߔX!\-{,1ry(sIJ"X~vFWڽC,x7ھU ( m3IRm&sYB#Fpc]}a>$I/F,Fȱ6:nmd>r-El Qѷ]JS,^$1?jV=zx^(ڀ]O>ϝ0Քx3 Ӭ&,G Vگwl'"fz_õ]0S]v,ùro8R制 -=Ucn}TDYh +$BQK^Nnxtmœje\LJ/5q`0{LpzL^3fyB <{ۦVdWчW8#OY3S5Cr_^$T}@VK\tu!ukysa0f8]}t_W+_6*ZVҮ JŠ,7/ϲDQMʬ2Y9{[won\-ՉO{"[j5o+a\3[>*U96кeMl%qVsz%[67i\&/uN髻:Otc0*Uj3TRW}gO vߠ!CYdp_\Ӏ.dkge1ѺWϳ9%{be|E -W>y:WAB_eA_j3 =2sz)8oP̳&/ &V zv}|A qgz8UҪƨ;p9ݜ"_Y}6\`^V@greQdwd07/Ȕ'^A"L5-"{1@BdA&'fd̑@F}Lb u K*L]d6lV`fl/>ȝ>"??iZ 4Wc~S |riYu ?<@Z]X ? F)4" )cO5u}+q|W\[*= s=V+C7Ox?pM~3@(72[!@6}E%ɯ$n(E?se?n~SƝu.^"ZF}+@a&z8tV¿o vۏoiA`z4pyI{)gtNӱGĥlR|̹}TB~7Fip+\sH5 ->_A\"PAxB/}d}3t$?xC0繌qdGxw|/֔ ;kY[R|@垧GWstҊfྲྀh7@CBcߣwwGGKd v'ͤ8 su8';똓‹UPDzU<ծ.G7x@?Nu{a?HeNFQ=5FW>)gNDU9kP"BYicx?xr>f:ω;Rnx}6rVٮ`۩۱εp ~MVߡPUN;ľg(CuG,_Q^fzzQ٦)i${/Iwفr vy^#f"< -ffhF&m!2Hd<Tf>U &Ko@˙+w;=U70zpծ[\^/TWgo?Y]v5V# -~ 74--o)Pcx]}+N7ap>\d1,,:^f_\r6ӪV5wl&tF["k9m^h!G/Wiw\ul+]Z?X ϯ w_|7]0=\g9Y[\5oЯTSO.&2w{c܃Š& k`}FrJFT*~ݲie'aG+>M!6ُWX)D3gC}}FjO說͉ETsӟI75/,٤3?讕{B.>V4|-ٍ=[g슟S6 E47@jX>xq~0'jZC[?fbKfi^Z 2]?d~PNkv6giEc2c]z9cxe*-fe^E(znwD\ǰxЃ%T3]zf/nA:uϸ.c,^*G[itV5za}-1yYq&a$2Qq|nOKiyY -BZCc7dk'ЩK?!r]WMt\Y\7}jy{yO wpsMf}n+ʴ>}dP1 * 0(L gYk\ж67.rUc&F-mİW3sTL+K5Y{MyPQm_ix9yS#]Olӽ%+XM뙘`ڜv!լ%0YX!UXMZPKbsOnSq7]ܻW d |n5]4fөTa\??Z$2گ${WTjW3[XwKCn:US瘼\LLE]T^OeF2AsS ̟yann{w]uNo04..մnLCZAtadq=;Gٽ2mYg>[,j4s x8<׍dGeKl߃<{wJ;D r.t7Ԋ8Su -\E72`Լ&wEgrUH=?NZb>JBNͱ3VqxvH_Y9Gf;i]XkzDvp.d].r+"!(׺#\Nt:ehx5Ne캫fRb37ztAc!sk7sVFP)u,.C%ҩG)Iøq.via{A7[h -FW 0uΉ=*3j+rYmf<꽹!5Z/n$y˼[c>MF^ǦۨC׀gD6]s6uz7wNvNj` sqNK]k_>upxԀj=2}}fݴܝܧޖ3N2\7̨驚kD5GNٜ9ܒ"^w%4Pu&g*4}녣/X@`\Hy7ahmޥONL+UjTTRVnÓT)ި9;-M씟4|Ė [V;ԧ^~^b $H{kA{pRH[1oƪvѽjaf*″zˁ e2$`, 1V~K++jZ,B#]z~p ƍv/º_ui͈ɩ لom -RfqV|Ac_ -ܛ( =feQi㜈RW*T+Q8k- -v -fgTʿcZI7^3ssX#nY4˫NF)TSHς*I>_Wiai?!Ejn -u*̽i_4s*HYm nJ>,VdXpJe$/!7e[ˏl`\Ϋx.dh!e#2ڍ...7U_t]trUr˚ÍԝAС:t)L !D;+T还n7ggy"i9Ѭ2f nc0 mKr}fs*5_Hw?X6pa&:'z0FU?C~\uז"i/QZƨ`98鹽1[. o;pXn[*z`\Nh՛fBe5iBăEPqV}]US;1t0&Fmy~~*ߧØ{Ò8CֽCPaC!.;fC}966@x1tͿ?e$#T;#=j12IZ1b$3yacXB1\1\4b1 & ~ %06xTb8,hkk*"9?"f$ʂ@}i#׌tx%.ưn@0 1<6Ó,`TcX [<ڶ^8rڄfE toc411:oe0j +FS+UHȰ̓o|#U lN}|>L@[pSzLJ-a_Aؽ';if͌׳Tf PBĸn1VccWfŨTo.@'#0#G6HA/'C4<ɕ\Zo&xJ:IE <}#@hW^;ۥi^m4Lt98$7J-u:>Hpw=ˉ1z0Sݕc|cHpg{LG?Sױ[!BrdNQS?L>c^qYrw#O'N)l' FC6=0Y/ë/bs1^K'P>´˰ĭgwp7o}.#W!o4_IaGo/ouT>Zk^cJP>u+&凪EŐe>9ye̖I -fi&גuݳuDK\ȯ-/eHxx 89IW˜j M_ J& ;f!GfWS7x|L`<ǾpW,fxm MTȅY` Nj;.0.n\X_P0]6f>7/{cڟTϚ17FtȽdf|L^Nї%R֑e_䊳m[m֩c&5¥Ei4MZL1ko2ݵƔ0{C-sܱڠg`#8v,T`61.N 11K19Yu۔NyB6@v:l;+]w}7+$4KorǼM֤eQo/&b'}uǥ,'%'U3tNqs\8Y7҈i1EC7k;vKt6Tǰ g/0uΟ i,>} e@ -coUՇtFo)GSa,i"'6,)G\N_sГ(jWA4&zꮛ葼UDD eM1[h9]Ledi,66}{$?1=wcv_[_uF詣>T!v!=~8Xca1S  iaxL52ݨ3ӾRRq~HSj|(R'.ڵ(mKb%MN= 6hc\ɱ/ˇU9wSMHhL5dy}+ow+A~IO O\}aqcձ¹ܚ^i Xm’xGL~ix?9s(V`o/;s˩qļ۾[lM@ +]mACȾιh/b(DT'N?̊m2譣MdZͶ\#^ƿ"JO.r6v*57qDAk%Oi.Sdӧ,Ufuz=EW>L/T4VN -»5}#>5 !i^gQ{Uɚxt 0'r/nog}3g4A1#R#zW!H%`?=⇳,anwR/͎)b 'ˮ_-sM{8Z76kpT 6gk1f3 >k\FZ-wSWR=cjpMûQ=Z@rqnLg~!/Iڻ2{pa*Z:z>K:j>AM-*#9^ x)yRK%oKpr[,_`_w{3?whiMm׋Ѿ x\oh5yHkǫV\)v[eU $RPԊLgU$ֹBy"hl雔[iPcViY2- -¥3lᕸftZpUb1`e3v2%!%l#e7(̸u--$)&9}-9g9iDL5gVJ9eGRj)5NQmsfT :W#(\iov \H=7B.(]NQ*߀R,jQ=T@{ -fR1wA @goIVK #kb0ORa~3!›P/!f|!%R\ P4 pZDi1Pq1?)JՆ\P*?9a_t>FXP2F>é}n^'Ɣm yg/tC8Cڹ=i -1S6}~ Ͱ`chmE'vhL 3׏Uo7k?Q^ ] kHHfnua#saLpE00K%@pץYA>pT d{`=Q^ѓoNRcPQ(bdU>@ br3wzCk&F1 -~P:KGܓp7sNGv)`v~"Fz9*K2Fo:Ʋ:܋1$3QQռhя> O.\jP֘[oWvן=}Dro=0ߩUd79Wj^w~nyU_:0XAccGcǾc| nOh>#VGӽ;VV/O2yoy'U%yS]֏M!!hw}恘_hmaeb|Lb|!`0Do}[?t|fKGeyЋB-w\,N[pw\[YۅDZS>sK;θwNk۲Y h5Dŵw h^^?t[|`ݧKV09 sbptb7?hdg nAmyg7Cucv6l۰qn^_mڎnEi0]NWwm;f],z} j.'IFr&.oMm]ҚmK~-P^j,ꤰ>C=V]leQ_X"$J\Ij {%//#y ֜{ξ Nܐh%!l7jy*܊\-Fk812~simdrn8;7Bm^'0׋/bL1.ֈ_>»-x 鬻6iuvu7~[7ZIzgeyIlQl1ؘj⼎bn&zq#TLAPRz5,&=[R{=QxOS*n⾾N"^Z@5Zݻ8}g<5|Y[?9l"m>ώoL[4z P}Hh ~6:4q2ELzYrB݃A/ֈ5"F f'%c«sNPUTAu>nzsv9b`^s)tc$6ŅuٍkkB0TV[\F11tw@J~u u Ys,v 5o39KgLcO*U%=۲h!9-+M{]Hpr] Fpg#Lr5ڻ@ked\3]RGFwD=e$E~ImX5siz$ nrYxz sKze ~71nq6jmRZ޼hAXF[ǪIτf,9FG -|T`L6Ruwi{wW"AGɄq=iI]Jdw]vi~צF[+G͂SdWyUw-?92,H39ڭO|r}?nMh\V.f nѬ66Jm- Amrob a%. :1kh6v -6OZ2sݼBp=əF__`0 ]w@{1[bt]޻XZx7s̱}MrotWtK'E#W;V5*F%WQ`zc_ -֚Ck[tֺҠ=TOͽCqpQ&~yyuChm2ÔϤwy5yb5R~Rz/70Gu/Έ:әlҢRSY&Zm -F/MM(:ք::ZvrS9#*7`SVq$ͻ+;uQzCˍ ]i\uKww$psJ!L=ɶug)5Ϫ^ש(go ̖||t~pSܚ*O &Q} A_AI_7h]jhrf3z5q6{g̑cވwF4DoT̈́udG8gkV|r$cYW?X\b;)UȹeOJ%Zc6}E8\}csd -8NF6 (l2'NgqUv嶬+"d#²#[/1Yf)AkRKR1:.M -d2] oSe4MݤZ -`1i.V -MF0bm4(bE/M Y=@ St_pP @&<4TZUW6],ef/\Se_h4Ζgvlʫb9N (R=) VU endstream endobj 16 0 obj [/ICCBased 25 0 R] endobj 6 0 obj [5 0 R] endobj 48 0 obj <> endobj xref 0 49 0000000000 65535 f -0000000016 00000 n -0000000144 00000 n -0000046947 00000 n -0000000000 00000 f -0000534590 00000 n -0001208740 00000 n -0000046998 00000 n -0000047414 00000 n -0000053435 00000 n -0000535003 00000 n -0000101249 00000 n -0000534776 00000 n -0000534889 00000 n -0000054579 00000 n -0000053496 00000 n -0001208705 00000 n -0000054018 00000 n -0000054066 00000 n -0000054915 00000 n -0000058488 00000 n -0000491173 00000 n -0000054978 00000 n -0000058531 00000 n -0000101284 00000 n -0000101340 00000 n -0000491287 00000 n -0000491350 00000 n -0000491384 00000 n -0000491687 00000 n -0000534478 00000 n -0000491760 00000 n -0000534660 00000 n -0000534691 00000 n -0000535077 00000 n -0000535475 00000 n -0000536535 00000 n -0000542020 00000 n -0000607608 00000 n -0000673196 00000 n -0000684001 00000 n -0000749589 00000 n -0000815177 00000 n -0000880765 00000 n -0000946353 00000 n -0001011941 00000 n -0001077529 00000 n -0001143117 00000 n -0001208763 00000 n -trailer <]>> startxref 1208937 %%EOF \ No newline at end of file diff --git a/doc/deltacheck_logo_large.png b/doc/deltacheck_logo_large.png deleted file mode 100644 index e30de0350..000000000 Binary files a/doc/deltacheck_logo_large.png and /dev/null differ diff --git a/doc/deltacheck_logo_small.png b/doc/deltacheck_logo_small.png deleted file mode 100644 index 23e55f1d5..000000000 Binary files a/doc/deltacheck_logo_small.png and /dev/null differ diff --git a/doc/deltacheck_logo_small_html.html b/doc/deltacheck_logo_small_html.html deleted file mode 100644 index 813274c36..000000000 --- a/doc/deltacheck_logo_small_html.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - -deltacheck_logo_small.png - - - - diff --git a/install.sh b/install.sh index af86688af..c72d8988e 100755 --- a/install.sh +++ b/install.sh @@ -1,13 +1,33 @@ -git clone https://github.com/diffblue/cbmc +#!/bin/bash + +CBMC_REPO=https://github.com/peterschrammel/cbmc +CBMC_VERSION=d95e7da28018fd315b04a1201d5b7cfe8195cbc6 + +if [ "$1" != "" ] +then + COMPILER="$1" +fi + +git clone $CBMC_REPO cd cbmc CBMC=`pwd` -git checkout e4a5a611f569c72f97c0e099e56a827c9a55d2aa -cd src -make minisat2-download -make -cd ../../src +git checkout $CBMC_VERSION +make -C src minisat2-download +if [ "$COMPILER" != "" ] +then + make -C src CXX=$COMPILER +else + make -C src +fi + +cd ../src cp config.inc.template config.inc sed -i.bak "s#CBMC = ~/my-cbmc#CBMC = $CBMC#g" config.inc -make +if [ "$COMPILER" != "" ] +then + make CXX=$COMPILER +else + make +fi cd .. -echo "The executable is src/summarizer/2ls" +echo "The executable is src/2ls/2ls" diff --git a/regression/interprocedural/Makefile b/regression/interprocedural/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/interprocedural/Makefile +++ b/regression/interprocedural/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/interprocedural/contextsensitive1/main.c b/regression/interprocedural/contextsensitive1/main.c index c1b928562..743ca83a6 100644 --- a/regression/interprocedural/contextsensitive1/main.c +++ b/regression/interprocedural/contextsensitive1/main.c @@ -1,17 +1,17 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive2/main.c b/regression/interprocedural/contextsensitive2/main.c index b228a31b4..4547b1d62 100644 --- a/regression/interprocedural/contextsensitive2/main.c +++ b/regression/interprocedural/contextsensitive2/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive3/main.c b/regression/interprocedural/contextsensitive3/main.c index deab63659..e6ff9f250 100644 --- a/regression/interprocedural/contextsensitive3/main.c +++ b/regression/interprocedural/contextsensitive3/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive4/main.c b/regression/interprocedural/contextsensitive4/main.c index 9f1348396..3a9f9d42d 100644 --- a/regression/interprocedural/contextsensitive4/main.c +++ b/regression/interprocedural/contextsensitive4/main.c @@ -1,29 +1,29 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive5/main.c b/regression/interprocedural/contextsensitive5/main.c index fb0251140..25f460e52 100644 --- a/regression/interprocedural/contextsensitive5/main.c +++ b/regression/interprocedural/contextsensitive5/main.c @@ -1,15 +1,15 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} + diff --git a/regression/interprocedural/contextsensitive6/main.c b/regression/interprocedural/contextsensitive6/main.c index be4631faa..2030a7355 100644 --- a/regression/interprocedural/contextsensitive6/main.c +++ b/regression/interprocedural/contextsensitive6/main.c @@ -1,12 +1,12 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - + +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} + diff --git a/regression/interprocedural/global1/main.c b/regression/interprocedural/global1/main.c index ecb897b62..01bd988fa 100644 --- a/regression/interprocedural/global1/main.c +++ b/regression/interprocedural/global1/main.c @@ -1,21 +1,21 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} + diff --git a/regression/interprocedural/global2/main.c b/regression/interprocedural/global2/main.c index f653bce18..d2ae47492 100644 --- a/regression/interprocedural/global2/main.c +++ b/regression/interprocedural/global2/main.c @@ -1,22 +1,22 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} + diff --git a/regression/interprocedural/global5/main.c b/regression/interprocedural/global5/main.c new file mode 100644 index 000000000..f7e5c9bd0 --- /dev/null +++ b/regression/interprocedural/global5/main.c @@ -0,0 +1,19 @@ +#include + +int x = 0; + +void foo() +{ + x = 1; +} + +void bar() +{ + foo(); +} + +int main(int argc, char** argv) +{ + bar(); + assert(x == 1); +} diff --git a/regression/one-version/context2/test.desc b/regression/interprocedural/global5/test.desc similarity index 75% rename from regression/one-version/context2/test.desc rename to regression/interprocedural/global5/test.desc index 81e6e1c1a..4c994a780 100644 --- a/regression/one-version/context2/test.desc +++ b/regression/interprocedural/global5/test.desc @@ -1,7 +1,6 @@ KNOWNBUG main.c - +--context-sensitive ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/interprocedural/partialinline1/test.desc b/regression/interprocedural/partialinline1/test.desc index 46a1a642f..0eb76f70f 100644 --- a/regression/interprocedural/partialinline1/test.desc +++ b/regression/interprocedural/partialinline1/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 5 +--inline-partial 7 ^EXIT=10$ ^SIGNAL=0$ ^** 1 of 2 failed$ diff --git a/regression/interprocedural/partialinline2/test.desc b/regression/interprocedural/partialinline2/test.desc index caccafb2c..7e9e73c85 100644 --- a/regression/interprocedural/partialinline2/test.desc +++ b/regression/interprocedural/partialinline2/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 6 +--inline-partial 8 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/interprocedural/sum1/main.c b/regression/interprocedural/sum1/main.c index df354feeb..b28d8b049 100644 --- a/regression/interprocedural/sum1/main.c +++ b/regression/interprocedural/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/regression/invariants/Makefile b/regression/invariants/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/invariants/Makefile +++ b/regression/invariants/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/invariants/ite1/main.c b/regression/invariants/ite1/main.c index d34a2cd40..f846c9438 100644 --- a/regression/invariants/ite1/main.c +++ b/regression/invariants/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) //need to distinguish first iteration - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) //need to distinguish first iteration + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/invariants/unwind20/test.desc b/regression/invariants/unwind20/test.desc index bc9c22f1f..744e6bbe7 100644 --- a/regression/invariants/unwind20/test.desc +++ b/regression/invariants/unwind20/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --unwind 10 ^EXIT=10$ diff --git a/regression/kiki-modular/Makefile b/regression/kiki-modular/Makefile new file mode 100644 index 000000000..d2ff74910 --- /dev/null +++ b/regression/kiki-modular/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/kiki-modular/cex-struct1/main.c b/regression/kiki-modular/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/kiki-modular/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/one-version/array1/test.desc b/regression/kiki-modular/cex-struct1/test.desc similarity index 53% rename from regression/one-version/array1/test.desc rename to regression/kiki-modular/cex-struct1/test.desc index 25b56b8ec..b5b4ee326 100644 --- a/regression/one-version/array1/test.desc +++ b/regression/kiki-modular/cex-struct1/test.desc @@ -1,7 +1,6 @@ CORE main.c ---bounds-check +--havoc --k-induction --spurious-check complete ^EXIT=10$ ^SIGNAL=0$ -^** 2 of 4 failed$ ^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex-struct2/main.c b/regression/kiki-modular/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/kiki-modular/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/kiki-modular/cex-struct2/test.desc b/regression/kiki-modular/cex-struct2/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex-struct3/main.c b/regression/kiki-modular/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/kiki-modular/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/kiki-modular/cex-struct3/test.desc b/regression/kiki-modular/cex-struct3/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex1/main.c b/regression/kiki-modular/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/kiki-modular/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/cex1/test.desc b/regression/kiki-modular/cex1/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex10/main.c b/regression/kiki-modular/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/kiki-modular/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/kiki-modular/cex10/test.desc b/regression/kiki-modular/cex10/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex13/main.c b/regression/kiki-modular/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/kiki-modular/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/cex13/test.desc b/regression/kiki-modular/cex13/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex2/main.c b/regression/kiki-modular/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/kiki-modular/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/cex2/test.desc b/regression/kiki-modular/cex2/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex3/main.c b/regression/kiki-modular/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/kiki-modular/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/cex3/test.desc b/regression/kiki-modular/cex3/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex4/main.c b/regression/kiki-modular/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/kiki-modular/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/kiki-modular/cex4/test.desc b/regression/kiki-modular/cex4/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex5/main.c b/regression/kiki-modular/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/kiki-modular/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/cex5/test.desc b/regression/kiki-modular/cex5/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex6/main.c b/regression/kiki-modular/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/kiki-modular/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/kiki-modular/cex6/test.desc b/regression/kiki-modular/cex6/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex7/main.c b/regression/kiki-modular/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/kiki-modular/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/kiki-modular/cex7/test.desc b/regression/kiki-modular/cex7/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex8/main.c b/regression/kiki-modular/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/kiki-modular/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/kiki-modular/cex8/test.desc b/regression/kiki-modular/cex8/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/cex9/main.c b/regression/kiki-modular/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/kiki-modular/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/kiki-modular/cex9/test.desc b/regression/kiki-modular/cex9/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/kiki-modular/induction1/main.c b/regression/kiki-modular/induction1/main.c new file mode 100644 index 000000000..23a7e0054 --- /dev/null +++ b/regression/kiki-modular/induction1/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1; + + while(1) + { + if(x==2) x=-x; + if(x>0) x++; + if(x==0) assert(0); + if(-10<=x && x<0) x--; + } +} diff --git a/regression/kiki-modular/induction1/test.desc b/regression/kiki-modular/induction1/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/induction1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/induction2/main.c b/regression/kiki-modular/induction2/main.c new file mode 100644 index 000000000..76f88d6eb --- /dev/null +++ b/regression/kiki-modular/induction2/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + while(1) + { + z = y; + y = x; + x = -x; + assert(x==z); + } +} diff --git a/regression/kiki-modular/induction2/test.desc b/regression/kiki-modular/induction2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/induction2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/induction3/main.c b/regression/kiki-modular/induction3/main.c new file mode 100644 index 000000000..38ef4b24f --- /dev/null +++ b/regression/kiki-modular/induction3/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 0, y = 0, z = 0; + + while(1) + { + z = -y; + y = -x; + x++; + assert(x<=z+2); + } +} diff --git a/regression/kiki-modular/induction3/test.desc b/regression/kiki-modular/induction3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/induction3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/induction4/main.c b/regression/kiki-modular/induction4/main.c new file mode 100644 index 000000000..241e31944 --- /dev/null +++ b/regression/kiki-modular/induction4/main.c @@ -0,0 +1,16 @@ +#define a 2 + +extern int nondet_int(); + +int main() { + int i=0, n=3; + + int sn0 = nondet_int(); + int sn = sn0; + + while(i=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/kiki-modular/induction8/test.desc b/regression/kiki-modular/induction8/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/induction8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine1/main.c b/regression/kiki-modular/inline-refine1/main.c new file mode 100644 index 000000000..5662b4933 --- /dev/null +++ b/regression/kiki-modular/inline-refine1/main.c @@ -0,0 +1,17 @@ +#include + +int inc(int x) +{ + return x+1; +} + +void main() +{ + int x = 0; + int y = 0; + + x = inc(x); + y = inc(y); + + assert(x==1); +} diff --git a/regression/kiki-modular/inline-refine1/test.desc b/regression/kiki-modular/inline-refine1/test.desc new file mode 100644 index 000000000..af2fdc37b --- /dev/null +++ b/regression/kiki-modular/inline-refine1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --no-propagation --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine2/main.c b/regression/kiki-modular/inline-refine2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/kiki-modular/inline-refine2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/inline-refine2/test.desc b/regression/kiki-modular/inline-refine2/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine3/main.c b/regression/kiki-modular/inline-refine3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/kiki-modular/inline-refine3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/inline-refine3/test.desc b/regression/kiki-modular/inline-refine3/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine4/main.c b/regression/kiki-modular/inline-refine4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/kiki-modular/inline-refine4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/inline-refine4/test.desc b/regression/kiki-modular/inline-refine4/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine5/main.c b/regression/kiki-modular/inline-refine5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/kiki-modular/inline-refine5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/kiki-modular/inline-refine5/test.desc b/regression/kiki-modular/inline-refine5/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine6/main.c b/regression/kiki-modular/inline-refine6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/kiki-modular/inline-refine6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/kiki-modular/inline-refine6/test.desc b/regression/kiki-modular/inline-refine6/test.desc new file mode 100644 index 000000000..dbc94b93b --- /dev/null +++ b/regression/kiki-modular/inline-refine6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 --k-induction --spurious-check concrete +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/kiki-modular/inline-refine7/main.c b/regression/kiki-modular/inline-refine7/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/kiki-modular/inline-refine7/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/inline-refine7/test.desc b/regression/kiki-modular/inline-refine7/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine8/main.c b/regression/kiki-modular/inline-refine8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/kiki-modular/inline-refine8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/kiki-modular/inline-refine8/test.desc b/regression/kiki-modular/inline-refine8/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine9/main.c b/regression/kiki-modular/inline-refine9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/kiki-modular/inline-refine9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/kiki-modular/inline-refine9/test.desc b/regression/kiki-modular/inline-refine9/test.desc new file mode 100644 index 000000000..842d3328d --- /dev/null +++ b/regression/kiki-modular/inline-refine9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind1/main.c b/regression/kiki-modular/kind1/main.c new file mode 100644 index 000000000..679c7c8be --- /dev/null +++ b/regression/kiki-modular/kind1/main.c @@ -0,0 +1,22 @@ +#include + +//Simple K-Induction safe example for K=4 +//No procedure calls; loop inv required: a != b != c + +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) + { + assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + i++; + } + + return 0; +} + diff --git a/regression/kiki-modular/kind1/test.desc b/regression/kiki-modular/kind1/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind2/main.c b/regression/kiki-modular/kind2/main.c new file mode 100644 index 000000000..0925661c3 --- /dev/null +++ b/regression/kiki-modular/kind2/main.c @@ -0,0 +1,28 @@ +#include + + +int foo(int a, int b, int c) +{ + return a+1; +} + +// This main illustrates K-induction with proc call +// Safety can be shown for K = 4 after using summary TRUE for foo +// Only unwinding loop is required +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { + assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + if (b == c) a = foo(a,b,c); + i++; + } + + return 0; +} + diff --git a/regression/kiki-modular/kind2/test.desc b/regression/kiki-modular/kind2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind3/main.c b/regression/kiki-modular/kind3/main.c new file mode 100644 index 000000000..813307dda --- /dev/null +++ b/regression/kiki-modular/kind3/main.c @@ -0,0 +1,26 @@ +#include + + +int bar(int a, int b, int c) +{ + return c; +} + +//This main illustrates need for refinement of bar +//based on spurious CEX +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { + assert(a != b); + sc = c; c = b; b = a; a = bar(a,b,sc); + //a, b, c = bar(a,b,c), a, b; parallel assignment; + i++; + } + + return 0; +} + diff --git a/regression/kiki-modular/kind3/test.desc b/regression/kiki-modular/kind3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind4/main.c b/regression/kiki-modular/kind4/main.c new file mode 100644 index 000000000..5b4106e35 --- /dev/null +++ b/regression/kiki-modular/kind4/main.c @@ -0,0 +1,37 @@ +#include + + +int foo(int a, int b, int c) +{ + return a+1; +} + +int bar(int a, int b, int c) +{ + assert(a != b); + return c; +} + + +// This main illustrates K-induction with proc call +// Safety can be shown for K = 4 after using summary TRUE for foo +// Only unwinding loop is required +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { +// assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + if (b == c) a = foo(a,b,c); + //else //TODO: investigate why k-ind doesn't terminate if the call to bar is here + c = bar(a,b,c); + i++; + } + + return 0; +} + diff --git a/regression/kiki-modular/kind4/test.desc b/regression/kiki-modular/kind4/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind5/main.c b/regression/kiki-modular/kind5/main.c new file mode 100644 index 000000000..6f6d29b27 --- /dev/null +++ b/regression/kiki-modular/kind5/main.c @@ -0,0 +1,18 @@ +#include + +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,sc, i = 0; + __CPROVER_assume(a != b); + + while (i < limit) + { + assert(a != b); + sc = b; b = a; a = sc; + i++; + } + + return 0; +} + diff --git a/regression/kiki-modular/kind5/test.desc b/regression/kiki-modular/kind5/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind6/main.c b/regression/kiki-modular/kind6/main.c new file mode 100644 index 000000000..4b957b301 --- /dev/null +++ b/regression/kiki-modular/kind6/main.c @@ -0,0 +1,34 @@ + +#include + +int inc(int c) +{ + return c+1; +} + +int dec(int b) +{ + return b-1; +} + +int add(int i, int j) +{ + int b = i; + int c = j; + int ret = c; + + while(b > 0){ + b = dec(b); + c = inc(c); + ret = c; + assert((ret + b) == (i + j)); //loop invariant + } + assert(ret == (i + j)); + return ret; +} + +void main() { + int x = 5; + int y = 3; + int result = add(x, y); +} diff --git a/regression/kiki-modular/kind6/test.desc b/regression/kiki-modular/kind6/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind7/main.c b/regression/kiki-modular/kind7/main.c new file mode 100644 index 000000000..30592979f --- /dev/null +++ b/regression/kiki-modular/kind7/main.c @@ -0,0 +1,35 @@ + +// for complete to work faster: introduce a function call to modify an irrelevant variable + +#include + +unsigned nondet_int(); + +int foo(int a){ + + a = a + (2*a) + (3*a); + a = a - (2*a) - (3*a); + a = -a; + + return a; + +} + +int main(){ + + int x; + int y; + + __CPROVER_assume(x > 0 and x < 10); + + while(x < 10000){ + x = x + 1; + y = foo(y); + + if(nondet_int()) + break; + } + + assert(x < 10001); + +} diff --git a/regression/kiki-modular/kind7/test.desc b/regression/kiki-modular/kind7/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/kind8/main.c b/regression/kiki-modular/kind8/main.c new file mode 100644 index 000000000..dbebaeb45 --- /dev/null +++ b/regression/kiki-modular/kind8/main.c @@ -0,0 +1,40 @@ + +// for concrete to work faster: a false assertion such that all counterexamples are valid counterexamples + +#include + +unsigned nondet_int(); + +int bar(int x){ + if((x > 0) || (x <= 0)) + assert(0); + + return 0; +} + +int inc_or_dec(int x){ + if(nondet_int()) + x = x + 1; + else + x = x - 1; + + return x; +} + +int main(){ + + int k; + int counter = 0 + + while(counter < 100000){ + k = inc_or_dec(k); + if(nondet_int()) + break; + counter++; + } + + bar(k); + + return 0; + +} diff --git a/regression/kiki-modular/kind8/test.desc b/regression/kiki-modular/kind8/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/loop25/main.c b/regression/kiki-modular/loop25/main.c new file mode 100644 index 000000000..8febd85d7 --- /dev/null +++ b/regression/kiki-modular/loop25/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + while(x==z) + { + z = y; + y = x; + x = -x; + } + assert(0); +} diff --git a/regression/kiki-modular/loop25/test.desc b/regression/kiki-modular/loop25/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/loop25/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/loop27/main.c b/regression/kiki-modular/loop27/main.c new file mode 100644 index 000000000..ca9acda4c --- /dev/null +++ b/regression/kiki-modular/loop27/main.c @@ -0,0 +1,13 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + do + { + z = y; + y = x; + x = -x; + } + while(x==z); + assert(0); +} diff --git a/regression/kiki-modular/loop27/test.desc b/regression/kiki-modular/loop27/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/loop27/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/loop28/main.c b/regression/kiki-modular/loop28/main.c new file mode 100644 index 000000000..7ae8d5301 --- /dev/null +++ b/regression/kiki-modular/loop28/main.c @@ -0,0 +1,8 @@ +void main() { + int b = 3; + unsigned int j=0; + while (j<1 && b!=3) { + j++; + } + assert(j<1); +} diff --git a/regression/kiki-modular/loop28/test.desc b/regression/kiki-modular/loop28/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/loop28/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nested11/main.c b/regression/kiki-modular/nested11/main.c new file mode 100644 index 000000000..790b6c4b3 --- /dev/null +++ b/regression/kiki-modular/nested11/main.c @@ -0,0 +1,19 @@ +void main() +{ + int x,y; + for(x=0;x<10;) + { + for(y=0;y + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/nocex1/test.desc b/regression/kiki-modular/nocex1/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex2/main.c b/regression/kiki-modular/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/kiki-modular/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/nocex2/test.desc b/regression/kiki-modular/nocex2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex3/main.c b/regression/kiki-modular/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/kiki-modular/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/nocex3/test.desc b/regression/kiki-modular/nocex3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex4/main.c b/regression/kiki-modular/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/kiki-modular/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/nocex4/test.desc b/regression/kiki-modular/nocex4/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex5/main.c b/regression/kiki-modular/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/kiki-modular/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/kiki-modular/nocex5/test.desc b/regression/kiki-modular/nocex5/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex6/main.c b/regression/kiki-modular/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/kiki-modular/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/kiki-modular/nocex6/test.desc b/regression/kiki-modular/nocex6/test.desc new file mode 100644 index 000000000..f0edbd788 --- /dev/null +++ b/regression/kiki-modular/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 --k-induction --spurious-check complete +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/kiki-modular/nocex8/main.c b/regression/kiki-modular/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/kiki-modular/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/kiki-modular/nocex8/test.desc b/regression/kiki-modular/nocex8/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex9/main.c b/regression/kiki-modular/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/kiki-modular/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/kiki-modular/nocex9/test.desc b/regression/kiki-modular/nocex9/test.desc new file mode 100644 index 000000000..312f3d84e --- /dev/null +++ b/regression/kiki-modular/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/s3_clnt_1/main.c b/regression/kiki-modular/s3_clnt_1/main.c new file mode 100644 index 000000000..f958efad6 --- /dev/null +++ b/regression/kiki-modular/s3_clnt_1/main.c @@ -0,0 +1,64 @@ +#include + +extern int __VERIFIER_nondet_int(); + +int ssl3_connect(void) +{ + int s__state ; + int blastFlag ; + + s__state = 12292; + blastFlag = 0; + + while (1) { + if (s__state == 12292) { + goto switch_1_12292; + } else { + if (s__state == 4368) { + goto switch_1_4368; + } else { + if (s__state == 4384) { + goto switch_1_4384; + } else { + if (s__state == 4400) { + goto switch_1_4400; + } else { + return 0; + if (0) { + switch_1_12292: /* CIL Label */ + s__state = 4368; + continue; + switch_1_4368: /* CIL Label */ ; + blastFlag++; + s__state = 4384; + continue; + switch_1_4384: /* CIL Label */ ; + blastFlag++; + s__state = 4400; + continue; + switch_1_4400: /* CIL Label */ ; + if (blastFlag == 2) { + break; + } + continue; + } + } + } + } + } + } + assert(0); + return -1; +} +int main(void) +{ + ssl3_connect(); + return 0; +} + +/* +We get hoisted assertion: +(C) $guard#ls50%2 && ($cond#22%2 || $cond#36%2 || $cond#49%2) ==> ($guard#51 ==> FALSE) + +But $cond#36%2 is a return and no break condition! +*/ diff --git a/regression/kiki-modular/s3_clnt_1/test.desc b/regression/kiki-modular/s3_clnt_1/test.desc new file mode 100644 index 000000000..62a74380a --- /dev/null +++ b/regression/kiki-modular/s3_clnt_1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^.*FAILURE$ diff --git a/regression/kiki-modular/scope1/main.c b/regression/kiki-modular/scope1/main.c new file mode 100644 index 000000000..ec678aac1 --- /dev/null +++ b/regression/kiki-modular/scope1/main.c @@ -0,0 +1,11 @@ +void main() +{ + int y = 5; + int i; + for(i=0; i<10; i+=y) + { + int y = 20; + } + assert(y==5); + assert(i==10); +} diff --git a/regression/kiki-modular/scope1/test.desc b/regression/kiki-modular/scope1/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/scope1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/unwind-refine1/main.c b/regression/kiki-modular/unwind-refine1/main.c new file mode 100644 index 000000000..6852ba883 --- /dev/null +++ b/regression/kiki-modular/unwind-refine1/main.c @@ -0,0 +1,15 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + int c; + + while(x<10) ++x; + + int c; + while(c) ++y; + + assert(x==10); +} diff --git a/regression/kiki-modular/unwind-refine1/test.desc b/regression/kiki-modular/unwind-refine1/test.desc new file mode 100644 index 000000000..80195020a --- /dev/null +++ b/regression/kiki-modular/unwind-refine1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --no-propagation --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki/Makefile b/regression/kiki/Makefile index d2ff74910..569e1b125 100644 --- a/regression/kiki/Makefile +++ b/regression/kiki/Makefile @@ -1,12 +1,12 @@ default: tests.log -FLAGS = --verbosity 10 +FLAGS = --verbosity 10 --inline test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/kiki/induction4/main.c b/regression/kiki/induction4/main.c index c55adf781..241e31944 100644 --- a/regression/kiki/induction4/main.c +++ b/regression/kiki/induction4/main.c @@ -1,16 +1,16 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3; - - int sn0 = nondet_int(); - int sn = sn0; - - while(i=10) { - x = y = z = 0; - } -// if(x>z+2) break; -// assert(x<=z+2); - } - assert(0); //this works with assertion hoisting -} +int main() { + int x,y,z; + __CPROVER_assume(x==y && y==z && -10<=x && x<0); + +// while(1) + while(x<=z+2) + { +// __CPROVER_assume(x<=z+2); + z = -y; + y = -x; + if(nondet()) x = x+1; + if(x>=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/kiki/nested12/main.c b/regression/kiki/nested12/main.c index 3b136bbd9..c36a9507a 100644 --- a/regression/kiki/nested12/main.c +++ b/regression/kiki/nested12/main.c @@ -1,18 +1,18 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3,sn=0,x,y; - - for(x=0;x<5;x++) - { - sn = nondet_int(); - - while(i=0 && i<10) - { - my_buffer[i]=0; // should pass - } - else - my_buffer[i]=1; // should fail -} diff --git a/regression/one-version/array2/main.c b/regression/one-version/array2/main.c deleted file mode 100644 index 764abe8c8..000000000 --- a/regression/one-version/array2/main.c +++ /dev/null @@ -1,21 +0,0 @@ -int main() -{ - long int i, j; - char my_buffer[10]; - - // constant index - my_buffer[1]=1; - my_buffer[2]=2; - assert(my_buffer[1]==1); - assert(my_buffer[2]==2); - - // variable index - if(i>=0 && i<10 && j>=0 && j<10) - { - my_buffer[i]=1; - assert(my_buffer[i]==1); - my_buffer[j]=2; - assert(my_buffer[j]==2); - if(i!=j) assert(my_buffer[i]==1); - } -} diff --git a/regression/one-version/assumption1/main.c b/regression/one-version/assumption1/main.c deleted file mode 100644 index 63ac134ec..000000000 --- a/regression/one-version/assumption1/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - int x, i, y; - - __CPROVER_assume(x==y); - - x++; - y++; - - assert(x==y); -} diff --git a/regression/one-version/context1/main.c b/regression/one-version/context1/main.c deleted file mode 100644 index 8e4a9bfd3..000000000 --- a/regression/one-version/context1/main.c +++ /dev/null @@ -1,12 +0,0 @@ -void f(int i) -{ - if(i == 1) - assert(0); // this should not fail, due to calling context -} - -void main() -{ - int i = 2; - f(i); -} - diff --git a/regression/one-version/context1/test.desc b/regression/one-version/context1/test.desc deleted file mode 100644 index d19906887..000000000 --- a/regression/one-version/context1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/context2/main.c b/regression/one-version/context2/main.c deleted file mode 100644 index 36eb553b0..000000000 --- a/regression/one-version/context2/main.c +++ /dev/null @@ -1,18 +0,0 @@ -int z; - -int f(int i) -{ - z=1; - return i; -} - -void main() -{ - int i, j; - j=f(i); - - // should pass, due to postcondition of f - assert(j==i); - assert(z==1); -} - diff --git a/regression/one-version/context3/main.c b/regression/one-version/context3/main.c deleted file mode 100644 index 5b87d158a..000000000 --- a/regression/one-version/context3/main.c +++ /dev/null @@ -1,26 +0,0 @@ -#include - -void main() -{ - int i; - - if(i==1) - { - // should pass, due to postcondition of exit() - exit(0); - assert(0); - } - else if(i==2) - { - // should pass, due to postcondition of _Exit() - _Exit(0); - assert(0); - } - else if(i==3) - { - // should pass, due to postcondition of abort() - abort(); - assert(0); - } -} - diff --git a/regression/one-version/context4/main.c b/regression/one-version/context4/main.c deleted file mode 100644 index 6ac09c6e6..000000000 --- a/regression/one-version/context4/main.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -void f(int i, int j) -{ - if(i!=j) - exit(0); -} - -void main() -{ - int i, j; - - f(i, j); - - // should pass, due to postcondition of f - assert(i==j); -} - diff --git a/regression/one-version/context4/test.desc b/regression/one-version/context4/test.desc deleted file mode 100644 index d19906887..000000000 --- a/regression/one-version/context4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/function1/main.c b/regression/one-version/function1/main.c deleted file mode 100644 index bc9cd7dc2..000000000 --- a/regression/one-version/function1/main.c +++ /dev/null @@ -1,24 +0,0 @@ -int my_global; - -int f(void) -{ - my_global=4; - return 2; -} - -int main() -{ - int i=1; - my_global=3; - - i=f(); - - // check that 'i' is assigned, i.e., should fail - assert(i==1); - - // check that 'my_global' is assigned, i.e., should fail - assert(my_global==3); - - return 0; -} - diff --git a/regression/one-version/goto1/main.c b/regression/one-version/goto1/main.c deleted file mode 100644 index e90e2e2db..000000000 --- a/regression/one-version/goto1/main.c +++ /dev/null @@ -1,23 +0,0 @@ -int main() -{ - int x, i, y; - - y=1; - - switch(i) - { - case 1: goto l1; - case 2: goto l2; - case 3: goto l3; - case 4: goto l4; - default: return 0; - } - - l1: x=1; goto check; - l2: x=y; goto check; - l3: x=1; goto check; - l4: x=1; goto check; - - check: - assert(x==1); -} diff --git a/regression/one-version/goto2/main.c b/regression/one-version/goto2/main.c deleted file mode 100644 index 01272964c..000000000 --- a/regression/one-version/goto2/main.c +++ /dev/null @@ -1,22 +0,0 @@ -int main() -{ - int x, i; - - switch(i) - { - case 1: goto l1; - case 2: goto l2; - case 3: goto l3; - case 4: goto l4; - default: return 0; - } - - l1: x=1; goto check; - l2: x=2; goto check; - l3: x=1; goto check; - l4: x=1; goto check; - - check: - // should fail - assert(x==1); -} diff --git a/regression/one-version/guards1/main.c b/regression/one-version/guards1/main.c deleted file mode 100644 index 9808a9254..000000000 --- a/regression/one-version/guards1/main.c +++ /dev/null @@ -1,12 +0,0 @@ -int main() -{ - int x; - x=1; - - while(x==1) - { - } - - // the above never exits - assert(0); -} diff --git a/regression/one-version/guards1/test.desc b/regression/one-version/guards1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/guards1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/guards2/main.c b/regression/one-version/guards2/main.c deleted file mode 100644 index 0871f3447..000000000 --- a/regression/one-version/guards2/main.c +++ /dev/null @@ -1,15 +0,0 @@ -int main() -{ - int i, j, k; - - __CPROVER_assume(i==10); - - k=100; - - while(j!=10) - { - } - - __CPROVER_assert(i==10, "i"); - __CPROVER_assert(j==10, "j"); -} diff --git a/regression/one-version/guards2/test.desc b/regression/one-version/guards2/test.desc deleted file mode 100644 index 81e6e1c1a..000000000 --- a/regression/one-version/guards2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/interval1/main.c b/regression/one-version/interval1/main.c deleted file mode 100644 index 5c84d5f76..000000000 --- a/regression/one-version/interval1/main.c +++ /dev/null @@ -1,13 +0,0 @@ -int main() -{ - unsigned int x, y; - - __CPROVER_assume(x>=1 && x<=3); - __CPROVER_assume(y<2); - - // should be both UNSAT - assert(x!=4); - assert(y<=1); - - return 0; -} diff --git a/regression/one-version/interval1/test.desc b/regression/one-version/interval1/test.desc deleted file mode 100644 index 5cc4b55db..000000000 --- a/regression/one-version/interval1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/interval2/main.c b/regression/one-version/interval2/main.c deleted file mode 100644 index 9e5697e10..000000000 --- a/regression/one-version/interval2/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - unsigned int x, y; - - __CPROVER_assume(x>=1 && x<=3); - __CPROVER_assume(y<=2); - - // should be both SAT - assert(x!=2); - assert(y!=x); -} diff --git a/regression/one-version/loop1/main.c b/regression/one-version/loop1/main.c deleted file mode 100644 index a1e45faac..000000000 --- a/regression/one-version/loop1/main.c +++ /dev/null @@ -1,17 +0,0 @@ -int some_function(); - -int main() -{ - unsigned int x, y; - - x=0; - y=0; - - while(some_function()) - { - x++; - y++; - } - - assert(x==y); -} diff --git a/regression/one-version/loop1/test.desc b/regression/one-version/loop1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/loop1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop2/main.c b/regression/one-version/loop2/main.c deleted file mode 100644 index 1acf2c01f..000000000 --- a/regression/one-version/loop2/main.c +++ /dev/null @@ -1,20 +0,0 @@ -int some_function(); - -int main() -{ - unsigned int x, y, c; - - x=0; - y=0; - - while(some_function()) - { - if(c) - { - x++; - y++; - } - } - - assert(x==y); -} diff --git a/regression/one-version/loop2/test.desc b/regression/one-version/loop2/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/loop2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop3/main.c b/regression/one-version/loop3/main.c deleted file mode 100644 index ce5792d6c..000000000 --- a/regression/one-version/loop3/main.c +++ /dev/null @@ -1,21 +0,0 @@ -int x, y, z; - -int main() -{ - // does not require invariant, just exit condition - for(x=0; x!=100; x++); - assert(x==100); - - // same again with do-while - y=0; - do y++; while(y!=100); - assert(y==100); - - // Same again with a 'break', but this is basically - // the same CFG as the first one. - z=0; - while(1) { if(z==100) break; z++; } - assert(z==100); - - return 0; -} diff --git a/regression/one-version/loop3/test.desc b/regression/one-version/loop3/test.desc deleted file mode 100644 index 28d612c3f..000000000 --- a/regression/one-version/loop3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 3 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop4/main.c b/regression/one-version/loop4/main.c deleted file mode 100644 index 5d2168ad1..000000000 --- a/regression/one-version/loop4/main.c +++ /dev/null @@ -1,14 +0,0 @@ -int main() -{ - int i; - - // jump into the loop - goto label; - - while(i!=100) - { - label:; - } - - assert(0); // unsafe -} diff --git a/regression/one-version/pointer1/main.c b/regression/one-version/pointer1/main.c deleted file mode 100644 index 725733dc8..000000000 --- a/regression/one-version/pointer1/main.c +++ /dev/null @@ -1,10 +0,0 @@ -unsigned int x, y; - -int main() -{ - unsigned int *p; - - p=&x; - - y=*p; -} diff --git a/regression/one-version/pointer1/test.desc b/regression/one-version/pointer1/test.desc deleted file mode 100644 index f6cb77be0..000000000 --- a/regression/one-version/pointer1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---pointer-check -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 0 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer2/main.c b/regression/one-version/pointer2/main.c deleted file mode 100644 index 12a0d0e6c..000000000 --- a/regression/one-version/pointer2/main.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -int global; - -int main() -{ - int *p; - - p=&global; - assert(p==&global); - global=1; - assert(global==1); - - // writing to pointer - *p=2; - assert(global==2); - - // reading from pointer - global=3; - assert(*p==3); -} diff --git a/regression/one-version/pointer2/test.desc b/regression/one-version/pointer2/test.desc deleted file mode 100644 index 8c2cd97d3..000000000 --- a/regression/one-version/pointer2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---pointer-check -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer3/main.c b/regression/one-version/pointer3/main.c deleted file mode 100644 index 376867895..000000000 --- a/regression/one-version/pointer3/main.c +++ /dev/null @@ -1,23 +0,0 @@ -struct S -{ - unsigned int x, y; -} my_s; - -int main() -{ - unsigned int *p; - - my_s.x=0; - my_s.y=0; - - // read - p=&my_s.x; - *p=2; - my_s.x=1; - assert(*p==1); - - // write - p=&my_s.y; - *p=3; - assert(my_s.y==3); -} diff --git a/regression/one-version/pointer3/test.desc b/regression/one-version/pointer3/test.desc deleted file mode 100644 index 5cc4b55db..000000000 --- a/regression/one-version/pointer3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer4/main.c b/regression/one-version/pointer4/main.c deleted file mode 100644 index 6fdf021c4..000000000 --- a/regression/one-version/pointer4/main.c +++ /dev/null @@ -1,26 +0,0 @@ -int array[10000]; - -int main() -{ - int *p; - int index; - - // read - p=array; - assert(p==array); - array[1]=10; - p++; - assert(p[0]==10); - - // read with index - if(index>=0 && index<10000) - { - p=array+index; - assert(*p==array[index]); - } - - // write - p=array; - *(p+3)=3; - assert(array[3]==3); -} diff --git a/regression/one-version/pointer4/test.desc b/regression/one-version/pointer4/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/pointer4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer5/main.c b/regression/one-version/pointer5/main.c deleted file mode 100644 index 223b6d6de..000000000 --- a/regression/one-version/pointer5/main.c +++ /dev/null @@ -1,25 +0,0 @@ -int i; - -int main() -{ - // 'open' pointers - int *p, *q; - - *p=1; - assert(*p==1); - - if(p==q) - assert(*q==1); - - i=100; - *q=2; - - if(p==q) - assert(*p==2); - - // aliasing with other stuff - if(q==&i) - assert(i==2); - else - assert(i==100); -} diff --git a/regression/one-version/pointer5/test.desc b/regression/one-version/pointer5/test.desc deleted file mode 100644 index 7282e4951..000000000 --- a/regression/one-version/pointer5/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 5 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/simple1/main.c b/regression/one-version/simple1/main.c deleted file mode 100644 index 44598aa83..000000000 --- a/regression/one-version/simple1/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - unsigned int x, y; - - x=y; - - x++; - y++; - - assert(x==y); -} diff --git a/regression/one-version/simple1/test.desc b/regression/one-version/simple1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/simple1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/simple2/main.c b/regression/one-version/simple2/main.c deleted file mode 100644 index 8d66a2ce8..000000000 --- a/regression/one-version/simple2/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int g; - -int main() -{ - int input; - - if(input) - g=1; - - assert(g!=1); -} diff --git a/regression/one-version/simple2/test.desc b/regression/one-version/simple2/test.desc deleted file mode 100644 index 6511ad6c7..000000000 --- a/regression/one-version/simple2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 1 of 1 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/struct1/main.c b/regression/one-version/struct1/main.c deleted file mode 100644 index 1c43f3853..000000000 --- a/regression/one-version/struct1/main.c +++ /dev/null @@ -1,27 +0,0 @@ -struct outer -{ - struct inner - { - int x, y; - } a; - - int b, c, d; -}; - -int main() -{ - struct outer my_s1; - struct inner my_s2; - - my_s1.b=1; - assert(my_s1.b==1); - - my_s1.b=my_s1.c; - assert(my_s1.b==my_s1.c); - - my_s2.x=10; - my_s1.a=my_s2; - assert(my_s1.a.x==10); - - assert(my_s1.a.y==my_s2.y); -} diff --git a/regression/one-version/struct1/test.desc b/regression/one-version/struct1/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct2/main.c b/regression/one-version/struct2/main.c deleted file mode 100644 index 75ec068d1..000000000 --- a/regression/one-version/struct2/main.c +++ /dev/null @@ -1,18 +0,0 @@ -struct S -{ - int a, b, c; - int array[4]; -}; - -int main() -{ - struct S s1, s2; - - // copy full struct - s1=s2; - - assert(s1.a==s2.a && s1.b==s2.b); - assert(s1.array[0]==s2.array[0]); - assert(s1.array[1]==s2.array[1]); - assert(s1==s2); -} diff --git a/regression/one-version/struct2/test.desc b/regression/one-version/struct2/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct3/main.c b/regression/one-version/struct3/main.c deleted file mode 100644 index 9a7b80f1d..000000000 --- a/regression/one-version/struct3/main.c +++ /dev/null @@ -1,18 +0,0 @@ -struct some_struct -{ - int n; -}; - -void func(struct some_struct *my_ptr) -{ - assert(my_ptr->n==42); -} - -int main() -{ - struct some_struct local_struct = { .n = 42 }; - - func(&local_struct); - - return 0; -} diff --git a/regression/one-version/struct3/test.desc b/regression/one-version/struct3/test.desc deleted file mode 100644 index a67495a20..000000000 --- a/regression/one-version/struct3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---inline -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct4/main.c b/regression/one-version/struct4/main.c deleted file mode 100644 index 4a7a6beea..000000000 --- a/regression/one-version/struct4/main.c +++ /dev/null @@ -1,37 +0,0 @@ -struct some_struct -{ - int a, b, c, d; - - struct nested - { - int e, f; - } sub; -}; - -int main() -{ - struct some_struct my_s, *p; - int *q; - char *char_p=0; - - // read through pointer - p=&my_s; - my_s.b=1; - __CPROVER_assert(p->b==1, "p->b"); - - // write through pointer - p->c=2; - __CPROVER_assert(my_s.c==2, "my_s.c"); - - // pointer into struct - q=&p->d; - my_s.d=3; - __CPROVER_assert(*q==3, "q 3"); - - // pointer into sub-struct - q=&p->sub.e; - my_s.sub.e=4; - __CPROVER_assert(*q==4, "q 4"); - - return 0; -} diff --git a/regression/one-version/struct4/test.desc b/regression/one-version/struct4/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/union1/main.c b/regression/one-version/union1/main.c deleted file mode 100644 index 3c034ae00..000000000 --- a/regression/one-version/union1/main.c +++ /dev/null @@ -1,34 +0,0 @@ -union my_U -{ - char array[sizeof(int)]; - - int i; - - struct S - { - char ch0, ch1, ch2, ch3; - } s; -}; - -int main() -{ - union my_U u; - - assert(u.array[0]==u.s.ch0); - assert(u.array[1]==u.s.ch1); - assert(u.array[2]==u.s.ch2); - assert(u.array[3]==u.s.ch3); - - u.i=0x04030201; - - if(u.s.ch0==0x01) - { - // little endian - assert(u.s.ch1==0x02 && u.s.ch2==0x03 && u.s.ch3==0x04); - } - else - { - // big endian - assert(u.s.ch1==0x03 && u.s.ch2==0x02 && u.s.ch3==0x01); - } -} diff --git a/regression/one-version/union1/test.desc b/regression/one-version/union1/test.desc deleted file mode 100644 index 72624a0c9..000000000 --- a/regression/one-version/union1/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 6 failed$ diff --git a/regression/preconditions/Makefile b/regression/preconditions/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/preconditions/Makefile +++ b/regression/preconditions/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/preconditions/precond_contextsensitive1/main.c b/regression/preconditions/precond_contextsensitive1/main.c index ccf80ffd1..ce8f207e2 100644 --- a/regression/preconditions/precond_contextsensitive1/main.c +++ b/regression/preconditions/precond_contextsensitive1/main.c @@ -1,15 +1,15 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x; - int y = sign(x); - assert(y==0); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x; + int y = sign(x); + assert(y==0); +} + diff --git a/regression/preconditions/precond_contextsensitive2/main.c b/regression/preconditions/precond_contextsensitive2/main.c index d1713f3e3..f8f2291c1 100644 --- a/regression/preconditions/precond_contextsensitive2/main.c +++ b/regression/preconditions/precond_contextsensitive2/main.c @@ -1,11 +1,11 @@ - -void foo(int x) -{ - assert(x!=1); -} - -int main(int argc, char** argv) -{ - foo(argc); -} - + +void foo(int x) +{ + assert(x!=1); +} + +int main(int argc, char** argv) +{ + foo(argc); +} + diff --git a/regression/preconditions/precond_contextsensitive2/test.desc b/regression/preconditions/precond_contextsensitive2/test.desc index 5bd0c35e6..f757b9ab1 100644 --- a/regression/preconditions/precond_contextsensitive2/test.desc +++ b/regression/preconditions/precond_contextsensitive2/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[main\]: argc <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc) <= -2$ +^\[_start\]: argc' <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc') <= -2$ diff --git a/regression/spurious-check-abstract/Makefile b/regression/spurious-check-abstract/Makefile new file mode 100644 index 000000000..3a92d5017 --- /dev/null +++ b/regression/spurious-check-abstract/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check abstract + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/spurious-check-abstract/cex-struct1/main.c b/regression/spurious-check-abstract/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/one-version/loop4/test.desc b/regression/spurious-check-abstract/cex-struct1/test.desc similarity index 73% rename from regression/one-version/loop4/test.desc rename to regression/spurious-check-abstract/cex-struct1/test.desc index 6511ad6c7..429367b44 100644 --- a/regression/one-version/loop4/test.desc +++ b/regression/spurious-check-abstract/cex-struct1/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=10$ ^SIGNAL=0$ -^** 1 of 1 failed$ ^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex-struct2/main.c b/regression/spurious-check-abstract/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/one-version/function1/test.desc b/regression/spurious-check-abstract/cex-struct2/test.desc similarity index 73% rename from regression/one-version/function1/test.desc rename to regression/spurious-check-abstract/cex-struct2/test.desc index 4a219b909..429367b44 100644 --- a/regression/one-version/function1/test.desc +++ b/regression/spurious-check-abstract/cex-struct2/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=10$ ^SIGNAL=0$ -^** 2 of 2 failed$ ^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex-struct3/main.c b/regression/spurious-check-abstract/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/one-version/goto2/test.desc b/regression/spurious-check-abstract/cex-struct3/test.desc similarity index 73% rename from regression/one-version/goto2/test.desc rename to regression/spurious-check-abstract/cex-struct3/test.desc index 6511ad6c7..429367b44 100644 --- a/regression/one-version/goto2/test.desc +++ b/regression/spurious-check-abstract/cex-struct3/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=10$ ^SIGNAL=0$ -^** 1 of 1 failed$ ^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex1/main.c b/regression/spurious-check-abstract/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-abstract/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/one-version/interval2/test.desc b/regression/spurious-check-abstract/cex1/test.desc similarity index 73% rename from regression/one-version/interval2/test.desc rename to regression/spurious-check-abstract/cex1/test.desc index 4a219b909..429367b44 100644 --- a/regression/one-version/interval2/test.desc +++ b/regression/spurious-check-abstract/cex1/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=10$ ^SIGNAL=0$ -^** 2 of 2 failed$ ^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex10/main.c b/regression/spurious-check-abstract/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-abstract/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-abstract/cex10/test.desc b/regression/spurious-check-abstract/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex13/main.c b/regression/spurious-check-abstract/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-abstract/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex13/test.desc b/regression/spurious-check-abstract/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex2/main.c b/regression/spurious-check-abstract/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/spurious-check-abstract/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex2/test.desc b/regression/spurious-check-abstract/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex3/main.c b/regression/spurious-check-abstract/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-abstract/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex3/test.desc b/regression/spurious-check-abstract/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex4/main.c b/regression/spurious-check-abstract/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-abstract/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex4/test.desc b/regression/spurious-check-abstract/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex5/main.c b/regression/spurious-check-abstract/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-abstract/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex5/test.desc b/regression/spurious-check-abstract/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex6/main.c b/regression/spurious-check-abstract/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-abstract/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex6/test.desc b/regression/spurious-check-abstract/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex7/main.c b/regression/spurious-check-abstract/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-abstract/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/spurious-check-abstract/cex7/test.desc b/regression/spurious-check-abstract/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/spurious-check-abstract/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex8/main.c b/regression/spurious-check-abstract/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-abstract/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-abstract/cex8/test.desc b/regression/spurious-check-abstract/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/cex9/main.c b/regression/spurious-check-abstract/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-abstract/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-abstract/cex9/test.desc b/regression/spurious-check-abstract/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-abstract/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-abstract/nocex1/main.c b/regression/spurious-check-abstract/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-abstract/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/one-version/array2/test.desc b/regression/spurious-check-abstract/nocex1/test.desc similarity index 74% rename from regression/one-version/array2/test.desc rename to regression/spurious-check-abstract/nocex1/test.desc index 3cffe7b65..dc053e6ab 100644 --- a/regression/one-version/array2/test.desc +++ b/regression/spurious-check-abstract/nocex1/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 5 failed$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex10/main.c b/regression/spurious-check-abstract/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-abstract/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-abstract/nocex10/test.desc b/regression/spurious-check-abstract/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-abstract/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-abstract/nocex2/main.c b/regression/spurious-check-abstract/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-abstract/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/one-version/assumption1/test.desc b/regression/spurious-check-abstract/nocex2/test.desc similarity index 74% rename from regression/one-version/assumption1/test.desc rename to regression/spurious-check-abstract/nocex2/test.desc index 880397d5d..dc053e6ab 100644 --- a/regression/one-version/assumption1/test.desc +++ b/regression/spurious-check-abstract/nocex2/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex3/main.c b/regression/spurious-check-abstract/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-abstract/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/one-version/goto1/test.desc b/regression/spurious-check-abstract/nocex3/test.desc similarity index 74% rename from regression/one-version/goto1/test.desc rename to regression/spurious-check-abstract/nocex3/test.desc index 880397d5d..dc053e6ab 100644 --- a/regression/one-version/goto1/test.desc +++ b/regression/spurious-check-abstract/nocex3/test.desc @@ -1,7 +1,6 @@ CORE main.c - +--havoc ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex4/main.c b/regression/spurious-check-abstract/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-abstract/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/spurious-check-abstract/nocex4/test.desc b/regression/spurious-check-abstract/nocex4/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-abstract/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex5/main.c b/regression/spurious-check-abstract/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-abstract/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/spurious-check-abstract/nocex5/test.desc b/regression/spurious-check-abstract/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-abstract/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex6/main.c b/regression/spurious-check-abstract/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-abstract/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/spurious-check-abstract/nocex6/test.desc b/regression/spurious-check-abstract/nocex6/test.desc new file mode 100644 index 000000000..1f240717a --- /dev/null +++ b/regression/spurious-check-abstract/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-abstract/nocex8/main.c b/regression/spurious-check-abstract/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-abstract/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/spurious-check-abstract/nocex8/test.desc b/regression/spurious-check-abstract/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-abstract/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/nocex9/main.c b/regression/spurious-check-abstract/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-abstract/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-abstract/nocex9/test.desc b/regression/spurious-check-abstract/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-abstract/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-complete/Makefile b/regression/spurious-check-complete/Makefile new file mode 100644 index 000000000..2a3b5cb15 --- /dev/null +++ b/regression/spurious-check-complete/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check complete + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/spurious-check-complete/cex-struct1/main.c b/regression/spurious-check-complete/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-complete/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct1/test.desc b/regression/spurious-check-complete/cex-struct1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex-struct2/main.c b/regression/spurious-check-complete/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct2/test.desc b/regression/spurious-check-complete/cex-struct2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex-struct3/main.c b/regression/spurious-check-complete/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-complete/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct3/test.desc b/regression/spurious-check-complete/cex-struct3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex1/main.c b/regression/spurious-check-complete/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-complete/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex1/test.desc b/regression/spurious-check-complete/cex1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex10/main.c b/regression/spurious-check-complete/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-complete/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex10/test.desc b/regression/spurious-check-complete/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex13/main.c b/regression/spurious-check-complete/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-complete/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex13/test.desc b/regression/spurious-check-complete/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex2/main.c b/regression/spurious-check-complete/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/spurious-check-complete/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex2/test.desc b/regression/spurious-check-complete/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex3/main.c b/regression/spurious-check-complete/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-complete/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex3/test.desc b/regression/spurious-check-complete/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex4/main.c b/regression/spurious-check-complete/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-complete/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex4/test.desc b/regression/spurious-check-complete/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex5/main.c b/regression/spurious-check-complete/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-complete/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex5/test.desc b/regression/spurious-check-complete/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex6/main.c b/regression/spurious-check-complete/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-complete/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/cex6/test.desc b/regression/spurious-check-complete/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex7/main.c b/regression/spurious-check-complete/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-complete/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/cex7/test.desc b/regression/spurious-check-complete/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/spurious-check-complete/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex8/main.c b/regression/spurious-check-complete/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-complete/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex8/test.desc b/regression/spurious-check-complete/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex9/main.c b/regression/spurious-check-complete/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-complete/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex9/test.desc b/regression/spurious-check-complete/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/nocex1/main.c b/regression/spurious-check-complete/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-complete/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/nocex1/test.desc b/regression/spurious-check-complete/nocex1/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex10/main.c b/regression/spurious-check-complete/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-complete/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-complete/nocex10/test.desc b/regression/spurious-check-complete/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-complete/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-complete/nocex2/main.c b/regression/spurious-check-complete/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-complete/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/nocex2/test.desc b/regression/spurious-check-complete/nocex2/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex3/main.c b/regression/spurious-check-complete/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-complete/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/spurious-check-complete/nocex3/test.desc b/regression/spurious-check-complete/nocex3/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex4/main.c b/regression/spurious-check-complete/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-complete/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/spurious-check-complete/nocex4/test.desc b/regression/spurious-check-complete/nocex4/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex5/main.c b/regression/spurious-check-complete/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-complete/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/spurious-check-complete/nocex5/test.desc b/regression/spurious-check-complete/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex6/main.c b/regression/spurious-check-complete/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-complete/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/nocex6/test.desc b/regression/spurious-check-complete/nocex6/test.desc new file mode 100644 index 000000000..1f240717a --- /dev/null +++ b/regression/spurious-check-complete/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-complete/nocex8/main.c b/regression/spurious-check-complete/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-complete/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/spurious-check-complete/nocex8/test.desc b/regression/spurious-check-complete/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex9/main.c b/regression/spurious-check-complete/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-complete/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-complete/nocex9/test.desc b/regression/spurious-check-complete/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-complete/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/Makefile b/regression/spurious-check-concrete/Makefile new file mode 100644 index 000000000..c26b769b2 --- /dev/null +++ b/regression/spurious-check-concrete/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check concrete + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/spurious-check-concrete/cex-struct1/main.c b/regression/spurious-check-concrete/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct1/test.desc b/regression/spurious-check-concrete/cex-struct1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex-struct2/main.c b/regression/spurious-check-concrete/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct2/test.desc b/regression/spurious-check-concrete/cex-struct2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex-struct3/main.c b/regression/spurious-check-concrete/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct3/test.desc b/regression/spurious-check-concrete/cex-struct3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex1/main.c b/regression/spurious-check-concrete/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-concrete/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex1/test.desc b/regression/spurious-check-concrete/cex1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex10/main.c b/regression/spurious-check-concrete/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-concrete/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex10/test.desc b/regression/spurious-check-concrete/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex13/main.c b/regression/spurious-check-concrete/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-concrete/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex13/test.desc b/regression/spurious-check-concrete/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex2/main.c b/regression/spurious-check-concrete/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/spurious-check-concrete/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex2/test.desc b/regression/spurious-check-concrete/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex3/main.c b/regression/spurious-check-concrete/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-concrete/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex3/test.desc b/regression/spurious-check-concrete/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex4/main.c b/regression/spurious-check-concrete/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-concrete/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex4/test.desc b/regression/spurious-check-concrete/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex5/main.c b/regression/spurious-check-concrete/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-concrete/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex5/test.desc b/regression/spurious-check-concrete/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex6/main.c b/regression/spurious-check-concrete/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-concrete/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex6/test.desc b/regression/spurious-check-concrete/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex7/main.c b/regression/spurious-check-concrete/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-concrete/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex7/test.desc b/regression/spurious-check-concrete/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/spurious-check-concrete/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex8/main.c b/regression/spurious-check-concrete/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-concrete/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex8/test.desc b/regression/spurious-check-concrete/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex9/main.c b/regression/spurious-check-concrete/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-concrete/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex9/test.desc b/regression/spurious-check-concrete/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/nocex1/main.c b/regression/spurious-check-concrete/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-concrete/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex1/test.desc b/regression/spurious-check-concrete/nocex1/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex10/main.c b/regression/spurious-check-concrete/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-concrete/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-concrete/nocex10/test.desc b/regression/spurious-check-concrete/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-concrete/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/nocex2/main.c b/regression/spurious-check-concrete/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-concrete/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex2/test.desc b/regression/spurious-check-concrete/nocex2/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex3/main.c b/regression/spurious-check-concrete/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-concrete/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex3/test.desc b/regression/spurious-check-concrete/nocex3/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex4/main.c b/regression/spurious-check-concrete/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-concrete/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex4/test.desc b/regression/spurious-check-concrete/nocex4/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex5/main.c b/regression/spurious-check-concrete/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-concrete/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex5/test.desc b/regression/spurious-check-concrete/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex6/main.c b/regression/spurious-check-concrete/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-concrete/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex6/test.desc b/regression/spurious-check-concrete/nocex6/test.desc new file mode 100644 index 000000000..1f240717a --- /dev/null +++ b/regression/spurious-check-concrete/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/nocex8/main.c b/regression/spurious-check-concrete/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-concrete/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex8/test.desc b/regression/spurious-check-concrete/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex9/main.c b/regression/spurious-check-concrete/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-concrete/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-concrete/nocex9/test.desc b/regression/spurious-check-concrete/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-concrete/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/termination/Makefile b/regression/termination/Makefile index 47bfd1905..f92c8b2be 100644 --- a/regression/termination/Makefile +++ b/regression/termination/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --termination test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/termination/bubble_sort2/main.c b/regression/termination/bubble_sort2/main.c new file mode 100644 index 000000000..193cf226c --- /dev/null +++ b/regression/termination/bubble_sort2/main.c @@ -0,0 +1,20 @@ +void main() +{ + int n; + __CPROVER_assume(2<=n && n<=100); + int array[n]; + int c, d, swap; + + for (c = 0 ; c < ( n - 1 ); c++) + { + for (d = 0 ; d < n - c - 1; d++) + { + if (array[d] > array[d+1]) + { + swap = array[d]; + array[d] = array[d+1]; + array[d+1] = swap; + } + } + } +} diff --git a/regression/one-version/context3/test.desc b/regression/termination/bubble_sort2/test.desc similarity index 75% rename from regression/one-version/context3/test.desc rename to regression/termination/bubble_sort2/test.desc index 28d612c3f..9ebb38e34 100644 --- a/regression/one-version/context3/test.desc +++ b/regression/termination/bubble_sort2/test.desc @@ -3,5 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 3 failed$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/termination/contextsensitive1/main.c b/regression/termination/contextsensitive1/main.c index c1b928562..743ca83a6 100644 --- a/regression/termination/contextsensitive1/main.c +++ b/regression/termination/contextsensitive1/main.c @@ -1,17 +1,17 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive2/main.c b/regression/termination/contextsensitive2/main.c index b228a31b4..4547b1d62 100644 --- a/regression/termination/contextsensitive2/main.c +++ b/regression/termination/contextsensitive2/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive3/main.c b/regression/termination/contextsensitive3/main.c index deab63659..e6ff9f250 100644 --- a/regression/termination/contextsensitive3/main.c +++ b/regression/termination/contextsensitive3/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive4/main.c b/regression/termination/contextsensitive4/main.c index 9f1348396..3a9f9d42d 100644 --- a/regression/termination/contextsensitive4/main.c +++ b/regression/termination/contextsensitive4/main.c @@ -1,29 +1,29 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive5/main.c b/regression/termination/contextsensitive5/main.c index fb0251140..25f460e52 100644 --- a/regression/termination/contextsensitive5/main.c +++ b/regression/termination/contextsensitive5/main.c @@ -1,15 +1,15 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} + diff --git a/regression/termination/contextsensitive6/main.c b/regression/termination/contextsensitive6/main.c index be4631faa..2030a7355 100644 --- a/regression/termination/contextsensitive6/main.c +++ b/regression/termination/contextsensitive6/main.c @@ -1,12 +1,12 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - + +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} + diff --git a/regression/termination/float4/test.desc b/regression/termination/float4/test.desc index 8314a397f..c2dc66423 100644 --- a/regression/termination/float4/test.desc +++ b/regression/termination/float4/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c ^EXIT=5$ diff --git a/regression/termination/global1/main.c b/regression/termination/global1/main.c index ecb897b62..01bd988fa 100644 --- a/regression/termination/global1/main.c +++ b/regression/termination/global1/main.c @@ -1,21 +1,21 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} + diff --git a/regression/termination/global2/main.c b/regression/termination/global2/main.c index f653bce18..d2ae47492 100644 --- a/regression/termination/global2/main.c +++ b/regression/termination/global2/main.c @@ -1,22 +1,22 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} + diff --git a/regression/termination/ite1/main.c b/regression/termination/ite1/main.c index b4829d864..a0844f924 100644 --- a/regression/termination/ite1/main.c +++ b/regression/termination/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/termination/pointer5/test.desc b/regression/termination/pointer5/test.desc index 7f13074e3..8b39a0a04 100644 --- a/regression/termination/pointer5/test.desc +++ b/regression/termination/pointer5/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --equalities ^EXIT=0$ diff --git a/regression/termination/precond_term4/test.desc b/regression/termination/precond_term4/test.desc index 4589ad0c6..425ba9bf1 100644 --- a/regression/termination/precond_term4/test.desc +++ b/regression/termination/precond_term4/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --preconditions --context-sensitive ^EXIT=5$ diff --git a/regression/termination/running3/test.desc b/regression/termination/running3/test.desc index bba925826..cfa859601 100644 --- a/regression/termination/running3/test.desc +++ b/regression/termination/running3/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --context-sensitive --preconditions ^EXIT=5$ diff --git a/regression/termination/sum1/main.c b/regression/termination/sum1/main.c index df354feeb..b28d8b049 100644 --- a/regression/termination/sum1/main.c +++ b/regression/termination/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/regression/two-versions/Makefile b/regression/two-versions/Makefile deleted file mode 100644 index bbbf50d3a..000000000 --- a/regression/two-versions/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -default: tests.log - -test: - @../test.pl -c ../../../src/deltacheck/deltacheck - -tests.log: ../test.pl - @../test.pl -c ../../../src/deltacheck/deltacheck - -show: - @for dir in *; do \ - if [ -d "$$dir" ]; then \ - vim -o "$$dir/*.c" "$$dir/*.out"; \ - fi; \ - done; diff --git a/regression/two-versions/ofer-example1/new.c b/regression/two-versions/ofer-example1/new.c deleted file mode 100644 index 8a49e92dc..000000000 --- a/regression/two-versions/ofer-example1/new.c +++ /dev/null @@ -1,18 +0,0 @@ -_Bool some_function(); - -void f(int x, int y) -{ - while(some_function()) - { - x--; - y--; - } - - assert(x==y); -} - -void main() -{ - int x,y; - f(x, y); -} diff --git a/regression/two-versions/ofer-example1/new.o b/regression/two-versions/ofer-example1/new.o deleted file mode 100644 index ae6401fb7..000000000 Binary files a/regression/two-versions/ofer-example1/new.o and /dev/null differ diff --git a/regression/two-versions/ofer-example1/old.c b/regression/two-versions/ofer-example1/old.c deleted file mode 100644 index ec76ef8c0..000000000 --- a/regression/two-versions/ofer-example1/old.c +++ /dev/null @@ -1,18 +0,0 @@ -_Bool some_function(); - -void f(int x, int y) -{ - while(some_function()) - { - x++; - y++; - } - - assert(x==y); -} - -void main() -{ - int x,y; - f(x, y); -} diff --git a/regression/two-versions/ofer-example1/old.o b/regression/two-versions/ofer-example1/old.o deleted file mode 100644 index c2d19b0ba..000000000 Binary files a/regression/two-versions/ofer-example1/old.o and /dev/null differ diff --git a/regression/two-versions/ofer-example1/test.desc b/regression/two-versions/ofer-example1/test.desc deleted file mode 100644 index 27a3f37cc..000000000 --- a/regression/two-versions/ofer-example1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 1$ -^Properties failed: 0$ diff --git a/regression/two-versions/ofer-example2/new.c b/regression/two-versions/ofer-example2/new.c deleted file mode 100644 index 257d4ed68..000000000 --- a/regression/two-versions/ofer-example2/new.c +++ /dev/null @@ -1,11 +0,0 @@ -int g(); - -void f(unsigned l, int x, int y) { - while (l) { - l--; - x++; - y--; // note the --. It should fail it. - } - - assert(x == y); // this should fail. -} diff --git a/regression/two-versions/ofer-example2/new.o b/regression/two-versions/ofer-example2/new.o deleted file mode 100644 index 98317978c..000000000 Binary files a/regression/two-versions/ofer-example2/new.o and /dev/null differ diff --git a/regression/two-versions/ofer-example2/old.c b/regression/two-versions/ofer-example2/old.c deleted file mode 100644 index a379b1e23..000000000 --- a/regression/two-versions/ofer-example2/old.c +++ /dev/null @@ -1,14 +0,0 @@ -int z; - -void f(unsigned l, int x, int y) -{ - z++; - while (l) { - l--; - x++; - y++; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example2/old.o b/regression/two-versions/ofer-example2/old.o deleted file mode 100644 index 468b00b49..000000000 Binary files a/regression/two-versions/ofer-example2/old.o and /dev/null differ diff --git a/regression/two-versions/ofer-example2/test.desc b/regression/two-versions/ofer-example2/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/ofer-example2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/ofer-example3/new.c b/regression/two-versions/ofer-example3/new.c deleted file mode 100644 index 92fcf17ee..000000000 --- a/regression/two-versions/ofer-example3/new.c +++ /dev/null @@ -1,10 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; // note the --. It should fail it. - } - - assert(x == y); // this should fail. -} diff --git a/regression/two-versions/ofer-example3/new.o b/regression/two-versions/ofer-example3/new.o deleted file mode 100644 index f36e64253..000000000 Binary files a/regression/two-versions/ofer-example3/new.o and /dev/null differ diff --git a/regression/two-versions/ofer-example3/old.c b/regression/two-versions/ofer-example3/old.c deleted file mode 100644 index 005e7b70d..000000000 --- a/regression/two-versions/ofer-example3/old.c +++ /dev/null @@ -1,11 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y++; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example3/old.o b/regression/two-versions/ofer-example3/old.o deleted file mode 100644 index ffa8fda70..000000000 Binary files a/regression/two-versions/ofer-example3/old.o and /dev/null differ diff --git a/regression/two-versions/ofer-example3/test.desc b/regression/two-versions/ofer-example3/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/ofer-example3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/ofer-example4/new.c b/regression/two-versions/ofer-example4/new.c deleted file mode 100644 index 8694ef3e6..000000000 --- a/regression/two-versions/ofer-example4/new.c +++ /dev/null @@ -1,10 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; - } - - assert(x == y); // this should pass. -} diff --git a/regression/two-versions/ofer-example4/new.o b/regression/two-versions/ofer-example4/new.o deleted file mode 100644 index f36e64253..000000000 Binary files a/regression/two-versions/ofer-example4/new.o and /dev/null differ diff --git a/regression/two-versions/ofer-example4/old.c b/regression/two-versions/ofer-example4/old.c deleted file mode 100644 index 53027a24f..000000000 --- a/regression/two-versions/ofer-example4/old.c +++ /dev/null @@ -1,11 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example4/old.o b/regression/two-versions/ofer-example4/old.o deleted file mode 100644 index 01c8f2070..000000000 Binary files a/regression/two-versions/ofer-example4/old.o and /dev/null differ diff --git a/regression/two-versions/ofer-example4/test.desc b/regression/two-versions/ofer-example4/test.desc deleted file mode 100644 index 27a3f37cc..000000000 --- a/regression/two-versions/ofer-example4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 1$ -^Properties failed: 0$ diff --git a/regression/two-versions/ofer-example5/new.c b/regression/two-versions/ofer-example5/new.c deleted file mode 100644 index 831e18f7a..000000000 --- a/regression/two-versions/ofer-example5/new.c +++ /dev/null @@ -1,11 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - int x = 0; - while (x == 0) { - x = 1; - } - assert(parameter==1); - assert(glob==2); -} diff --git a/regression/two-versions/ofer-example5/new.o b/regression/two-versions/ofer-example5/new.o deleted file mode 100644 index 1023eb155..000000000 Binary files a/regression/two-versions/ofer-example5/new.o and /dev/null differ diff --git a/regression/two-versions/ofer-example5/old.c b/regression/two-versions/ofer-example5/old.c deleted file mode 100644 index db9246507..000000000 --- a/regression/two-versions/ofer-example5/old.c +++ /dev/null @@ -1,7 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - assert(parameter==1); - assert(glob==2); -} diff --git a/regression/two-versions/ofer-example5/old.o b/regression/two-versions/ofer-example5/old.o deleted file mode 100644 index f5f5632c5..000000000 Binary files a/regression/two-versions/ofer-example5/old.o and /dev/null differ diff --git a/regression/two-versions/ofer-example5/test.desc b/regression/two-versions/ofer-example5/test.desc deleted file mode 100644 index c17a01622..000000000 --- a/regression/two-versions/ofer-example5/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/pointer1/new.c b/regression/two-versions/pointer1/new.c deleted file mode 100644 index 3d4684abb..000000000 --- a/regression/two-versions/pointer1/new.c +++ /dev/null @@ -1,18 +0,0 @@ -void my_f(int *parameter) -{ - parameter[10]=1; -} - -struct S -{ - struct S *n; - int payload; -}; - -int i; - -void my_g(struct S *p) -{ - i++; - p->n->n->payload=1; -} diff --git a/regression/two-versions/pointer1/new.o b/regression/two-versions/pointer1/new.o deleted file mode 100644 index 8b75edbff..000000000 Binary files a/regression/two-versions/pointer1/new.o and /dev/null differ diff --git a/regression/two-versions/pointer1/old.c b/regression/two-versions/pointer1/old.c deleted file mode 100644 index 6a6f09d46..000000000 --- a/regression/two-versions/pointer1/old.c +++ /dev/null @@ -1,15 +0,0 @@ -void my_f(int *parameter) -{ - parameter[10]=1; -} - -struct S -{ - struct S *n; - int payload; -}; - -void my_g(struct S *p) -{ - p->n->n->payload=1; -} diff --git a/regression/two-versions/pointer1/old.o b/regression/two-versions/pointer1/old.o deleted file mode 100644 index 55298d542..000000000 Binary files a/regression/two-versions/pointer1/old.o and /dev/null differ diff --git a/regression/two-versions/pointer1/test.desc b/regression/two-versions/pointer1/test.desc deleted file mode 100644 index 5413dfe1a..000000000 --- a/regression/two-versions/pointer1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o --pointer-check --bounds-check -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 18$ -^Properties failed: 0$ diff --git a/regression/two-versions/simple1/new.c b/regression/two-versions/simple1/new.c deleted file mode 100644 index 488416073..000000000 --- a/regression/two-versions/simple1/new.c +++ /dev/null @@ -1,10 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - if(parameter>=0) - { - assert(parameter==1); - assert(glob==2); - } -} diff --git a/regression/two-versions/simple1/new.o b/regression/two-versions/simple1/new.o deleted file mode 100644 index db3fcd32b..000000000 Binary files a/regression/two-versions/simple1/new.o and /dev/null differ diff --git a/regression/two-versions/simple1/old.c b/regression/two-versions/simple1/old.c deleted file mode 100644 index 7e2a97e05..000000000 --- a/regression/two-versions/simple1/old.c +++ /dev/null @@ -1,7 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - assert(parameter==1); - assert(glob==2); -} diff --git a/regression/two-versions/simple1/old.o b/regression/two-versions/simple1/old.o deleted file mode 100644 index f5f5632c5..000000000 Binary files a/regression/two-versions/simple1/old.o and /dev/null differ diff --git a/regression/two-versions/simple1/test.desc b/regression/two-versions/simple1/test.desc deleted file mode 100644 index c17a01622..000000000 --- a/regression/two-versions/simple1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/simple2/new.c b/regression/two-versions/simple2/new.c deleted file mode 100644 index 52748f287..000000000 --- a/regression/two-versions/simple2/new.c +++ /dev/null @@ -1,6 +0,0 @@ -void my_f(int parameter) -{ - // should fail, stronger! - assert(parameter==100); -} - diff --git a/regression/two-versions/simple2/new.o b/regression/two-versions/simple2/new.o deleted file mode 100644 index 4616339a0..000000000 Binary files a/regression/two-versions/simple2/new.o and /dev/null differ diff --git a/regression/two-versions/simple2/old.c b/regression/two-versions/simple2/old.c deleted file mode 100644 index 11f74f395..000000000 --- a/regression/two-versions/simple2/old.c +++ /dev/null @@ -1,8 +0,0 @@ -void my_f(int parameter) -{ - int bound; - - if(bound<10) - assert(parameter==100); -} - diff --git a/regression/two-versions/simple2/old.o b/regression/two-versions/simple2/old.o deleted file mode 100644 index beb1b8a48..000000000 Binary files a/regression/two-versions/simple2/old.o and /dev/null differ diff --git a/regression/two-versions/simple2/test.desc b/regression/two-versions/simple2/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/simple2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/slam-example1/new.c b/regression/two-versions/slam-example1/new.c deleted file mode 100644 index 9c114dff4..000000000 --- a/regression/two-versions/slam-example1/new.c +++ /dev/null @@ -1,23 +0,0 @@ -int nondet_int(); - -int main() -{ - int Old = 0, New = 0; - char lock; - - do - { - lock = 1; - Old = New; - - if(nondet_int()) - { - lock = 0; - New++; - } - } - while (New != Old); - - assert(lock == 1); -} - diff --git a/regression/two-versions/slam-example1/old.c b/regression/two-versions/slam-example1/old.c deleted file mode 100644 index 9c114dff4..000000000 --- a/regression/two-versions/slam-example1/old.c +++ /dev/null @@ -1,23 +0,0 @@ -int nondet_int(); - -int main() -{ - int Old = 0, New = 0; - char lock; - - do - { - lock = 1; - Old = New; - - if(nondet_int()) - { - lock = 0; - New++; - } - } - while (New != Old); - - assert(lock == 1); -} - diff --git a/regression/two-versions/slam-example1/test.desc b/regression/two-versions/slam-example1/test.desc deleted file mode 100644 index 362e2770c..000000000 --- a/regression/two-versions/slam-example1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/struct1/new.c b/regression/two-versions/struct1/new.c deleted file mode 100644 index d40416971..000000000 --- a/regression/two-versions/struct1/new.c +++ /dev/null @@ -1,9 +0,0 @@ -struct my_S_new -{ - int a, b, c; -} x; - -void f() -{ - assert(x.a==x.b); -} diff --git a/regression/two-versions/struct1/new.o b/regression/two-versions/struct1/new.o deleted file mode 100644 index 6f058eaa7..000000000 Binary files a/regression/two-versions/struct1/new.o and /dev/null differ diff --git a/regression/two-versions/struct1/old.c b/regression/two-versions/struct1/old.c deleted file mode 100644 index 4082080b8..000000000 --- a/regression/two-versions/struct1/old.c +++ /dev/null @@ -1,9 +0,0 @@ -struct my_S -{ - int a, b, c; -} x; - -void f() -{ - assert(x.a==x.b); -} diff --git a/regression/two-versions/struct1/old.o b/regression/two-versions/struct1/old.o deleted file mode 100644 index 79b0d5f5f..000000000 Binary files a/regression/two-versions/struct1/old.o and /dev/null differ diff --git a/regression/two-versions/struct1/test.desc b/regression/two-versions/struct1/test.desc deleted file mode 100644 index e0de823f7..000000000 --- a/regression/two-versions/struct1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 1$ -^Properties failed: 0$ diff --git a/scripts/cpplint.py b/scripts/cpplint.py new file mode 100644 index 000000000..57d9025d7 --- /dev/null +++ b/scripts/cpplint.py @@ -0,0 +1,6619 @@ +#!/usr/bin/env python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] [--root=subdir] + [--linelength=digits] [--headers=x,y,...] + [file] ... + + The style guidelines this tries to follow are those in + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the + extensions with the --extensions flag. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuming that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ + + linelength=digits + This is the allowed line length for the project. The default value is + 80 characters. + + Examples: + --linelength=120 + + extensions=extension,extension,... + The allowed file extensions that cpplint will check + + Examples: + --extensions=hpp,cpp + + headers=x,y,... + The header extensions that cpplint will treat as .h in checks. Values are + automatically added to --extensions list. + + Examples: + --headers=hpp,hxx + --headers=hpp + + cpplint.py supports per-directory configurations specified in CPPLINT.cfg + files. CPPLINT.cfg file can contain a number of key=value pairs. + Currently the following options are supported: + + set noparent + filter=+filter1,-filter2,... + exclude_files=regex + linelength=80 + root=subdir + headers=x,y,... + + "set noparent" option prevents cpplint from traversing directory tree + upwards looking for more .cfg files in parent directories. This option + is usually placed in the top-level project directory. + + The "filter" option is similar in function to --filter flag. It specifies + message filters in addition to the |_DEFAULT_FILTERS| and those specified + through --filter command-line flag. + + "exclude_files" allows to specify a regular expression to be matched against + a file name. If the expression matches, the file is skipped and not run + through liner. + + "linelength" allows to specify the allowed line length for the project. + + The "root" option is similar in function to the --root flag (see example + above). + + The "headers" option is similar in function to the --headers flag + (see example above). + + CPPLINT.cfg has an effect on files in the same directory and all + sub-directories, unless overridden by a nested configuration file. + + Example file: + filter=-build/include_order,+build/include_alpha + exclude_files=.*\.cc + + The above example disables build/include_order warning and enables + build/include_alpha as well as excludes all .cc from being + processed by linter, in the current directory (where the .cfg + file is located) and all sub-directories. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +_ERROR_CATEGORIES = [ + 'build/class', + 'build/c++11', + 'build/c++14', + 'build/c++tr1', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/inheritance', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/identifiers', + 'readability/nolint', + 'readability/nul', + 'readability/strings', + 'readability/todo', + 'readability/throw', + 'readability/utf8', + 'readability/function_comment' + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/indentation_namespace', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/vlog', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_conditional_body', + 'whitespace/empty_if_body', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + ] + +# These error categories are no longer enforced by cpplint, but for backwards- +# compatibility they may still appear in NOLINT comments. +_LEGACY_ERROR_CATEGORIES = [ + 'readability/streams', + 'readability/function', + ] + +# The default state of the category filter. This is overridden by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# The default list of categories suppressed for C (not C++) files. +_DEFAULT_C_SUPPRESSED_CATEGORIES = [ + 'readability/casting', + ] + +# The default list of categories suppressed for Linux Kernel files. +_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ + 'whitespace/tab', + ] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# C++ headers +_CPP_HEADERS = frozenset([ + # Legacy + 'algobase.h', + 'algo.h', + 'alloc.h', + 'builtinbuf.h', + 'bvector.h', + 'complex.h', + 'defalloc.h', + 'deque.h', + 'editbuf.h', + 'fstream.h', + 'function.h', + 'hash_map', + 'hash_map.h', + 'hash_set', + 'hash_set.h', + 'hashtable.h', + 'heap.h', + 'indstream.h', + 'iomanip.h', + 'iostream.h', + 'istream.h', + 'iterator.h', + 'list.h', + 'map.h', + 'multimap.h', + 'multiset.h', + 'ostream.h', + 'pair.h', + 'parsestream.h', + 'pfstream.h', + 'procbuf.h', + 'pthread_alloc', + 'pthread_alloc.h', + 'rope', + 'rope.h', + 'ropeimpl.h', + 'set.h', + 'slist', + 'slist.h', + 'stack.h', + 'stdiostream.h', + 'stl_alloc.h', + 'stl_relops.h', + 'streambuf.h', + 'stream.h', + 'strfile.h', + 'strstream.h', + 'tempbuf.h', + 'tree.h', + 'type_traits.h', + 'vector.h', + # 17.6.1.2 C++ library headers + 'algorithm', + 'array', + 'atomic', + 'bitset', + 'chrono', + 'codecvt', + 'complex', + 'condition_variable', + 'deque', + 'exception', + 'forward_list', + 'fstream', + 'functional', + 'future', + 'initializer_list', + 'iomanip', + 'ios', + 'iosfwd', + 'iostream', + 'istream', + 'iterator', + 'limits', + 'list', + 'locale', + 'map', + 'memory', + 'mutex', + 'new', + 'numeric', + 'ostream', + 'queue', + 'random', + 'ratio', + 'regex', + 'scoped_allocator', + 'set', + 'sstream', + 'stack', + 'stdexcept', + 'streambuf', + 'string', + 'strstream', + 'system_error', + 'thread', + 'tuple', + 'typeindex', + 'typeinfo', + 'type_traits', + 'unordered_map', + 'unordered_set', + 'utility', + 'valarray', + 'vector', + # 17.6.1.2 C++ headers for C library facilities + 'cassert', + 'ccomplex', + 'cctype', + 'cerrno', + 'cfenv', + 'cfloat', + 'cinttypes', + 'ciso646', + 'climits', + 'clocale', + 'cmath', + 'csetjmp', + 'csignal', + 'cstdalign', + 'cstdarg', + 'cstdbool', + 'cstddef', + 'cstdint', + 'cstdio', + 'cstdlib', + 'cstring', + 'ctgmath', + 'ctime', + 'cuchar', + 'cwchar', + 'cwctype', + ]) + +# Type names +_TYPES = re.compile( + r'^(?:' + # [dcl.type.simple] + r'(char(16_t|32_t)?)|wchar_t|' + r'bool|short|int|long|signed|unsigned|float|double|' + # [support.types] + r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' + # [cstdint.syn] + r'(u?int(_fast|_least)?(8|16|32|64)_t)|' + r'(u?int(max|ptr)_t)|' + r')$') + + +# These headers are excluded from [build/include] and [build/include_order] +# checks: +# - Anything not following google file name conventions (containing an +# uppercase character, such as Python.h or nsStringAPI.h, for example). +# - Lua headers. +_THIRD_PARTY_HEADERS_PATTERN = re.compile( + r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') + +# Pattern for matching FileInfo.BaseName() against test file name +_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' + +# Pattern that matches only complete whitespace, possibly across multiple lines. +_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) + +# Assertion macros. These are defined in base/logging.h and +# testing/base/public/gunit.h. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE', 'ASSERT_TRUE', + 'EXPECT_FALSE', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments and multi-line strings +# but those have always been troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + +# Match strings that indicate we're working on a C (not C++) file. +_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' + r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') + +# Match string that indicates we're working on a Linux Kernel file. +_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') + +_regexp_compile_cache = {} + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +# The allowed line length of files. +# This is set by --linelength flag. +_line_length = 80 + +# The allowed extensions for file names +# This is set by --extensions flag. +_valid_extensions = set(['cc', 'h', 'cpp', 'hh']) + +# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc. +# This is set by --headers flag. +_hpp_headers = set(['h']) + +# {str, bool}: a map from error categories to booleans which indicate if the +# category should be suppressed for every line. +_global_error_suppressions = {} + +def ProcessHppHeadersOption(val): + global _hpp_headers + try: + _hpp_headers = set(val.split(',')) + # Automatically append to extensions list so it does not have to be set 2 times + _valid_extensions.update(_hpp_headers) + except ValueError: + PrintUsage('Header extensions must be comma seperated list.') + +def IsHeaderExtension(file_extension): + return file_extension in _hpp_headers + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of line error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) + if matched: + if matched.group(1): + suppressed_line = linenum + 1 + else: + suppressed_line = linenum + category = matched.group(2) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(suppressed_line) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(suppressed_line) + elif category not in _LEGACY_ERROR_CATEGORIES: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ProcessGlobalSuppresions(lines): + """Updates the list of global error suppressions. + + Parses any lint directives in the file that have global effect. + + Args: + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + """ + for line in lines: + if _SEARCH_C_FILE.search(line): + for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True + if _SEARCH_KERNEL_FILE.search(line): + for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True + + +def ResetNolintSuppressions(): + """Resets the set of NOLINT suppressions to empty.""" + _error_suppressions.clear() + _global_error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment or + global suppression. + """ + return (_global_error_suppressions.get(category, False) or + linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def ReplaceAll(pattern, rep, s): + """Replaces instances of pattern in a string with a replacement. + + The compiled regex is kept in a cache shared by Match and Search. + + Args: + pattern: regex pattern + rep: replacement text + s: search string + + Returns: + string with replacements made (or original string if no replacements) + """ + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].sub(rep, s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +def _IsSourceExtension(s): + """File extension (excluding dot) matches a source file extension.""" + return s in ('c', 'cc', 'cpp') + + +class _IncludeState(object): + """Tracks line numbers for includes, and the order in which includes appear. + + include_list contains list of lists of (header, line number) pairs. + It's a lists of lists rather than just one flat list to make it + easier to update across preprocessor boundaries. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + self.include_list = [[]] + self.ResetSection('') + + def FindHeader(self, header): + """Check if a header has already been included. + + Args: + header: header to check. + Returns: + Line number of previous occurrence, or -1 if the header has not + been seen before. + """ + for section_list in self.include_list: + for f in section_list: + if f[0] == header: + return f[1] + return -1 + + def ResetSection(self, directive): + """Reset section checking for preprocessor directive. + + Args: + directive: preprocessor directive (e.g. "if", "else"). + """ + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + # Update list of includes. Note that we never pop from the + # include list. + if directive in ('if', 'ifdef', 'ifndef'): + self.include_list.append([]) + elif directive in ('else', 'elif'): + self.include_list[-1] = [] + + def SetLastHeader(self, header_path): + self._last_header = header_path + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + header_path: Canonicalized header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + # If previous section is different from current section, _last_header will + # be reset to empty string, so it's always less than current header. + # + # If previous line was a blank line, assume that the headers are + # intentionally sorted the way they are. + if (self._last_header > header_path and + Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): + return False + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + # backup of filter list. Used to restore the state after each file. + self._filters_backup = self.filters[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + self.AddFilters(filters) + + def AddFilters(self, filters): + """ Adds more filters to the existing list of error-message filters. """ + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def BackupFilters(self): + """ Saves the current filter list to backup storage.""" + self._filters_backup = self.filters[:] + + def RestoreFilters(self): + """ Restores filters previously backed up.""" + self.filters = self._filters_backup[:] + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stdout.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + +def _AddFilters(filters): + """Adds more filter overrides. + + Unlike _SetFilters, this function does not reset the current list of filters + available. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.AddFilters(filters) + +def _BackupFilters(): + """ Saves the current filter list to backup storage.""" + _cpplint_state.BackupFilters() + +def _RestoreFilters(): + """ Restores filters previously backed up.""" + _cpplint_state.RestoreFilters() + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if not self.in_a_function: + return + + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo(object): + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = current_dir = os.path.dirname(fullname) + while current_dir != os.path.dirname(current_dir): + if (os.path.exists(os.path.join(current_dir, ".git")) or + os.path.exists(os.path.join(current_dir, ".hg")) or + os.path.exists(os.path.join(current_dir, ".svn"))): + root_dir = current_dir + current_dir = os.path.dirname(current_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return _IsSourceExtension(self.Extension()[1:]) + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Match a single C style comment on the same line. +_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' +# Matches multi-line C style comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + + _RE_PATTERN_C_COMMENTS + r'\s+|' + + r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + + _RE_PATTERN_C_COMMENTS + r')') + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def CleanseRawStrings(raw_lines): + """Removes C++11 raw strings from lines. + + Before: + static const char kData[] = R"( + multi-line string + )"; + + After: + static const char kData[] = "" + (replaced by blank line) + ""; + + Args: + raw_lines: list of raw lines. + + Returns: + list of lines with C++11 raw strings replaced by empty strings. + """ + + delimiter = None + lines_without_raw_strings = [] + for line in raw_lines: + if delimiter: + # Inside a raw string, look for the end + end = line.find(delimiter) + if end >= 0: + # Found the end of the string, match leading space for this + # line and resume copying the original lines, and also insert + # a "" on the last line. + leading_space = Match(r'^(\s*)\S', line) + line = leading_space.group(1) + '""' + line[end + len(delimiter):] + delimiter = None + else: + # Haven't found the end yet, append a blank line. + line = '""' + + # Look for beginning of a raw string, and replace them with + # empty strings. This is done in a loop to handle multiple raw + # strings on the same line. + while delimiter is None: + # Look for beginning of a raw string. + # See 2.14.15 [lex.string] for syntax. + # + # Once we have matched a raw string, we check the prefix of the + # line to make sure that the line is not part of a single line + # comment. It's done this way because we remove raw strings + # before removing comments as opposed to removing comments + # before removing raw strings. This is because there are some + # cpplint checks that requires the comments to be preserved, but + # we don't want to check comments that are inside raw strings. + matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) + if (matched and + not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', + matched.group(1))): + delimiter = ')' + matched.group(2) + '"' + + end = matched.group(3).find(delimiter) + if end >= 0: + # Raw string ended on same line + line = (matched.group(1) + '""' + + matched.group(3)[end + len(delimiter):]) + delimiter = None + else: + # Start of a multi-line raw string + line = matched.group(1) + '""' + else: + break + + lines_without_raw_strings.append(line) + + # TODO(unknown): if delimiter is not None here, we might want to + # emit a warning for unterminated string. + return lines_without_raw_strings + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '/**/' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 4 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments. + 2) lines member contains lines without comments. + 3) raw_lines member contains all the lines without processing. + 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw + strings removed. + All these members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + self.lines_without_raw_strings = CleanseRawStrings(lines) + for linenum in range(len(self.lines_without_raw_strings)): + self.lines.append(CleanseComments( + self.lines_without_raw_strings[linenum])) + elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if _RE_PATTERN_INCLUDE.match(elided): + return elided + + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + + # Replace quoted strings and digit separators. Both single quotes + # and double quotes are processed in the same loop, otherwise + # nested quotes wouldn't work. + collapsed = '' + while True: + # Find the first quote character + match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) + if not match: + collapsed += elided + break + head, quote, tail = match.groups() + + if quote == '"': + # Collapse double quoted strings + second_quote = tail.find('"') + if second_quote >= 0: + collapsed += head + '""' + elided = tail[second_quote + 1:] + else: + # Unmatched double quote, don't bother processing the rest + # of the line since this is probably a multiline string. + collapsed += elided + break + else: + # Found single quote, check nearby text to eliminate digit separators. + # + # There is no special handling for floating point here, because + # the integer/fractional/exponent parts would all be parsed + # correctly as long as there are digits on both sides of the + # separator. So we are fine as long as we don't see something + # like "0.'3" (gcc 4.9.0 will not allow this literal). + if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): + match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) + collapsed += head + match_literal.group(1).replace("'", '') + elided = match_literal.group(2) + else: + second_quote = tail.find('\'') + if second_quote >= 0: + collapsed += head + "''" + elided = tail[second_quote + 1:] + else: + # Unmatched single quote + collapsed += elided + break + + return collapsed + +def IsTemplateArgumentList_DB(clean_lines, linenum, pos): + """if lines[linenum][pos] is >, finds out if it is closing bracket + of a template argument. + + Finds a matching < (if possible) to the > at position line[linenum][pos] + and then analyzes the string between the two to work out if it could + in fact be a template argument list or just some general code. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + True if the angled bracket pointed to is the end of a template arguement + list + """ + (start_line, start_linenum, start_pos) = OpenExpression(clean_lines, linenum, pos) + + # We didn't find a matching < so clearly not a template argument list + if start_pos == -1: + return False + + # This collects all the charatcers between the > and matching < + # It also reverses it since we don't care about the actual contents + # we are just looking to make sure no weird characters (like &&) + inbetween_string = "" + if linenum == start_linenum: + inbetween_string = clean_lines.elided[start_linenum][start_pos:pos + 1] + else: + while(start_linenum != linenum): + inbetween_string += clean_lines.elided[start_linenum][start_pos:] + start_linenum += 1 + start_pos = 0 + inbetween_string += clean_lines.elided[linenum][0:pos] + + is_simple_template_params = Match('^[<>(::),\w\s]*$', inbetween_string) + if is_simple_template_params: + return True + + # The operators & or * are accepted within the template arguments + # as we can have reference or pointer types in the list + # however, they must be followed by a > (excepting white space) + # anything else indicates they are being used as a type + + is_looking_for_closing_angle_bracket = False + for char in inbetween_string: + if char in ['&', '*']: + is_looking_for_closing_angle_bracket = True + elif is_looking_for_closing_angle_bracket: + if char == '>': + is_looking_for_closing_angle_bracket = False + elif char not in [' ', '\t']: + return False + + return True + +def ForceOpenExpression(clean_lines, linenum, pos, bracket): + """Find an opening bracket matching the specified closing bracket + + Search starting at the position for a matching closing bracket of the + same type as bracket. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + bracket: The style of bracket to match + + Returns: + A tuple (line, linenum, pos) pointer *to* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + + # Check first line + (end_pos, stack) = FindStartOfExpressionInLine(line, pos , [bracket]) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (end_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def OpenExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the closing of the expression. + + Essentially a mirror of what CloseExpression does + + Calls ForceOpenExpression with the bracket type pointed at + + TODO(tkiley): could probably be merged with CloseExpression + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *to* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + if (line[pos] not in ')}]>'): + return (line, clean_lines.NumLines(), -1) + else: + return ForceOpenExpression(clean_lines, linenum, pos-1, line[pos]) + + +def FindEndOfExpressionInLine(line, startpos, stack): + """Find the position just after the end of current parenthesized expression. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + stack: nesting stack at startpos. + + Returns: + On finding matching end: (index just after matching end, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at end of this line) + """ + for i in xrange(startpos, len(line)): + char = line[i] + if char in '([{': + # Found start of parenthesized expression, push to expression stack + stack.append(char) + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + if stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + elif i > 0 and Search(r'\boperator\s*$', line[0:i]): + # operator<, don't add to stack + continue + else: + # Tentative start of template argument list + stack.append('<') + elif char in ')]}': + # Found end of parenthesized expression. + # + # If we are currently expecting a matching '>', the pending '<' + # must have been an operator. Remove them from expression stack. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + if ((stack[-1] == '(' and char == ')') or + (stack[-1] == '[' and char == ']') or + (stack[-1] == '{' and char == '}')): + stack.pop() + if not stack: + return (i + 1, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == '>': + # Found potential end of template argument list. + + # Ignore "->" and operator functions + if (i > 0 and + (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): + continue + + # Pop the stack if there is a matching '<'. Otherwise, ignore + # this '>' since it must be an operator. + if stack: + if stack[-1] == '<': + stack.pop() + if not stack: + return (i + 1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '>', the matching '<' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + + # Did not find end of expression or unbalanced parentheses on this line + return (-1, stack) + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [ or <, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the + linenum/pos that correspond to the closing of the expression. + + TODO(unknown): cpplint spends a fair bit of time matching parentheses. + Ideally we would want to index all opening and closing parentheses once + and have CloseExpression be just a simple lookup, but due to preprocessor + tricks, this is not so easy. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): + return (line, clean_lines.NumLines(), -1) + + # Check first line + (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def FindDoStart(clean_lines, linenum): + """If found a while (...); find the potential do to match it. + + Work our way up through the lines starting at linenum to find a do + that hasn't been matched. This might not succeed as might just be an + emtpy while statement. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The line number with the while(...); on + + Returns: + A tuple (found, linenum), found is true if an opening + do statement found, false otherwise. The linenum will + be the line the do is found on (or -1 if never found) + """ + + reverse_lines = clean_lines.lines[linenum-1:0:-1] + found_linenum = linenum - 1; + for line in reverse_lines: + if Search(r'^\s*do\s*{?\s*$', line): + return True, found_linenum + elif Search(r'^\s*}?\s*while\(.*\)\s*;\s*$', line): + return False, -1 + else: + found_linenum = found_linenum - 1 + + return False, -1 + +def FindStartOfExpressionInLine(line, endpos, stack): + """Find position at the matching start of current expression. + + This is almost the reverse of FindEndOfExpressionInLine, but note + that the input position and returned position differs by 1. + + Args: + line: a CleansedLines line. + endpos: start searching at this position. + stack: nesting stack at endpos. + + Returns: + On finding matching start: (index at matching start, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at beginning of this line) + """ + i = endpos + while i >= 0: + char = line[i] + if char in ')]}': + # Found end of expression, push to expression stack + stack.append(char) + elif char == '>': + # Found potential end of template argument list. + # + # Ignore it if it's a "->" or ">=" or "operator>" + if (i > 0 and + (line[i - 1] == '-' or + Match(r'\s>=\s', line[i - 1:]) or + Search(r'\boperator\s*$', line[0:i]))): + i -= 1 + else: + stack.append('>') + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + i -= 1 + else: + # If there is a matching '>', we can pop the expression stack. + # Otherwise, ignore this '<' since it must be an operator. + if stack and stack[-1] == '>': + stack.pop() + if not stack: + return (i, None) + elif char in '([{': + # Found start of expression. + # + # If there are any unmatched '>' on the stack, they must be + # operators. Remove those. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + if ((char == '(' and stack[-1] == ')') or + (char == '[' and stack[-1] == ']') or + (char == '{' and stack[-1] == '}')): + stack.pop() + if not stack: + return (i, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '<', the matching '>' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + + i -= 1 + + return (-1, stack) + + +def ReverseCloseExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the opening of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *at* the opening brace, or + (line, 0, -1) if we never find the matching opening brace. Note + we ignore strings and comments when matching; and the line we + return is the 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + if line[pos] not in ')}]>': + return (line, 0, -1) + + # Check last line + (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) + if start_pos > -1: + return (line, linenum, start_pos) + + # Continue scanning backward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if start_pos > -1: + return (line, linenum, start_pos) + + # Did not find start of expression before beginning of file, give up + return (line, 0, -1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Author', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Author: "') + + +def GetIndentLevel(line): + """Return the number of leading spaces in line. + + Args: + line: A string to check. + + Returns: + An integer count of leading spaces, possibly zero. + """ + indent = Match(r'^( *)\S', line) + if indent: + return len(indent.group(1)) + else: + return 0 + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + # Replace 'c++' with 'cpp'. + filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RepositoryName() + file_path_from_root = 'CPROVER_2LS_' + file_path_from_root[4:] + if _root: + suffix = os.sep + # On Windows using directory separator will leave us with + # "bogus escape error" unless we properly escape regex. + if suffix == '\\': + suffix += '\\' + file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root) + return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + + +def CheckForHeaderGuard(filename, clean_lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + clean_lines: A CleansedLines instance containing the file. + error: The function to call with any errors found. + """ + + # Don't check for header guards if there are error suppression + # comments somewhere in this file. + # + # Because this is silencing a warning for a nonexistent line, we + # only support the very specific NOLINT(build/header_guard) syntax, + # and not the general NOLINT or NOLINT(*) syntax. + raw_lines = clean_lines.lines_without_raw_strings + for i in raw_lines: + if Search(r'//\s*NOLINT\(build/header_guard\)', i): + return + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = '' + ifndef_linenum = 0 + define = '' + endif = '' + endif_linenum = 0 + for linenum, line in enumerate(raw_lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef or not define or ifndef != define: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + # Check for "//" comments on endif line. + #ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, + # error) + #match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) + #if match: + # if match.group(1) == '_': + # # Issue low severity warning for deprecated double trailing underscore + # error(filename, endif_linenum, 'build/header_guard', 0, + # '#endif line should be "#endif // %s"' % cppvar) + # return + + # Didn't find the corresponding "//" comment. If this file does not + # contain any "//" comments at all, it could be that the compiler + # only wants "/**/" comments, look for those instead. + #no_single_line_comments = True + #for i in xrange(1, len(raw_lines) - 1): + # line = raw_lines[i] + # if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): + # no_single_line_comments = False + # break + # + #if no_single_line_comments: + # match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) + # if match: + # if match.group(1) == '_': + # # Low severity warning for double trailing underscore + # error(filename, endif_linenum, 'build/header_guard', 0, + # '#endif line should be "#endif /* %s */"' % cppvar) + # return + # + ## Didn't find anything + #error(filename, endif_linenum, 'build/header_guard', 5, + # '#endif line should be "#endif // %s"' % cppvar) + + +def CheckHeaderFileIncluded(filename, include_state, error): + """Logs an error if a .cc file does not include its header.""" + + # Do not check test files + fileinfo = FileInfo(filename) + if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): + return + + headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' + if not os.path.exists(headerfile): + return + headername = FileInfo(headerfile).RepositoryName() + first_include = 0 + for section_list in include_state.include_list: + for f in section_list: + if headername in f[0] or f[0] in headername: + return + if not first_include: + first_include = f[1] + + error(filename, first_include, 'build/include', 5, + '%s should include its header file %s' % (fileinfo.RepositoryName(), + headername)) + + +def CheckForBadCharacters(filename, lines, error): + """Logs an error for each line containing bad characters. + + Two kinds of bad characters: + + 1. Unicode replacement characters: These indicate that either the file + contained invalid UTF-8 (likely) or Unicode replacement characters (which + it shouldn't). Note that it's possible for this to throw off line + numbering if the invalid UTF-8 occurred adjacent to a newline. + + 2. NUL bytes. These are problematic for some tools. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + if '\0' in line: + error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. ' + 'Use C++11 raw strings or concatenation instead.') + + +# (non-threadsafe name, thread-safe alternative, validation pattern) +# +# The validation pattern is used to eliminate false positives such as: +# _rand(); // false positive due to substring match. +# ->rand(); // some member function rand(). +# ACMRandom rand(seed); // some variable named rand. +# ISAACRandom rand(); // another variable named rand. +# +# Basically we require the return value of these functions to be used +# in some expression context on the same line by matching on some +# operator before the function name. This eliminates constructors and +# member function calls. +_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' +_THREADING_LIST = ( + ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), + ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), + ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), + ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), + ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), + ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), + ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), + ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), + ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), + ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), + ('strtok(', 'strtok_r(', + _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), + ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: + # Additional pattern matching check to confirm that this is the + # function we are looking for + if Search(pattern, line): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_func + + '...) instead of ' + single_thread_func + + '...) for improved thread safety.') + + +def CheckVlogArguments(filename, clean_lines, linenum, error): + """Checks that VLOG() is only used for defining a logging level. + + For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and + VLOG(FATAL) are not. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): + error(filename, linenum, 'runtime/vlog', 5, + 'VLOG() should be used with numeric verbosity level. ' + 'Use LOG() if you want symbolic severity levels.') + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +def IsMacroDefinition(clean_lines, linenum): + if Search(r'^#define', clean_lines[linenum]): + return True + + if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): + return True + + return False + + +def IsForwardClassDeclaration(clean_lines, linenum): + return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) + + +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, linenum, seen_open_brace): + self.starting_linenum = linenum + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + self.check_namespace_indentation = False + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. + + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def IsBlockInfo(self): + """Returns true if this block is a _BlockInfo. + + This is convenient for verifying that an object is an instance of + a _BlockInfo, but not an instance of any of the derived classes. + + Returns: + True for this class, False for derived classes. + """ + return self.__class__ == _BlockInfo + + +class _ExternCInfo(_BlockInfo): + """Stores information about an 'extern "C"' block.""" + + def __init__(self, linenum): + _BlockInfo.__init__(self, linenum, True) + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name + self.is_derived = False + self.check_namespace_indentation = True + if class_or_struct == 'struct': + self.access = 'public' + self.is_struct = True + else: + self.access = 'private' + self.is_struct = False + + # Remember initial indentation level for this class. Using raw_lines here + # instead of elided to account for leading comments. + self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + # If there is a DISALLOW macro, it should appear near the end of + # the class. + seen_last_thing_in_class = False + for i in xrange(linenum - 1, self.starting_linenum, -1): + match = Search( + r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + + self.name + r'\)', + clean_lines.elided[i]) + if match: + if seen_last_thing_in_class: + error(filename, i, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + break + + if not Match(r'^\s*$', clean_lines.elided[i]): + seen_last_thing_in_class = True + + # Check that closing brace is aligned with beginning of the class. + # Only do this if the closing brace is indented by only whitespaces. + # This means we will not check single-line class definitions. + indent = Match(r'^( *)\}', clean_lines.elided[linenum]) + if indent and len(indent.group(1)) != self.class_indent: + if self.is_struct: + parent = 'struct ' + self.name + else: + parent = 'class ' + self.name + error(filename, linenum, 'whitespace/indent', 3, + 'Closing brace should be aligned with beginning of %s' % parent) + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name or '' + self.check_namespace_indentation = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. + # + # We also accept stuff like "// end of namespace ." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. +# if self.name: +# # Named namespace +# if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + +# re.escape(self.name) + r'[\*/\.\\\s]*$'), +# line): +# error(filename, linenum, 'readability/namespace', 5, +# 'Namespace should be terminated with "// namespace %s"' % +# self.name) +# else: +# # Anonymous namespace +# if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): +# # If "// namespace anonymous" or "// anonymous namespace (more text)", +# # mention "// anonymous namespace" as an acceptable form +# if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): +# error(filename, linenum, 'readability/namespace', 5, +# 'Anonymous namespace should be terminated with "// namespace"' +# ' or "// anonymous namespace"') +# else: +# error(filename, linenum, 'readability/namespace', 5, +# 'Anonymous namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Top of the previous stack before each Update(). + # + # Because the nesting_stack is updated at the end of each line, we + # had to do some convoluted checks to find out what is the current + # scope at the beginning of the line. This check is simplified by + # saving the previous top of nesting stack. + # + # We could save the full stack, but we only need the top. Copying + # the full nesting stack would slow down cpplint by ~10%. + self.previous_stack_top = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def InExternC(self): + """Check if we are currently one level inside an 'extern "C"' block. + + Returns: + True if top of the stack is an extern block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ExternCInfo) + + def InClassDeclaration(self): + """Check if we are currently one level inside a class or struct declaration. + + Returns: + True if top of the stack is a class/struct, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ClassInfo) + + def InAsmBlock(self): + """Check if we are currently one level inside an inline ASM block. + + Returns: + True if the top of the stack is a block containing inline ASM. + """ + return self.stack and self.stack[-1].inline_asm != _NO_ASM + + def InTemplateArgumentList(self, clean_lines, linenum, pos): + """Check if current position is inside template argument list. + + TODO (tkiley): This method seems a little generous for what it + considers to be in a template argument list, providing the line + between pos and the end contains a > (or even an =) before the + end of the line or }, {, ; then it will say yes... For now have + added method IsTemplateArgumentList_DB but should work out + what the idea with this method is a unify. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: position just after the suspected template argument. + Returns: + True if (linenum, pos) is inside template arguments. + """ + while linenum < clean_lines.NumLines(): + # Find the earliest character that might indicate a template argument + line = clean_lines.elided[linenum] + match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) + if not match: + linenum += 1 + pos = 0 + continue + token = match.group(1) + pos += len(match.group(0)) + + # These things do not look like template argument list: + # class Suspect { + # class Suspect x; } + if token in ('{', '}', ';'): return False + + # These things look like template argument list: + # template + # template + # template + # template + if token in ('>', '=', '[', ']', '.'): return True + + # Check if token is an unmatched '<'. + # If not, move on to the next character. + if token != '<': + pos += 1 + if pos >= len(line): + linenum += 1 + pos = 0 + continue + + # We can't be sure if we just find a single '<', and need to + # find the matching '>'. + (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) + if end_pos < 0: + # Not sure if template argument list or syntax error in file + return False + linenum = end_line + pos = end_pos + return False + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + # TODO(unknown): Update() is too long, but we will refactor later. + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remember top of the previous nesting stack. + # + # The stack is always pushed/popped and not modified in place, so + # we can just do a shallow copy instead of copy.deepcopy. Using + # deepcopy would slow down cpplint by ~28%. + if self.stack: + self.previous_stack_top = self.stack[-1] + else: + self.previous_stack_top = None + + # Update pp_stack + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + class_decl_match = Match( + r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' + r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' + r'(.*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + # We do not want to accept classes that are actually template arguments: + # template , + # template class Ignore3> + # void Function() {}; + # + # To avoid template argument cases, we scan forward and look for + # an unmatched '>'. If we see one, assume we are inside a + # template argument list. + end_declaration = len(class_decl_match.group(1)) + if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): + self.stack.append(_ClassInfo( + class_decl_match.group(3), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(4) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + classinfo = self.stack[-1] + access_match = Match( + r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' + r':(?:[^:]|$)', + line) + if access_match: + classinfo.access = access_match.group(2) + +# # Check that access keywords are indented +1 space. Skip this +# # check if the keywords are not preceded by whitespaces. +# indent = access_match.group(1) +# if (len(indent) != classinfo.class_indent + 1 and +# Match(r'^\s*$', indent)): +# if classinfo.is_struct: +# parent = 'struct ' + classinfo.name +# else: +# parent = 'class ' + classinfo.name +# slots = '' +# if access_match.group(3): +# slots = access_match.group(3) +# error(filename, linenum, 'whitespace/indent', 3, +# '%s%s: should be indented +1 space inside %s' % ( +# access_match.group(2), slots, parent)) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + elif Match(r'^extern\s*"[^"]*"\s*\{', line): + self.stack.append(_ExternCInfo(linenum)) + else: + self.stack.append(_BlockInfo(linenum, True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckCompletedBlocks(self, filename, error): + """Checks that all classes and namespaces have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + elif isinstance(obj, _NamespaceInfo): + error(filename, obj.starting_linenum, 'build/namespaces', 5, + 'Failed to find complete declaration of namespace %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: + return + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + explicit_constructor_match = Match( + r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' + r'\(((?:[^()]|\([^()]*\))*)\)' + % re.escape(base_classname), + line) + + if explicit_constructor_match: + is_marked_explicit = explicit_constructor_match.group(1) + + if not explicit_constructor_match.group(2): + constructor_args = [] + else: + constructor_args = explicit_constructor_match.group(2).split(',') + + # collapse arguments so that commas in template parameter lists and function + # argument parameter lists don't split arguments in two + i = 0 + while i < len(constructor_args): + constructor_arg = constructor_args[i] + while (constructor_arg.count('<') > constructor_arg.count('>') or + constructor_arg.count('(') > constructor_arg.count(')')): + constructor_arg += ',' + constructor_args[i + 1] + del constructor_args[i + 1] + constructor_args[i] = constructor_arg + i += 1 + + defaulted_args = [arg for arg in constructor_args if '=' in arg] + noarg_constructor = (not constructor_args or # empty arg list + # 'void' arg specifier + (len(constructor_args) == 1 and + constructor_args[0].strip() == 'void')) + onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg + not noarg_constructor) or + # all but at most one arg defaulted + (len(constructor_args) >= 1 and + not noarg_constructor and + len(defaulted_args) >= len(constructor_args) - 1)) + initializer_list_constructor = bool( + onearg_constructor and + Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) + copy_constructor = bool( + onearg_constructor and + Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' + % re.escape(base_classname), constructor_args[0].strip())) + + if (not is_marked_explicit and + onearg_constructor and + not initializer_list_constructor and + not copy_constructor): + if defaulted_args: + error(filename, linenum, 'runtime/explicit', 5, + 'Constructors callable with one argument ' + 'should be marked explicit.') + else: + error(filename, linenum, 'runtime/explicit', 5, + 'Single-parameter constructors should be marked explicit.') + elif is_marked_explicit and not onearg_constructor: + if noarg_constructor: + error(filename, linenum, 'runtime/explicit', 5, + 'Zero-parameter constructors should not be marked explicit.') + + +def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', + fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and + not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and + not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and + not Search(r'\bcase\s+\(', fncall)): + # TODO(unknown): Space after an operator function seem to be a common + # error, silence those for now by restricting them to highest verbosity. + if Search(r'\boperator_*\b', line): + error(filename, linenum, 'whitespace/parens', 0, + 'Extra space before ( in function call') + else: + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error): + is_namespace_indent_item = ( + len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and + nesting_state.previous_stack_top == nesting_state.stack[-2]) + + if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + clean_lines.elided, line): + CheckItemIndentationInNamespace(filename, clean_lines.elided, + line, error) + +def CheckForFunctionCommentHeaders(filename, raw_lines, error): + """ Check all the lines for functions without function comment headers + + Using the raw original lines (before multi-line comments are removed) + find lines that contain functions and checks they have a function comment header + + Args: + filename - The name of the file being checked + raw_lines - The original, with comments lines + error - the function to report errors with + """ + linenum = 0 + function_state = _FunctionState() + for line in raw_lines: + joined_line = '' + starting_func = False + # Look for declaration function_name( but allowing for *, & being attached to the function name + # but not being considered part of it + regexp = r'\w(\w|::|\s|\*|\&)* (\*|\&)?(?P(\w(\w|::)*))\('# + operator_regexp = r'\w(\w|::|\s|\*|\&)* (\*|\&)?(?P(\w(\w|::)*::)?(operator\(.*\)|operator.*))\(' + operator_match = Match(operator_regexp, line) + match_result = Match(regexp, line) + function_name = "" + if operator_match: + function_name = operator_match.group('fnc_name') + elif match_result: + function_name = match_result.group('fnc_name') + + if operator_match or match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, len(raw_lines)): + start_line = raw_lines[start_linenum] + if Search(r'{', start_line): + body_found = True + break + elif Search(r';', start_line): + body_found = False + break + + # body found, i.e. not a declaration + if body_found: + CheckForFunctionCommentHeader(filename, raw_lines, linenum, function_name, error) + linenum += 1 + +def CheckForFunctionCommentHeader(filename, raw_lines, linenum, function_name, error): + """ Check each function has a comment header + + Rules for function comment header: + 1. Header demarked by /*{67}\ at the top + 2. Header demarked by \*{67}/ at the bottom + 3. Contains Function: classname::function_name + 4. Contains Inputs: + 5. Contains Outputs: + 6. Contains Purpose: + 7. new line between function and bottom of comment + + Args: + filename - the name of the file + raw_lines - all the unmodified lines (including multi-line comments) + linenum - the line number of the line to check + function_name - the name of the function that was found + error - function to report errors with + + """ + + function_name = re.escape(function_name) + + header_top_regex = r'^/\*{67}\\$' + header_bottom_regex = r'^\\\*{67}/$' + function_name_regex = r'Function: (\w+::)?' + function_name+'$' + + found_empty_space = raw_lines[linenum-1] == "" + + header_start = linenum - 2 if found_empty_space else linenum - 1 + found_header_bottom = Match(header_bottom_regex, raw_lines[header_start]) + + if not found_header_bottom: + error(filename, linenum, 'readability/function_comment', 4, + 'Could not find function header comment for ' + function_name) + return + + found_header_top = False + + found_function_name = False + found_inputs = False + found_outputs = False + found_purpose = False + + for i in xrange(header_start - 1, 0, -1): + if(Match(header_top_regex, raw_lines[i])): + found_header_top = True + break + + if(Search(r'Inputs:', raw_lines[i])): + found_inputs = True + elif(Search(r'Outputs:', raw_lines[i])): + found_outputs = True + elif(Search(r'Purpose:', raw_lines[i])): + found_purpose = True + elif(Search(r'Function:', raw_lines[i])): + found_function_name = True + if(not Search(function_name_regex, raw_lines[i])): + error(filename, i, 'readability/function_comment', 4, + 'Function: name in the comment doesn\'t match the function name') + + if found_header_top: + if not found_inputs: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Inputs:') + if not found_outputs: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Outputs:') + if not found_purpose: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Purpose:') + else: + error(filename, linenum, 'readability/function_comment', 4, + 'Could not find top of function header comment for ' + function_name) + + if not found_empty_space: + error(filename, linenum, 'readability/function_comment', 4, + 'Insert an empty line between function header comment and the function ' + function_name) + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(line, filename, linenum, next_line_start, error): + """Checks for common mistakes in comments. + + Args: + line: The line in question. + filename: The name of the current file. + linenum: The number of the line to check. + next_line_start: The first non-whitespace column of the next line. + error: The function to call with any errors found. + """ + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: + # Allow one space for new scopes, two spaces otherwise: + if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace))): +#or +# (commentpos >= 2 and +# line[commentpos-2] not in string.whitespace)) + error(filename, linenum, 'whitespace/comments', 2, +# 'At least two spaces is best between code and comments') + 'At least one space is best between code and comments') + + # Checks for common mistakes in TODO comments. + comment = line[commentpos:] + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + +# username = match.group(2) +# if not username: +# error(filename, linenum, 'readability/todo', 2, +# 'Missing username in TODO; it should look like ' +# '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + # If the comment contains an alphanumeric character, there + # should be a space somewhere between it and the // unless + # it's a /// or //! Doxygen comment. + if (Match(r'//[^ ]*\w', comment) and + not Match(r'(///|//\!)(\s+|$)', comment)): + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw = clean_lines.lines_without_raw_strings + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + # + # Also skip blank line checks for 'extern "C"' blocks, which are formatted + # like namespaces. + if (IsBlankLine(line) and + not nesting_state.InNamespaceBody() and + not nesting_state.InExternC()): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Redundant blank line at the start of a code block ' + 'should be deleted.') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Redundant blank line at the end of a code block ' + 'should be deleted.') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, check comments + next_line_start = 0 + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + next_line_start = len(next_line) - len(next_line.lstrip()) + CheckComment(line, filename, linenum, next_line_start, error) + + # get rid of comments and strings + line = clean_lines.elided[linenum] + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'return []() {};' + if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search(r'for *\(.*[^:]:[^: ]', line) or + Search(r'for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckOperatorSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around operators. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Don't try to do spacing checks for operator methods. Do this by + # replacing the troublesome characters with something else, + # preserving column position for all other characters. + # + # The replacement is done repeatedly to avoid false positives from + # operators that call operators. + while True: + match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) + if match: + line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) + else: + break + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + left_hand_space_match = Search(r'[^\s]+[\s](=|>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=|>|<|\^|\+[^\+]|\/)', line) + right_hand_space_match = Search(r'(=|>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=|>|<|!|\^|\+|\/)[\s]', line) + operator_pos, op_end = left_hand_space_match.span(1) if left_hand_space_match else (-1, -1) + operator_pos2, op_end2 = right_hand_space_match.span(1) if right_hand_space_match else (-1, -1) + + if (left_hand_space_match and + not Search(r'<<', line) and # We ignore the left shift operator since might be a stream and then formatting rules go out of the window + not Search(r'char \*', line) and # I don't know why this exception exists? + not Search(r'\#include', line) and # I suppose file names could contains operators?? + #not Search(r'<.*[^\s]>', line)): # This checks for template declarations (providing they don't run onto next line) + not IsTemplateArgumentList_DB(clean_lines, linenum, operator_pos)): +# and not Search(r'\b(if|while|for) ', line) +# # Operators taken from [lex.operators] in C++11 standard. +# and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) +# and not Search(r'operator=', line)): + error(filename, linenum, 'whitespace/operators', 4, +# 'Missing spaces around =') + 'Remove spaces around %s' % left_hand_space_match.group(1)) + elif (right_hand_space_match and + not Search(r'<<', line) and + not IsTemplateArgumentList_DB(clean_lines, linenum, operator_pos2)): + error(filename, linenum, 'whitespace/operators', 4, + 'Remove spaces around %s' % right_hand_space_match.group(0)) + +# these would cause too many false alarms if we checked for one-sided spaces only + match = Search(r'\s(-|\*)(\s|$)', line) + if match: + error(filename, linenum, 'readability/identifiers', 4, + 'Remove spaces around %s' % match.group(0)) + +# check any inherited classes don't have a space between the type and the : + if Search(r'(struct|class)\s[\w_]*\s+:', line): + error(filename, linenum, 'readability/identifiers', 4, 'There shouldn\'t be a space between class identifier and :') + + #check type definitions end with t + # Look for class declarations and check the final character is a t + # Exclude classes in side template argument lists (why?) + class_name_match = Search(r'\b(struct|class)\s(?P[\w_]+)', line) + if class_name_match: + class_name = class_name_match.group('class_name') + if not class_name.endswith('t'): + if not Search(r'\btemplate <', line) and not Search(r'\btemplate<', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Class or struct identifier should end with t') + + if Search(r'(struct|class)\s[\w_]*_t(;$|\s|:|$)', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Class or struct identifier should end with t, not _t') + + if Search(r'typedef\s.*\s[\w_]*[^t];$', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Typedef identifier should end with t') + + if Search(r'typedef\s.*\s[\w_]*_t;$', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Typedef identifier should end with t, not _t') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + # + # If the operator is followed by a comma, assume it's be used in a + # macro context and don't do any checks. This avoids false + # positives. + # + # Note that && is not included here. This is because there are too + # many false positives due to RValue references. +# match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) + match = Search(r'[^\s](&&|\|\|)[^\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + +# TODO Do we want this? +# match = Search(r'[^\s^:](:|\?)[^\s^:]', line) +# if match: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around %s' % match.group(1)) + +# elif not Match(r'#.*include', line): +# # Look for < that is not surrounded by spaces. This is only +# # triggered if both sides are missing spaces, even though +# # technically should should flag if at least one side is missing a +# # space. This is done to avoid some false positives with shifts. +# match = Match(r'^(.*[^\s<])<[^\s=<,]', line) +# if match: +# (_, _, end_pos) = CloseExpression( +# clean_lines, linenum, len(match.group(1))) +# if end_pos <= -1: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around <') +# +# # Look for > that is not surrounded by spaces. Similar to the +# # above, we only trigger if both sides are missing spaces to avoid +# # false positives with shifts. +# match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) +# if match: +# (_, _, start_pos) = ReverseCloseExpression( +# clean_lines, linenum, len(match.group(1))) +# if start_pos <= -1: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around >') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + +def CheckPointerReferenceSpacing(filename, clean_lines, linenum, error): + """Checks the */& are attached to variable names rather than types + + A pointer or reference type should have the */& attached to the variable + name rather than the type. I.e. a pointer to an int should be: + + int *var_name; + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Find types by looking for word_names that are at the start of the line + # If there are followed by a * or & (after an optional ' const') + # then they appear to be a reference/pointer type with it attached to + # the type rather than the variable + wrong_type_match = Search(r'^\s*([\w_])+( const)?(?P[&\*])\s*\w', line) + + if wrong_type_match: + # This still could be a false positive, we must + # check that we are not inside brackets as then could be just be + # operators (multiply and logical AND) + pos = wrong_type_match.start(1) + _, _, opening_pos = ForceOpenExpression(clean_lines, linenum, pos, ')') + + # If we don't find a matching close brace then we aren't in brackets + # so can assume this is a type with the * or & attached to it + if opening_pos == -1: + op_type = wrong_type_match.group('type') + op_word = "" + if op_type == '*': + op_word = 'Pointer' + else: + op_word = 'Reference' + error(filename, linenum, 'whitespace/operators', 4, + op_word + ' type name must have ' + op_type + ' attached to the type name') + + + +def CheckParenthesisSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around parentheses. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # No spaces after an if, while, switch, or for + match = Search(r' (if \(|for \(|while \(|switch \()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Remove space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if len(match.group(2)) not in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + +def CheckCommaSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing near commas and semicolons. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.lines_without_raw_strings + line = clean_lines.elided[linenum] + + # You should always have a space after a comma (either as fn arg or operator) + # + # This does not apply when the non-space character following the + # comma is another comma, since the only time when that happens is + # for empty macro arguments. + # + # We run this check in two passes: first pass on elided lines to + # verify that lines contain missing whitespaces, second pass on raw + # lines to confirm that those missing whitespaces are not due to + # elided comments. + if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and + Search(r',[^,\s]', raw[linenum])): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + +def _IsType(clean_lines, nesting_state, expr): + """Check if expression looks like a type name, returns true if so. + + Args: + clean_lines: A CleansedLines instance containing the file. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + expr: The expression to check. + Returns: + True, if token looks like a type. + """ + # Keep only the last token in the expression + last_word = Match(r'^.*(\b\S+)$', expr) + if last_word: + token = last_word.group(1) + else: + token = expr + + # Match native types and stdint types + if _TYPES.match(token): + return True + + # Try a bit harder to match templated types. Walk up the nesting + # stack until we find something that resembles a typename + # declaration for what we are looking for. + typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + + r'\b') + block_index = len(nesting_state.stack) - 1 + while block_index >= 0: + if isinstance(nesting_state.stack[block_index], _NamespaceInfo): + return False + + # Found where the opening brace is. We want to scan from this + # line up to the beginning of the function, minus a few lines. + # template + # class C + # : public ... { // start scanning here + last_line = nesting_state.stack[block_index].starting_linenum + + next_block_start = 0 + if block_index > 0: + next_block_start = nesting_state.stack[block_index - 1].starting_linenum + first_line = last_line + while first_line >= next_block_start: + if clean_lines.elided[first_line].find('template') >= 0: + break + first_line -= 1 + if first_line < next_block_start: + # Didn't find any "template" keyword before reaching the next block, + # there are probably no template things to check for this block + block_index -= 1 + continue + + # Look for typename in the specified range + for i in xrange(first_line, last_line + 1, 1): + if Search(typename_pattern, clean_lines.elided[i]): + return True + block_index -= 1 + + return False + + +def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for horizontal spacing near commas. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces when they are delimiting blocks, classes, namespaces etc. + # And since you should never have braces at the beginning of a line, + # this is an easy test. Except that braces used for initialization don't + # follow the same rule; we often don't want spaces before those. + match = Match(r'^(.*[^ ({>]){', line) + + if match: + # Try a bit harder to check for brace initialization. This + # happens in one of the following forms: + # Constructor() : initializer_list_{} { ... } + # Constructor{}.MemberFunction() + # Type variable{}; + # FunctionCall(type{}, ...); + # LastArgument(..., type{}); + # LOG(INFO) << type{} << " ..."; + # map_of_type[{...}] = ...; + # ternary = expr ? new type{} : nullptr; + # OuterTemplate{}> + # + # We check for the character following the closing brace, and + # silence the warning if it's one of those listed above, i.e. + # "{.;,)<>]:". + # + # To account for nested initializer list, we allow any number of + # closing braces up to "{;,)<". We can't simply silence the + # warning on first sight of closing brace, because that would + # cause false negatives for things that are not initializer lists. + # Silence this: But not this: + # Outer{ if (...) { + # Inner{...} if (...){ // Missing space before { + # }; } + # + # There is a false negative with this approach if people inserted + # spurious semicolons, e.g. "if (cond){};", but we will catch the + # spurious semicolon with a separate check. + leading_text = match.group(1) + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + trailing_text = '' + if endpos > -1: + trailing_text = endline[endpos:] + for offset in xrange(endlinenum + 1, + min(endlinenum + 3, clean_lines.NumLines() - 1)): + trailing_text += clean_lines.elided[offset] + # We also suppress warnings for `uint64_t{expression}` etc., as the style + # guide recommends brace initialization for integral types to avoid + # overflow/truncation. + if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) + and not _IsType(clean_lines, nesting_state, leading_text)): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + # if Search(r'}else', line): + # error(filename, linenum, 'whitespace/braces', 5, + # 'Missing space before else') + if (Search(r'^.*[^\s].*}$', line) or Search(r'^.*[^\s].*{$', line)) and not(Search(r'{[^}]*}', line)): + error(filename, linenum, 'whitespace/braces', 5, + 'Put braces on a separate next line') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use {} instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + +def IsDecltype(clean_lines, linenum, column): + """Check if the token ending on (linenum, column) is decltype(). + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is decltype() expression, False otherwise. + """ + (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) + if start_col < 0: + return False + if Search(r'\bdecltype\s*$', text[0:start_col]): + return True + return False + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Check for else if/else that are on the same line as the previous brace + if Search(r'}\s*else if', line): + error(filename, linenum, 'readability/braces', 5, + 'Else if should be on a new line after closing brace') + elif Search(r'}\s*else', line): + error(filename, linenum, 'readability/braces', 5, + 'Else should be on a new line after closing brace') + + # Check for if/else if/else don't have statements on the same line + if Search(r'^\s*if\(.*\)\s*\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after an if should be on a new line') + elif Search(r'else if\(.*\)', line): + if Search(r'else if\(.*\)\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after else if should be on a new line') + elif Search(r'else\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after else should be on a new line') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Check single-line if/else bodies. The style guide says 'curly braces are not + # required for single-line statements'. We additionally allow multi-line, + # single statements, but we reject anything with more than one semicolon in + # it. This means that the first semicolon after the if should be at the end of + # its line, and the line after that should have an indent level equal to or + # lower than the if. We also check for ambiguous if/else nesting without + # braces. + if_else_match = Search(r'\b(if\s*\(|else\b)', line) + if if_else_match and not Match(r'\s*#', line): + if_indent = GetIndentLevel(line) + endline, endlinenum, endpos = line, linenum, if_else_match.end() + if_match = Search(r'\bif\s*\(', line) + if if_match: + # This could be a multiline if condition, so find the end first. + pos = if_match.end() - 1 + (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) + # Check for an opening brace, either directly after the if or on the next + # line. If found, this isn't a single-statement conditional. + if (not Match(r'\s*{', endline[endpos:]) + and not (Match(r'\s*$', endline[endpos:]) + and endlinenum < (len(clean_lines.elided) - 1) + and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): + while (endlinenum < len(clean_lines.elided) + and ';' not in clean_lines.elided[endlinenum][endpos:]): + endlinenum += 1 + endpos = 0 + if endlinenum < len(clean_lines.elided): + endline = clean_lines.elided[endlinenum] + # We allow a mix of whitespace and closing braces (e.g. for one-liner + # methods) and a single \ after the semicolon (for macros) + endpos = endline.find(';') + if not Match(r';[\s}]*(\\?)$', endline[endpos:]): + # Semicolon isn't the last character, there's something trailing. + # Output a warning if the semicolon is not contained inside + # a lambda expression. + if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', + endline): + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + elif endlinenum < len(clean_lines.elided) - 1: + # Make sure the next line is dedented + next_line = clean_lines.elided[endlinenum + 1] + next_indent = GetIndentLevel(next_line) + # With ambiguous nested if statements, this will error out on the + # if that *doesn't* match the else, regardless of whether it's the + # inner one or outer one. + if (if_match and Match(r'\s*else\b', next_line) + and next_indent != if_indent): + error(filename, linenum, 'readability/braces', 4, + 'Else clause should be indented at the same level as if. ' + 'Ambiguous nested if/else chains require braces.') + elif next_indent > if_indent: + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + +def CheckDoWhile(filename, clean_lines, linenum, error): + """Looks for while of a do while on the same line as the closing brace + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'}\s*while\s*\(', line): + do_found, num = FindDoStart(clean_lines, linenum) + if do_found: + error(filename, linenum, 'readability/braces', 4, + 'while statement of do...while loop should be on a separate line to the closing brace') + +def CheckTrailingSemicolon(filename, clean_lines, linenum, error): + """Looks for redundant trailing semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] + + # Block bodies should not be followed by a semicolon. Due to C++11 + # brace initialization, there are more places where semicolons are + # required than not, so we use a whitelist approach to check these + # rather than a blacklist. These are the places where "};" should + # be replaced by just "}": + # 1. Some flavor of block following closing parenthesis: + # for (;;) {}; + # while (...) {}; + # switch (...) {}; + # Function(...) {}; + # if (...) {}; + # if (...) else if (...) {}; + # + # 2. else block: + # if (...) else {}; + # + # 3. const member function: + # Function(...) const {}; + # + # 4. Block following some statement: + # x = 42; + # {}; + # + # 5. Block at the beginning of a function: + # Function(...) { + # {}; + # } + # + # Note that naively checking for the preceding "{" will also match + # braces inside multi-dimensional arrays, but this is fine since + # that expression will not contain semicolons. + # + # 6. Block following another block: + # while (true) {} + # {}; + # + # 7. End of namespaces: + # namespace {}; + # + # These semicolons seems far more common than other kinds of + # redundant semicolons, possibly due to people converting classes + # to namespaces. For now we do not warn for this case. + # + # Try matching case 1 first. + match = Match(r'^(.*\)\s*)\{', line) + if match: + # Matched closing parenthesis (case 1). Check the token before the + # matching opening parenthesis, and don't warn if it looks like a + # macro. This avoids these false positives: + # - macro that defines a base class + # - multi-line macro that defines a base class + # - macro that defines the whole class-head + # + # But we still issue warnings for macros that we know are safe to + # warn, specifically: + # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P + # - TYPED_TEST + # - INTERFACE_DEF + # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: + # + # We implement a whitelist of safe macros instead of a blacklist of + # unsafe macros, even though the latter appears less frequently in + # google code and would have been easier to implement. This is because + # the downside for getting the whitelist wrong means some extra + # semicolons, while the downside for getting the blacklist wrong + # would result in compile errors. + # + # In addition to macros, we also don't want to warn on + # - Compound literals + # - Lambdas + # - alignas specifier with anonymous structs + # - decltype + closing_brace_pos = match.group(1).rfind(')') + opening_parenthesis = ReverseCloseExpression( + clean_lines, linenum, closing_brace_pos) + if opening_parenthesis[2] > -1: + line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] + macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) + func = Match(r'^(.*\])\s*$', line_prefix) + if ((macro and + macro.group(1) not in ( + 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', + 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', + 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or + (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or + Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or + Search(r'\bdecltype$', line_prefix) or + Search(r'\s+=\s*$', line_prefix)): + match = None + if (match and + opening_parenthesis[1] > 1 and + Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): + # Multi-line lambda-expression + match = None + + else: + # Try matching cases 2-3. + match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + if not match: + # Try matching cases 4-6. These are always matched on separate lines. + # + # Note that we can't simply concatenate the previous line to the + # current line and do a single match, otherwise we may output + # duplicate warnings for the blank line case: + # if (cond) { + # // blank line + # } + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if prevline and Search(r'[;{}]\s*$', prevline): + match = Match(r'^(\s*)\{', line) + + # Check matching closing brace + if match: + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + # Current {} pair is eligible for semicolon check, and we have found + # the redundant semicolon, output warning here. + # + # Note: because we are scanning forward for opening braces, and + # outputting warnings for the matching closing brace, if there are + # nested blocks with trailing semicolons, we will get the error + # messages in reversed order. + + # We need to check the line forward for NOLINT + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1, + error) + ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum, + error) + + error(filename, endlinenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyBlockBody(filename, clean_lines, linenum, error): + """Look for empty loop/conditional body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + # + # We also check "if" blocks here, since an empty conditional block + # is likely an error. + line = clean_lines.elided[linenum] + matched = Match(r'\s*(for|while|if)\s*\(', line) + while_matched = Match(r'\s*(while)\s*\(', line) + + # if it is a do while loop, we have the while clause on a separate line + # so need to exclude that + do_found = False + if while_matched: + do_found, num = FindDoStart(clean_lines, linenum) + + is_do_while = while_matched and do_found + # either not a while or if we are, we didn't find a do + if matched and not is_do_while: + # Find the end of the conditional expression. + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if matched.group(1) == 'if': + error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, + 'Empty conditional bodies should use {}') + else: + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + # Check for if statements that have completely empty bodies (no comments) + # and no else clauses. + if end_pos >= 0 and matched.group(1) == 'if': + # Find the position of the opening { for the if statement. + # Return without logging an error if it has no brackets. + opening_linenum = end_linenum + opening_line_fragment = end_line[end_pos:] + # Loop until EOF or find anything that's not whitespace or opening {. + while not Search(r'^\s*\{', opening_line_fragment): + if Search(r'^(?!\s*$)', opening_line_fragment): + # Conditional has no brackets. + return + opening_linenum += 1 + if opening_linenum == len(clean_lines.elided): + # Couldn't find conditional's opening { or any code before EOF. + return + opening_line_fragment = clean_lines.elided[opening_linenum] + # Set opening_line (opening_line_fragment may not be entire opening line). + opening_line = clean_lines.elided[opening_linenum] + + # Find the position of the closing }. + opening_pos = opening_line_fragment.find('{') + if opening_linenum == end_linenum: + # We need to make opening_pos relative to the start of the entire line. + opening_pos += end_pos + (closing_line, closing_linenum, closing_pos) = CloseExpression( + clean_lines, opening_linenum, opening_pos) + if closing_pos < 0: + return + + # Now construct the body of the conditional. This consists of the portion + # of the opening line after the {, all lines until the closing line, + # and the portion of the closing line before the }. + if (clean_lines.raw_lines[opening_linenum] != + CleanseComments(clean_lines.raw_lines[opening_linenum])): + # Opening line ends with a comment, so conditional isn't empty. + return + if closing_linenum > opening_linenum: + # Opening line after the {. Ignore comments here since we checked above. + body = list(opening_line[opening_pos+1:]) + # All lines until closing line, excluding closing line, with comments. + body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) + # Closing line before the }. Won't (and can't) have comments. + body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) + body = '\n'.join(body) + else: + # If statement has brackets and fits on a single line. + body = opening_line[opening_pos+1:closing_pos-1] + + # Check if the body is empty + if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): + return + # The body is empty. Now make sure there's not an else clause. + current_linenum = closing_linenum + current_line_fragment = closing_line[closing_pos:] + # Loop until EOF or find anything that's not whitespace or else clause. + while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): + if Search(r'^(?=\s*else)', current_line_fragment): + # Found an else clause, so don't log an error. + return + current_linenum += 1 + if current_linenum == len(clean_lines.elided): + break + current_line_fragment = clean_lines.elided[current_linenum] + + # The body is empty and there's no else clause until EOF or other code. + error(filename, end_linenum, 'whitespace/empty_if_body', 4, + ('If statement had no body and no else clause')) + + +def FindCheckMacro(line): + """Find a replaceable CHECK-like macro. + + Args: + line: line to search on. + Returns: + (macro name, start position), or (None, -1) if no replaceable + macro is found. + """ + for macro in _CHECK_MACROS: + i = line.find(macro) + if i >= 0: + # Find opening parenthesis. Do a regular expression match here + # to make sure that we are matching the expected CHECK macro, as + # opposed to some other macro that happens to contain the CHECK + # substring. + matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) + if not matched: + continue + return (macro, len(matched.group(1))) + return (None, -1) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + lines = clean_lines.elided + (check_macro, start_pos) = FindCheckMacro(lines[linenum]) + if not check_macro: + return + + # Find end of the boolean expression by matching parentheses + (last_line, end_line, end_pos) = CloseExpression( + clean_lines, linenum, start_pos) + if end_pos < 0: + return + + # If the check macro is followed by something other than a + # semicolon, assume users will log their own custom error messages + # and don't suggest any replacements. + if not Match(r'\s*;', last_line[end_pos:]): + return + + if linenum == end_line: + expression = lines[linenum][start_pos + 1:end_pos - 1] + else: + expression = lines[linenum][start_pos + 1:] + for i in xrange(linenum + 1, end_line): + expression += lines[i] + expression += last_line[0:end_pos - 1] + + # Parse expression so that we can take parentheses into account. + # This avoids false positives for inputs like "CHECK((a < 4) == b)", + # which is not replaceable by CHECK_LE. + lhs = '' + rhs = '' + operator = None + while expression: + matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' + r'==|!=|>=|>|<=|<|\()(.*)$', expression) + if matched: + token = matched.group(1) + if token == '(': + # Parenthesized operand + expression = matched.group(2) + (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) + if end < 0: + return # Unmatched parenthesis + lhs += '(' + expression[0:end] + expression = expression[end:] + elif token in ('&&', '||'): + # Logical and/or operators. This means the expression + # contains more than one term, for example: + # CHECK(42 < a && a < b); + # + # These are not replaceable with CHECK_LE, so bail out early. + return + elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): + # Non-relational operator + lhs += token + expression = matched.group(2) + else: + # Relational operator + operator = token + rhs = matched.group(2) + break + else: + # Unparenthesized operand. Instead of appending to lhs one character + # at a time, we do another regular expression match to consume several + # characters at once if possible. Trivial benchmark shows that this + # is more efficient when the operands are longer than a single + # character, which is generally the case. + matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) + if not matched: + matched = Match(r'^(\s*\S)(.*)$', expression) + if not matched: + break + lhs += matched.group(1) + expression = matched.group(2) + + # Only apply checks if we got all parts of the boolean expression + if not (lhs and operator and rhs): + return + + # Check that rhs do not contain logical operators. We already know + # that lhs is fine since the loop above parses out && and ||. + if rhs.find('&&') > -1 or rhs.find('||') > -1: + return + + # At least one of the operands must be a constant literal. This is + # to avoid suggesting replacements for unprintable things like + # CHECK(variable != iterator) + # + # The following pattern matches decimal, hex integers, strings, and + # characters (in that order). + lhs = lhs.strip() + rhs = rhs.strip() + match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' + if Match(match_constant, lhs) or Match(match_constant, rhs): + # Note: since we know both lhs and rhs, we can provide a more + # descriptive error message like: + # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) + # Instead of: + # Consider using CHECK_EQ instead of CHECK(a == b) + # + # We are still keeping the less descriptive message because if lhs + # or rhs gets long, the error message might become unreadable. + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[check_macro][operator], + check_macro, operator)) + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw_lines = clean_lines.lines_without_raw_strings + line = raw_lines[linenum] + prev = raw_lines[linenum - 1] if linenum > 0 else '' + + elided_line = clean_lines.elided[linenum] + elided_prev = clean_lines.elided[linenum - 1] if linenum > 0 else '' + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found, replace by spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' + classinfo = nesting_state.InnermostClass() + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + # There are certain situations we allow one space, notably for + # section labels, and also lines containing multi-line raw strings. + # We also don't check for lines that look like continuation lines + # (of lines ending in double quotes, commas, equals, or angle brackets) + # because the rules for how to indent those are non-trivial. + if (not Search(r'[",=><] *$', prev) and + (initial_spaces == 1 or initial_spaces == 3) and + not Match(scope_or_label_pattern, cleansed_line) and + not (clean_lines.raw_lines[linenum] != line and + Match(r'^\s*""', line))): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + +# ensure indentation within parenthesized expressions (only on elided lines since we don't care about comments or strings) + prev_initial_spaces = 0 + if linenum>0: + while prev_initial_spaces < len(prev) and prev[prev_initial_spaces] == ' ': + prev_initial_spaces += 1 + # here a regex isn't sufficent we need a stack to match brackets + # because even an open bracket and a , at the end might just be function call + # as a parameter. + # Instead the rule we try to apply here is: + # - if we find an opening bracket, we find the matching closing bracket + # - if the bracket is on a different line we require all of the parameters to be on a separate line + # - if there is another opening bracket Skip to the closing bracket as will be checked in a subsequent line check + # - ignore the line if it is a for/if etc since rules are different + + # Look for an opening bracket that doesn't have a semi-colon on the same line + bracket_search = Search(r'[\w_]+\s*(?P\()[^;]*$', elided_line) + + # Exclude the check if any of these keywords are present + # They could trip us up as they have different formatting rules to functions + keyword_search = Search(r'\b(if|for|while|catch|switch)\b', elided_line) + + if bracket_search and not keyword_search: + open_bracket_pos = bracket_search.start('bracket') + close_line, close_linenum, close_pos = CloseExpression(clean_lines, linenum, open_bracket_pos) + if close_pos != -1: + # If the closing line is different from the opening line we need to + # verify that each of the parameters are on separate lines + if close_linenum != linenum: + # The first line needs to have no parameters on it + if(Search(r'\(+[^\(]+', elided_line)): + error(filename, linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + # For each line afer we need to verify it consists of exactly one parameter + # Except if we find an opening bracket - in this case we ignore everything until the closing + # bracket (any errors within these brackets will be picked up when we check this line) + start_linenum = linenum + 1 + while(start_linenum < close_linenum): + arg_line = clean_lines.elided[start_linenum] + nested_bracket_search = Search('\(', arg_line) + if nested_bracket_search: + nested_open_bracket_pos = nested_bracket_search.start() + # Just because we are calling a nested function doesn't mean + # we allow multiple parameters on the line + if(Search(',', arg_line[:nested_open_bracket_pos])): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + nested_close_line, nested_close_linenum, _ = CloseExpression(clean_lines, start_linenum, nested_open_bracket_pos) + + # If anything other closing statements or commas appear there is another parameter after the nested call + if not Search(r'\)(,|\)|;)*', nested_close_line): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + # Skip to the end of the bracket + start_linenum = nested_close_linenum + else: + if(not Match('^\s*[^,]+,$', arg_line)): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + start_linenum+=1 + # For the final line we also need to check one parameter on it + # e.g. we require bracket on same line as last parameter + # foo( + # x); + if not Search(r'^\s*[^,]+\)', close_line): + # If this is true, the we failed because we just had the close bracket + if Search(r'[^,]*\)', close_line): + error(filename, close_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, the closing bracket should be on the same line as the final parameter') + else: + # In this case the problem is we had a bracket + # i.e. more than one parameter on the last line + error(filename, close_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + + if (Search(r'\([^\)]*$', elided_prev) and initial_spaces-2 != prev_initial_spaces) and not Search(r'for|while|if|;', elided_prev): + error(filename, linenum, 'whitespace/indent', 4, + 'Indent of wrapped parenthesized expression or parameter or argument list should be 2') + + # Check if the line is a header guard. + is_header_guard = False + if IsHeaderExtension(file_extension): + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^\s*//\s*[^\s]*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + if line_width > _line_length: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= %i characters long' % _line_length) + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckDoWhile(filename, clean_lines, linenum, error) + CheckTrailingSemicolon(filename, clean_lines, linenum, error) + CheckEmptyBlockBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckOperatorSpacing(filename, clean_lines, linenum, error) + CheckPointerReferenceSpacing(filename, clean_lines, linenum, error) + CheckParenthesisSpacing(filename, clean_lines, linenum, error) + CheckCommaSpacing(filename, clean_lines, linenum, error) + CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) + CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) + + +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_cpp_h = include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + # Only do this check if the included header follows google naming + # conventions. If not, assume that it's a 3rd party API that + # requires special include conventions. + # + # We also make an exception for Lua headers, which follow google + # naming convention but not the include convention. +# match = Match(r'#include\s*"([^/]+\.h)"', line) +# if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): +# error(filename, linenum, 'build/include', 4, +# 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + duplicate_line = include_state.FindHeader(include) + if duplicate_line >= 0: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, duplicate_line)) + elif (include.endswith('.cc') and + os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): + error(filename, linenum, 'build/include', 4, + 'Do not include .cc files from other packages') + elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): + include_state.include_list[-1].append((include, linenum)) + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. +# error_message = include_state.CheckNextIncludeOrder( +# _ClassifyInclude(fileinfo, include, is_system)) +# if error_message: +# error(filename, linenum, 'build/include_order', 4, +# '%s. Should be: %s.h, c system, c++ system, other.' % +# (error_message, fileinfo.BaseName())) +# canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) +# if not include_state.IsInAlphabeticalOrder( +# clean_lines, linenum, canonical_include): +# error(filename, linenum, 'build/include_alpha', 4, +# 'Include "%s" not in alphabetical order' % include) +# include_state.SetLastHeader(canonical_include) + + + +def _GetTextInside(text, start_pattern): + r"""Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(unknown): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +# Patterns for matching call-by-reference parameters. +# +# Supports nested templates up to 2 levels deep using this messy pattern: +# < (?: < (?: < [^<>]* +# > +# | [^<>] )* +# > +# | [^<>] )* +# > +_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* +_RE_PATTERN_TYPE = ( + r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' + r'(?:\w|' + r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' + r'::)+') +# A call-by-reference parameter ends with '& identifier'. +_RE_PATTERN_REF_PARAM = re.compile( + r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' + r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') +# A call-by-const-reference parameter either ends with 'const& identifier' +# or looks like 'const type& identifier' when 'type' is atomic. +_RE_PATTERN_CONST_REF_PARAM = ( + r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + + r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') +# Stream types. +_RE_PATTERN_REF_STREAM_PARAM = ( + r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, + include_state, nesting_state, error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Reset include state across preprocessor directives. This is meant + # to silence warnings for conditional includes. + match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) + if match: + include_state.ResetSection(match.group(1)) + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # Perform other checks now that we are sure that this is not an include line + CheckCasts(filename, clean_lines, linenum, error) + CheckGlobalStatic(filename, clean_lines, linenum, error) + CheckPrintf(filename, clean_lines, linenum, error) + + if IsHeaderExtension(file_extension): + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes declare or disable copy/assign + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. +# if Search(r'\bshort port\b', line): +# if not Search(r'\bunsigned short port\b', line): +# error(filename, linenum, 'runtime/int', 4, +# 'Use "unsigned short" for ports, not "short"') +# else: +# match = Search(r'\b(short|long(?! +double)|long long)\b', line) +# if match: +# error(filename, linenum, 'runtime/int', 4, +# 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(unknown): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays.') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (IsHeaderExtension(file_extension) + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + + + # Check that throw statements don't include the optional bracket + # We use raw lines as we want to check the contents of the string too + # We require the error message starts with a lower case character + raw_line = clean_lines.raw_lines[linenum] + if(Match(r'^\s*throw', raw_line)): + if(Match(r'^\s*throw\s*\(', raw_line)): + error(filename, linenum, 'readability/throw', 4, + 'Do not include brackets when throwing an error') + if(Match(r'\s*throw\s*\(?"[A-Z]', raw_line)): + error(filename, linenum, 'readability/throw', 4, + 'First character of throw error message should be lower case') + + +def CheckGlobalStatic(filename, clean_lines, linenum, error): + """Check for unsafe global or static objects. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Match two lines at a time to support multiline declarations + if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): + line += clean_lines.elided[linenum + 1].strip() + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access, and + # also because globals can be destroyed when some threads are still running. + # TODO(unknown): Generalize this to also find static unique_ptr instances. + # TODO(unknown): File bugs for clang-tidy to find these. + match = Match( + r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' + r'([a-zA-Z0-9_:]+)\b(.*)', + line) + + # Remove false positives: + # - String pointers (as opposed to values). + # string *pointer + # const string *pointer + # string const *pointer + # string *const pointer + # + # - Functions and template specializations. + # string Function(... + # string Class::Method(... + # + # - Operators. These are matched separately because operator names + # cross non-word boundaries, and trying to match both operators + # and functions at the same time would decrease accuracy of + # matching identifiers. + # string Class::operator*() + if (match and + not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and + not Search(r'\boperator\W', line) and + not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): + if Search(r'\bconst\b', line): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string ' + 'instead: "%schar%s %s[]".' % + (match.group(1), match.group(2) or '', match.group(3))) + else: + error(filename, linenum, 'runtime/string', 4, + 'Static/global string variables are not permitted.') + + if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or + Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + +def CheckPrintf(filename, clean_lines, linenum, error): + """Check for printf related issues. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\s*\(', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\s*\(', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + +def IsDerivedFunction(clean_lines, linenum): + """Check if current line contains an inherited function. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains a function with "override" + virt-specifier. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) + if match: + # Look for "override" after the matching closing parenthesis + line, _, closing_paren = CloseExpression( + clean_lines, i, len(match.group(1))) + return (closing_paren >= 0 and + Search(r'\boverride\b', line[closing_paren:])) + return False + + +def IsOutOfLineMethodDefinition(clean_lines, linenum): + """Check if current line contains an out-of-line method definition. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains an out-of-line method definition. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): + return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None + return False + + +def IsInitializerList(clean_lines, linenum): + """Check if current line is inside constructor initializer list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line appears to be inside constructor initializer + list, False otherwise. + """ + for i in xrange(linenum, 1, -1): + line = clean_lines.elided[i] + if i == linenum: + remove_function_body = Match(r'^(.*)\{\s*$', line) + if remove_function_body: + line = remove_function_body.group(1) + + if Search(r'\s:\s*\w+[({]', line): + # A lone colon tend to indicate the start of a constructor + # initializer list. It could also be a ternary operator, which + # also tend to appear in constructor initializer lists as + # opposed to parameter lists. + return True + if Search(r'\}\s*,\s*$', line): + # A closing brace followed by a comma is probably the end of a + # brace-initialized member in constructor initializer list. + return True + if Search(r'[{};]\s*$', line): + # Found one of the following: + # - A closing brace or semicolon, probably the end of the previous + # function. + # - An opening brace, probably the start of current class or namespace. + # + # Current line is probably not inside an initializer list since + # we saw one of those things without seeing the starting colon. + return False + + # Got to the beginning of the file without seeing the start of + # constructor initializer list. + return False + + +def CheckForNonConstReference(filename, clean_lines, linenum, + nesting_state, error): + """Check for non-const references. + + Separate from CheckLanguage since it scans backwards from current + line, instead of scanning forward. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # Do nothing if there is no '&' on current line. + line = clean_lines.elided[linenum] + if '&' not in line: + return + + # If a function is inherited, current function doesn't have much of + # a choice, so any non-const references should not be blamed on + # derived function. + if IsDerivedFunction(clean_lines, linenum): + return + + # Don't warn on out-of-line method definitions, as we would warn on the + # in-line declaration, if it isn't marked with 'override'. + if IsOutOfLineMethodDefinition(clean_lines, linenum): + return + + # Long type names may be broken across multiple lines, usually in one + # of these forms: + # LongType + # ::LongTypeContinued &identifier + # LongType:: + # LongTypeContinued &identifier + # LongType< + # ...>::LongTypeContinued &identifier + # + # If we detected a type split across two lines, join the previous + # line to current line so that we can match const references + # accordingly. + # + # Note that this only scans back one line, since scanning back + # arbitrary number of lines would be expensive. If you have a type + # that spans more than 2 lines, please use a typedef. + if linenum > 1: + previous = None + if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): + # previous_line\n + ::current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', + clean_lines.elided[linenum - 1]) + elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): + # previous_line::\n + current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', + clean_lines.elided[linenum - 1]) + if previous: + line = previous.group(1) + line.lstrip() + else: + # Check for templated parameter that is split across multiple lines + endpos = line.rfind('>') + if endpos > -1: + (_, startline, startpos) = ReverseCloseExpression( + clean_lines, linenum, endpos) + if startpos > -1 and startline < linenum: + # Found the matching < on an earlier line, collect all + # pieces up to current line. + line = '' + for i in xrange(startline, linenum + 1): + line += clean_lines.elided[i].strip() + + # Check for non-const references in function parameters. A single '&' may + # found in the following places: + # inside expression: binary & for bitwise AND + # inside expression: unary & for taking the address of something + # inside declarators: reference parameter + # We will exclude the first two cases by checking that we are not inside a + # function body, including one that was just introduced by a trailing '{'. + # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. + if (nesting_state.previous_stack_top and + not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or + isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): + # Not at toplevel, not within a class, and not within a namespace + return + + # Avoid initializer lists. We only need to scan back from the + # current line for something that starts with ':'. + # + # We don't need to check the current line, since the '&' would + # appear inside the second set of parentheses on the current line as + # opposed to the first set. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 10), -1): + previous_line = clean_lines.elided[i] + if not Search(r'[),]\s*$', previous_line): + break + if Match(r'^\s*:\s+\S', previous_line): + return + + # Avoid preprocessors + if Search(r'\\\s*$', line): + return + + # Avoid constructor initializer lists + if IsInitializerList(clean_lines, linenum): + return + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". Do not check + # those function parameters. + # + # We also accept & in static_assert, which looks like a function but + # it's actually a declaration expression. +# whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' +# r'operator\s*[<>][<>]|' +# r'static_assert|COMPILE_ASSERT' +# r')\s*\(') +# if Search(whitelisted_functions, line): +# return +# elif not Search(r'\S+\([^)]*$', line): +# # Don't see a whitelisted function on this line. Actually we +# # didn't see any function name on this line, so this is likely a +# # multi-line parameter list. Try a bit harder to catch this case. +# for i in xrange(2): +# if (linenum > i and +# Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): +# return +# +# decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body +# for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): +# if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and +# not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): +# error(filename, linenum, 'runtime/references', 2, +# 'Is this a non-const reference? ' +# 'If so, make const or use a pointer: ' + +# ReplaceAll(' *<', '<', parameter)) + + +def CheckCasts(filename, clean_lines, linenum, error): + """Various cast related checks. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' + r'(int|float|double|bool|char|int32|uint32|int64|uint64)' + r'(\([^)].*)', line) + expecting_function = ExpectingFunctionArgs(clean_lines, linenum) + if match and not expecting_function: + matched_type = match.group(2) + + # matched_new_or_template is used to silence two false positives: + # - New operators + # - Template arguments with function types + # + # For template arguments, we match on types immediately following + # an opening bracket without any spaces. This is a fast way to + # silence the common case where the function type is the first + # template argument. False negative with less-than comparison is + # avoided because those operators are usually followed by a space. + # + # function // bracket + no space = false positive + # value < double(42) // bracket + space = true positive + matched_new_or_template = match.group(1) + + # Avoid arrays by looking for brackets that come after the closing + # parenthesis. + if Match(r'\([^()]+\)\s*\[', match.group(3)): + return + + # Other things to ignore: + # - Function pointers + # - Casts to pointer types + # - Placement new + # - Alias declarations + matched_funcptr = match.group(3) + if (matched_new_or_template is None and + not (matched_funcptr and + (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', + matched_funcptr) or + matched_funcptr.startswith('(*)'))) and + not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and + not Search(r'new\(\S+\)\s*' + matched_type, line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + matched_type) + + if not expecting_function: + CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', + r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', + r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + # + # Some non-identifier character is required before the '&' for the + # expression to be recognized as a cast. These are casts: + # expression = &static_cast(temporary()); + # function(&(int*)(temporary())); + # + # This is not a cast: + # reference_type&(int* function_param); + match = Search( + r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' + r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) + if match: + # Try a better error message when the & is bound to something + # dereferenced by the casted pointer, as opposed to the casted + # pointer itself. + parenthesis_error = False + match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) + if match: + _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) + if x1 >= 0 and clean_lines.elided[y1][x1] == '(': + _, y2, x2 = CloseExpression(clean_lines, y1, x1) + if x2 >= 0: + extended_line = clean_lines.elided[y2][x2:] + if y2 < clean_lines.NumLines() - 1: + extended_line += clean_lines.elided[y2 + 1] + if Match(r'\s*(?:->|\[)', extended_line): + parenthesis_error = True + + if parenthesis_error: + error(filename, linenum, 'readability/casting', 4, + ('Are you taking an address of something dereferenced ' + 'from a cast? Wrapping the dereferenced expression in ' + 'parentheses will make the binding more obvious')) + else: + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + +def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): + """Checks for a C-style cast by looking for the pattern. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + line = clean_lines.elided[linenum] + match = Search(pattern, line) + if not match: + return False + + # Exclude lines with keywords that tend to look like casts + context = line[0:match.start(1) - 1] + if Match(r'.*\b(?:sizeof|alignof|alignas|catch|[_A-Z][_A-Z0-9]*)\s*$', context): + return False + + # Try expanding current context to see if we one level of + # parentheses inside a macro. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 5), -1): + context = clean_lines.elided[i] + context + if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): + return False + + # operator++(int) and operator--(int) + if context.endswith(' operator++') or context.endswith(' operator--'): + return False + + # A single unnamed argument for a function tends to look like old style cast. + # If we see those, don't issue warnings for deprecated casts. + remainder = line[match.end(0):] + if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', + remainder): + return False + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +def ExpectingFunctionArgs(clean_lines, linenum): + """Checks whether where function type arguments are expected. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + + Returns: + True if the line at 'linenum' is inside something that expects arguments + of function types. + """ + line = clean_lines.elided[linenum] + return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + (linenum >= 2 and + (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', + clean_lines.elided[linenum - 1]) or + Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', + clean_lines.elided[linenum - 2]) or + Search(r'\bstd::m?function\s*\<\s*$', + clean_lines.elided[linenum - 1])))) + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', + 'unique_ptr', 'weak_ptr')), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('tuple',)), + ('', ('unordered_map', 'unordered_multimap')), + ('', ('unordered_set', 'unordered_multiset')), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_MAYBE_TEMPLATES = ( + ('', ('copy', 'max', 'min', 'min_element', 'sort', + 'transform', + )), + ('', ('forward', 'make_pair', 'move', 'swap')), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_headers_maybe_templates = [] +for _header, _templates in _HEADERS_MAYBE_TEMPLATES: + for _template in _templates: + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_headers_maybe_templates.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + _header)) + +# Other scripts may reach in and modify this pattern. +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + fileinfo = FileInfo(filename_cc) + if not fileinfo.IsSource(): + return (False, '') + filename_cc = filename_cc[:-len(fileinfo.Extension())] + matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) + if matched_test_suffix: + filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_dict, io=codecs): + """Fill up the include_dict with new includes found from the file. + + Args: + filename: the name of the header to read. + include_dict: a dictionary in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was successfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + include_dict.setdefault(include, linenum) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_headers_maybe_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + matched = pattern.search(line) + if matched: + # Don't warn about IWYU in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's flatten the include_state include_list and copy it into a dictionary. + include_dict = dict([item for sublist in include_state.include_list + for item in sublist]) + + # Did we find the header for this file (if any) and successfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_dict is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_dict.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_dict, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + +# # All the lines have been processed, report the errors found. +# for required_header_unstripped in required: +# template = required[required_header_unstripped][1] +# if required_header_unstripped.strip('<>"') not in include_dict: +# error(filename, required[required_header_unstripped][0], +# 'build/include_what_you_use', 4, +# 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def CheckRedundantVirtual(filename, clean_lines, linenum, error): + """Check if line contains a redundant "virtual" function-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for "virtual" on current line. + line = clean_lines.elided[linenum] + virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) + if not virtual: return + + # Ignore "virtual" keywords that are near access-specifiers. These + # are only used in class base-specifier and do not apply to member + # functions. + if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or + Match(r'^\s+(public|protected|private)\b', virtual.group(3))): + return + + # Ignore the "virtual" keyword from virtual base classes. Usually + # there is a column on the same line in these cases (virtual base + # classes are rare in google3 because multiple inheritance is rare). + if Match(r'^.*[^:]:[^:].*$', line): return + + # Look for the next opening parenthesis. This is the start of the + # parameter list (possibly on the next line shortly after virtual). + # TODO(unknown): doesn't work if there are virtual functions with + # decltype() or other things that use parentheses, but csearch suggests + # that this is rare. + end_col = -1 + end_line = -1 + start_col = len(virtual.group(2)) + for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): + line = clean_lines.elided[start_line][start_col:] + parameter_list = Match(r'^([^(]*)\(', line) + if parameter_list: + # Match parentheses to find the end of the parameter list + (_, end_line, end_col) = CloseExpression( + clean_lines, start_line, start_col + len(parameter_list.group(1))) + break + start_col = 0 + + if end_col < 0: + return # Couldn't find end of parameter list, give up + +# # Look for "override" or "final" after the parameter list +# # (possibly on the next few lines). +# for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): +# line = clean_lines.elided[i][end_col:] +# match = Search(r'\b(override|final)\b', line) +# if match: +# error(filename, linenum, 'readability/inheritance', 4, +# ('"virtual" is redundant since function is ' +# 'already declared as "%s"' % match.group(1))) +# +# # Set end_col to check whole lines after we are done with the +# # first line. +# end_col = 0 +# if Search(r'[^\w]\s*$', line): +# break + + + +# Returns true if we are at a new block, and it is directly +# inside of a namespace. +def IsBlockInNameSpace(nesting_state, is_forward_declaration): + """Checks that the new block is directly in a namespace. + + Args: + nesting_state: The _NestingState object that contains info about our state. + is_forward_declaration: If the class is a forward declared class. + Returns: + Whether or not the new block is directly in a namespace. + """ + if is_forward_declaration: + if len(nesting_state.stack) >= 1 and ( + isinstance(nesting_state.stack[-1], _NamespaceInfo)): + return True + else: + return False + + return (len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.stack[-2], _NamespaceInfo)) + + +def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + raw_lines_no_comments, linenum): + """This method determines if we should apply our namespace indentation check. + + Args: + nesting_state: The current nesting state. + is_namespace_indent_item: If we just put a new class on the stack, True. + If the top of the stack is not a class, or we did not recently + add the class, False. + raw_lines_no_comments: The lines without the comments. + linenum: The current line number we are processing. + + Returns: + True if we should apply our namespace indentation check. Currently, it + only works for classes and namespaces inside of a namespace. + """ + + is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, + linenum) + + if not (is_namespace_indent_item or is_forward_declaration): + return False + + # If we are in a macro, we do not want to check the namespace indentation. + if IsMacroDefinition(raw_lines_no_comments, linenum): + return False + + return IsBlockInNameSpace(nesting_state, is_forward_declaration) + + +# Call this method if the line is directly inside of a namespace. +# If the line above is blank (excluding comments) or the start of +# an inner namespace, it cannot be indented. +def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, + error): + line = raw_lines_no_comments[linenum] + if Match(r'^\s+', line): + error(filename, linenum, 'runtime/indentation_namespace', 4, + 'Do not indent within a namespace') + +def CheckNamespaceOrUsing(filename, clean_lines, linenum, error): + line = clean_lines.elided[linenum] + if Match(r'^namespace(\s|$)', line): + error(filename, linenum, 'readability/namespace', 4, + 'Do not use namespaces') + if Match(r'^using\s', line): + error(filename, linenum, 'readability/namespace', 4, + 'Do not use using') + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error) + if nesting_state.InAsmBlock(): return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + nesting_state, error) + CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckVlogArguments(filename, clean_lines, line, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + CheckRedundantVirtual(filename, clean_lines, line, error) + CheckNamespaceOrUsing(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def FlagCxx11Features(filename, clean_lines, linenum, error): + """Flag those c++11 features that we only allow in certain places. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + +# # Flag unapproved C++ TR1 headers. +# if include and include.group(1).startswith('tr1/'): +# error(filename, linenum, 'build/c++tr1', 5, +# ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) + + # Flag unapproved C++11 headers. + if include and include.group(1) in ('cfenv', + 'condition_variable', + 'fenv.h', + 'future', + 'mutex', + 'thread', + 'chrono', + 'ratio', + 'regex', + 'system_error', + ): + error(filename, linenum, 'build/c++11', 5, + ('<%s> is an unapproved C++11 header.') % include.group(1)) + + # The only place where we need to worry about C++11 keywords and library + # features in preprocessor directives is in macro definitions. + if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return + + # These are classes and free functions. The classes are always + # mentioned as std::*, but we only catch the free functions if + # they're not found by ADL. They're alphabetical by header. + for top_name in ( + # type_traits + 'alignment_of', + 'aligned_union', + ): + if Search(r'\bstd::%s\b' % top_name, line): + error(filename, linenum, 'build/c++11', 5, + ('std::%s is an unapproved C++11 class or function. Send c-style ' + 'an example of where it would make your code more readable, and ' + 'they may let you use it.') % top_name) + + +def FlagCxx14Features(filename, clean_lines, linenum, error): + """Flag those C++14 features that we restrict. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + + # Flag unapproved C++14 headers. + if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): + error(filename, linenum, 'build/c++14', 5, + ('<%s> is an unapproved C++14 header.') % include.group(1)) + + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = NestingState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + CheckForFunctionCommentHeaders(filename, lines, error) + ProcessGlobalSuppresions(lines) + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + + if IsHeaderExtension(file_extension): + CheckForHeaderGuard(filename, clean_lines, error) + + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + FlagCxx11Features(filename, clean_lines, line, error) + nesting_state.CheckCompletedBlocks(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # Check that the .cc file has included its header if it exists. + if _IsSourceExtension(file_extension): + CheckHeaderFileIncluded(filename, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForBadCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessConfigOverrides(filename): + """ Loads the configuration files and processes the config overrides. + + Args: + filename: The name of the file being processed by the linter. + + Returns: + False if the current |filename| should not be processed further. + """ + + abs_filename = os.path.abspath(filename) + cfg_filters = [] + keep_looking = True + while keep_looking: + abs_path, base_name = os.path.split(abs_filename) + if not base_name: + break # Reached the root directory. + + cfg_file = os.path.join(abs_path, "CPPLINT.cfg") + abs_filename = abs_path + if not os.path.isfile(cfg_file): + continue + + try: + with open(cfg_file) as file_handle: + for line in file_handle: + line, _, _ = line.partition('#') # Remove comments. + if not line.strip(): + continue + + name, _, val = line.partition('=') + name = name.strip() + val = val.strip() + if name == 'set noparent': + keep_looking = False + elif name == 'filter': + cfg_filters.append(val) + elif name == 'exclude_files': + # When matching exclude_files pattern, use the base_name of + # the current file name or the directory name we are processing. + # For example, if we are checking for lint errors in /foo/bar/baz.cc + # and we found the .cfg file at /foo/CPPLINT.cfg, then the config + # file's "exclude_files" filter is meant to be checked against "bar" + # and not "baz" nor "bar/baz.cc". + if base_name: + pattern = re.compile(val) + if pattern.match(base_name): + sys.stderr.write('Ignoring "%s": file excluded by "%s". ' + 'File path component "%s" matches ' + 'pattern "%s"\n' % + (filename, cfg_file, base_name, val)) + return False + elif name == 'linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + sys.stderr.write('Line length must be numeric.') + elif name == 'root': + global _root + _root = val + elif name == 'headers': + ProcessHppHeadersOption(val) + else: + sys.stderr.write( + 'Invalid configuration option (%s) in file %s\n' % + (name, cfg_file)) + + except IOError: + sys.stderr.write( + "Skipping config file '%s': Can't open for reading\n" % cfg_file) + keep_looking = False + + # Apply all the accumulated filters in reverse order (top-level directory + # config options having the least priority). + for filter in reversed(cfg_filters): + _AddFilters(filter) + + return True + + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + _BackupFilters() + +#exclude these files: + if Search(r'(\.l|\.y|\.inc|\.d|\.o|y\.tab\.cpp|\.tab\.h|\.yy\.cpp|builtin_headers)$', filename): + return + + if not ProcessConfigOverrides(filename): + _RestoreFilters() + return + + lf_lines = [] + crlf_lines = [] + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + # Remove trailing '\r'. + # The -1 accounts for the extra trailing blank line we get from split() + for linenum in range(len(lines) - 1): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + crlf_lines.append(linenum + 1) + else: + lf_lines.append(linenum + 1) + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + _RestoreFilters() + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if filename != '-' and file_extension not in _valid_extensions: + sys.stderr.write('Ignoring %s; not a valid file name ' + '(%s)\n' % (filename, ', '.join(_valid_extensions))) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + + # If end-of-line sequences are a mix of LF and CR-LF, issue + # warnings on the lines with CR. + # + # Don't issue any warnings if all lines are uniformly LF or CR-LF, + # since critique can handle these just fine, and the style guide + # doesn't dictate a particular end of line sequence. + # + # We can't depend on os.linesep to determine what the desired + # end-of-line sequence should be, since that will return the + # server-side end-of-line sequence. + if lf_lines and crlf_lines: + # Warn on every line with CR. An alternative approach might be to + # check whether the file is mostly CRLF or just LF, and warn on the + # minority, we bias toward LF here since most tools prefer LF. + for linenum in crlf_lines: + Error(filename, linenum, 'whitespace/newline', 1, + 'Unexpected \\r (^M) found; better to use only \\n') + + sys.stdout.write('Done processing %s\n' % filename) + _RestoreFilters() + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=', + 'root=', + 'linelength=', + 'extensions=', + 'headers=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if val not in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--root': + global _root + _root = val + elif opt == '--linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + PrintUsage('Line length must be digits.') + elif opt == '--extensions': + global _valid_extensions + try: + _valid_extensions = set(val.split(',')) + except ValueError: + PrintUsage('Extensions must be comma seperated list.') + elif opt == '--headers': + ProcessHppHeadersOption(val) + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/scripts/run_lint.sh b/scripts/run_lint.sh new file mode 100755 index 000000000..f2f1a41d8 --- /dev/null +++ b/scripts/run_lint.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +set -e + +if [[ "$#" -ne 2 ]] +then + echo "Script for running the CPP linter only on modified lines." + echo "Requires two arguments the start and the end" + echo "start - a git reference that marks the first commit whose changes to consider" + echo "end - a git reference that marks the last commit whose changes to consider" + + exit 1 +fi + +if ! [[ -e scripts/cpplint.py ]] +then + echo "Lint script could not be found in the scripts directory" + echo "Ensure cpplint.py is inside the scripts directory then run again" + exit 1 +fi + +git_start=$1 +git_end=$2 + +# Get the list of files that have changed +diff_files=`git diff --name-only $git_start $git_end` + +# Build a filter that will filter the blame output +# to only include lines that come from one of the relevant_commits +# We do this by making the blame tool output the same hash for all +# lines that are too old. +blame_grep_filter=`git rev-parse "$git_start"` + +# Build a regex for finding the line number of a given line inside blame +# First matches the 40 digit hash of the commi +# Then match an arbitary length number that represents the line in the original file +# Finally matches (and groups) another arbitary length digit which is the +# line in the final file +regex="[0-9a-f]{40} [0-9]+ ([0-9]+)" + +# We only split on lines or otherwise the git blame output is nonsense +IFS=$'\n' + +are_errors=0 + +for file in $diff_files; do + # We build another grep filter the output of the linting script + lint_grep_filter="^(" + + # Include line 0 errors (e.g. copyright) + lint_grep_filter+=$file + lint_grep_filter+=":0" + + # We first filter only the lines that start with a commit hash + # Then we filter out the ones that come from the start commit + modified_lines=`git blame $git_start..$git_end --line-porcelain $file | grep -E "^[0-9a-f]{40}" | { grep -v "$blame_grep_filter" || true; }` + + # For each modified line we find the line number + for line in $modified_lines; do + + # Use the above regex to match the line number + if [[ $line =~ $regex ]] + then + # Some bash magic to get the first group from the regex (the line number) + LINENUM="${BASH_REMATCH[1]}" + + # The format from the linting script is filepath:linenum: [error type] + # So we build the first bit to filter out relevant lines + LINE_FILTER=$file:$LINENUM + + # Add the line filter on to the grep expression as we want + # lines that match any of the line filters + lint_grep_filter+="|" + lint_grep_filter+=$LINE_FILTER + fi + done + + # Add the closing bracket + lint_grep_filter+=")" + + # Run the linting script and filter by the filter we've build + # of all the modified lines + # The errors from the linter go to STDERR so must be redirected to STDOUT + result=`python scripts/cpplint.py $file 2>&1 | { grep -E "$lint_grep_filter" || true; }` + + # Providing some errors were relevant we print them out + if [ "$result" ] + then + are_errors=1 + (>&2 echo "$result") + fi +done + +unset IFS + +# Return an error code if errors are found +exit $are_errors diff --git a/src/2ls/2ls_languages.cpp b/src/2ls/2ls_languages.cpp new file mode 100644 index 000000000..0b23d3539 --- /dev/null +++ b/src/2ls/2ls_languages.cpp @@ -0,0 +1,34 @@ +/*******************************************************************\ + +Module: Language Registration + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "2ls_parse_options.h" + +/*******************************************************************\ + +Function: twols_parse_optionst::register_languages + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::register_languages() +{ + register_language(new_ansi_c_language); +// register_language(new_cpp_language); +// register_language(new_java_bytecode_language); +} diff --git a/src/storefront/storefront_main.cpp b/src/2ls/2ls_main.cpp similarity index 60% rename from src/storefront/storefront_main.cpp rename to src/2ls/2ls_main.cpp index 3914e4dbd..1d4955202 100644 --- a/src/storefront/storefront_main.cpp +++ b/src/2ls/2ls_main.cpp @@ -1,18 +1,20 @@ /*******************************************************************\ -Module: Main Module +Module: 2LS Main Module -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ #include -#include "storefront_parse_options.h" +#include "2ls_parse_options.h" + +#ifdef _MSC_VER /*******************************************************************\ -Function: main +Function: wmain Inputs: @@ -22,17 +24,31 @@ Function: main \*******************************************************************/ -#ifdef _MSC_VER int wmain(int argc, const wchar_t **argv_wide) { const char **argv=narrow_argv(argc, argv_wide); - storefront_parse_optionst parse_options(argc, argv); + twols_parse_optionst parse_options(argc, argv); return parse_options.main(); } + #else + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + int main(int argc, const char **argv) { - storefront_parse_optionst parse_options(argc, argv); + twols_parse_optionst parse_options(argc, argv); return parse_options.main(); } + #endif diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/2ls/2ls_parse_options.cpp similarity index 67% rename from src/summarizer/summarizer_parse_options.cpp rename to src/2ls/2ls_parse_options.cpp index 492c3d1aa..046249b2e 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -1,12 +1,12 @@ /*******************************************************************\ -Module: Summarizer Command Line Options Processing +Module: 2LS Command Line Options Processing -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#include +#include #include #include #include @@ -28,13 +28,12 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include +#include #include #include -#include #include #include #include -#include "array_abstraction.h" #include @@ -43,23 +42,30 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "version.h" -#include "../ssa/malloc_ssa.h" +#include -#include "summarizer_parse_options.h" -#include "summary_db.h" +#include "graphml_witness_ext.h" +#include + +#include "2ls_parse_options.h" #include "summary_checker_ai.h" #include "summary_checker_bmc.h" #include "summary_checker_kind.h" -#include "../ssa/split_loopheads.h" #include "show.h" #include "horn_encoding.h" #define UNWIND_GOTO_INTO_LOOP 1 #define REMOVE_MULTIPLE_DEREFERENCES 1 +#define IGNORE_RECURSION 1 +#define IGNORE_THREADS 1 +#define EXPLICIT_NONDET_LOCALS 0 +#define FILTER_ASSERTIONS 1 +#define ASSUME_AFTER_ASSERT 0 + /*******************************************************************\ -Function: summarizer_parse_optionst::summarizer_parse_optionst +Function: twols_parse_optionst::twols_parse_optionst Inputs: @@ -69,15 +75,18 @@ Function: summarizer_parse_optionst::summarizer_parse_optionst \*******************************************************************/ -summarizer_parse_optionst::summarizer_parse_optionst(int argc, const char **argv): - parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit("2LS " CBMC_VERSION, cmdline) +twols_parse_optionst::twols_parse_optionst(int argc, const char **argv): +parse_options_baset(TWOLS_OPTIONS, argc, argv), +language_uit(cmdline, ui_message_handler), + ui_message_handler(cmdline, "2LS " TWOLS_VERSION), + recursion_detected(false), + threads_detected(false) { } - + /*******************************************************************\ -Function: summarizer_parse_optionst::eval_verbosity +Function: twols_parse_optionst::eval_verbosity Inputs: @@ -87,11 +96,11 @@ Function: summarizer_parse_optionst::eval_verbosity \*******************************************************************/ -void summarizer_parse_optionst::eval_verbosity() +void twols_parse_optionst::eval_verbosity() { // this is our default verbosity int v=messaget::M_STATISTICS; - + if(cmdline.isset("verbosity")) { v=unsafe_string2int(cmdline.get_value("verbosity")); @@ -100,13 +109,13 @@ void summarizer_parse_optionst::eval_verbosity() else if(v>10) v=10; } - + ui_message_handler.set_verbosity(v); } /*******************************************************************\ -Function: summarizer_parse_optionst::get_command_line_options +Function: twols_parse_optionst::get_command_line_options Inputs: @@ -116,7 +125,7 @@ Function: summarizer_parse_optionst::get_command_line_options \*******************************************************************/ -void summarizer_parse_optionst::get_command_line_options(optionst &options) +void twols_parse_optionst::get_command_line_options(optionst &options) { if(config.set(cmdline)) { @@ -147,53 +156,8 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) else options.set_option("slice", false); - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check overflow on floats - if(cmdline.isset("float-overflow-check")) - options.set_option("float-overflow-check", true); - else - options.set_option("float-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // check for memory leaks - if(cmdline.isset("memory-leak-check")) - options.set_option("memory-leak-check", true); - else - options.set_option("memory-leak-check", false); + // all checks supported by goto_check + GOTO_CHECK_PARSE_OPTIONS(cmdline, options); // check assertions if(cmdline.isset("no-assertions")) @@ -230,7 +194,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("equalities", true); options.set_option("std-invariants", true); } - else + else { if(cmdline.isset("zones")) options.set_option("zones", true); @@ -238,12 +202,12 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("qzones", true); else if(cmdline.isset("octagons")) options.set_option("octagons", true); - else //if(cmdline.isset("intervals")) //default + else // if(cmdline.isset("intervals")) // default options.set_option("intervals", true); - if(cmdline.isset("enum-solver")) + if(cmdline.isset("enum-solver")) options.set_option("enum-solver", true); - else //if(cmdline.isset("binsearch-solver")) //default + else // if(cmdline.isset("binsearch-solver")) // default options.set_option("binsearch-solver", true); } @@ -273,50 +237,62 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) { options.set_option("monolithic-ranking-function", true); } - else options.set_option("monolithic-ranking-function", false); + else + options.set_option("monolithic-ranking-function", false); if(cmdline.isset("lexicographic-ranking-function")) { - options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); + options.set_option( + "lexicographic-ranking-function", + cmdline.get_value("lexicographic-ranking-function")); } - else options.set_option("lexicographic-ranking-function",3); + else + options.set_option("lexicographic-ranking-function", 5); if(cmdline.isset("max-inner-ranking-iterations")) { - options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); + options.set_option( + "max-inner-ranking-iterations", + cmdline.get_value("max-inner-ranking-iterations")); } - else options.set_option("max-inner-ranking-iterations",20); + else + options.set_option("max-inner-ranking-iterations", 50); - // do k-induction refinement + // do k-induction refinement if(cmdline.isset("k-induction")) { options.set_option("std-invariants", true); options.set_option("k-induction", true); options.set_option("inline", true); if(!cmdline.isset("unwind")) - options.set_option("unwind",UINT_MAX); + options.set_option("unwind", UINT_MAX); } - // do incremental bmc + // do incremental bmc if(cmdline.isset("incremental-bmc")) { options.set_option("incremental-bmc", true); options.set_option("inline", true); options.set_option("havoc", true); if(!cmdline.isset("unwind")) - options.set_option("unwind",UINT_MAX); + options.set_option("unwind", UINT_MAX); } // check for spuriousness of assertion failures + /* if(cmdline.isset("no-spurious-check")) options.set_option("spurious-check", false); else options.set_option("spurious-check", true); + */ + + if(cmdline.isset("spurious-check")) + options.set_option("spurious-check", cmdline.get_value("spurious-check")); + else + options.set_option("spurious-check", "all"); // all properties (default) - if(cmdline.isset("no-all-properties")) + if(cmdline.isset("stop-on-fail")) options.set_option("all-properties", false); else options.set_option("all-properties", true); @@ -332,13 +308,15 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) { options.set_option("competition-mode", true); options.set_option("all-properties", false); + options.set_option("inline", true); } // instrumentation / output if(cmdline.isset("instrument-output")) - options.set_option("instrument-output", - cmdline.get_value("instrument-output")); - + options.set_option( + "instrument-output", + cmdline.get_value("instrument-output")); + #ifdef SHOW_CALLING_CONTEXTS if(cmdline.isset("show-calling-contexts")) @@ -346,22 +324,23 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) if(!options.get_bool_option("intervals")) throw "--show-calling-contexts only possible with --intervals"; options.set_option("show-calling-contexts", true); - options.set_option("do-not-analyze-functions", - cmdline.get_value("show-calling-contexts")); + options.set_option( + "do-not-analyze-functions", + cmdline.get_value("show-calling-contexts")); } #endif if(cmdline.isset("show-trace")) options.set_option("show-trace", true); - if(cmdline.isset("graphml-cex")) - options.set_option("graphml-cex", cmdline.get_value("graphml-cex")); + if(cmdline.isset("graphml-witness")) + options.set_option("graphml-witness", cmdline.get_value("graphml-witness")); if(cmdline.isset("json-cex")) options.set_option("json-cex", cmdline.get_value("json-cex")); } /*******************************************************************\ -Function: summarizer_parse_optionst::doit +Function: twols_parse_optionst::doit Inputs: @@ -371,14 +350,14 @@ Function: summarizer_parse_optionst::doit \*******************************************************************/ -int summarizer_parse_optionst::doit() +int twols_parse_optionst::doit() { if(cmdline.isset("version")) { - std::cout << SUMMARIZER_VERSION << std::endl; + std::cout << TWOLS_VERSION << std::endl; return 0; } - + // // command line options // @@ -387,17 +366,17 @@ int summarizer_parse_optionst::doit() get_command_line_options(options); eval_verbosity(); - + // // Print a banner // - status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; - - register_language(new_ansi_c_language); - register_language(new_cpp_language); + status() << "2LS version " TWOLS_VERSION " (based on CBMC " CBMC_VERSION ")" + << eom; goto_modelt goto_model; + register_languages(); + if(get_goto_program(options, goto_model)) return 6; @@ -408,7 +387,7 @@ int summarizer_parse_optionst::doit() } // options for various debug outputs - + if(cmdline.isset("show-ssa")) { bool simplify=!cmdline.isset("no-simplify"); @@ -417,14 +396,6 @@ int summarizer_parse_optionst::doit() return 7; } - if(cmdline.isset("show-fixed-points")) - { - bool simplify=!cmdline.isset("no-simplify"); - irep_idt function=cmdline.get_value("function"); - show_fixed_points(goto_model, function, simplify, std::cout, ui_message_handler); - return 7; - } - if(cmdline.isset("show-defs")) { irep_idt function=cmdline.get_value("function"); @@ -445,7 +416,7 @@ int summarizer_parse_optionst::doit() show_guards(goto_model, function, std::cout, ui_message_handler); return 7; } - + if(cmdline.isset("show-value-sets")) { irep_idt function=cmdline.get_value("function"); @@ -453,15 +424,33 @@ int summarizer_parse_optionst::doit() return 7; } - if(cmdline.isset("show-invariants")) + if(cmdline.isset("show-invariants")) { options.set_option("show-invariants", true); } +#if IGNORE_RECURSION + if(recursion_detected) + { + status() << "Recursion not supported" << eom; + report_unknown(); + return 5; + } +#endif + +#if IGNORE_THREADS + if(threads_detected) + { + status() << "Threads not supported" << eom; + report_unknown(); + return 5; + } +#endif + if(cmdline.isset("context-sensitive")) { options.set_option("context-sensitive", true); - status() << "Context-sensitive analysis from " << + status() << "Context-sensitive analysis from " << goto_model.goto_functions.entry_point() << eom; } @@ -471,7 +460,7 @@ int summarizer_parse_optionst::doit() status() << "Do not ignore array contents" << eom; } - //TODO: check option inconsistencies, ignored options etc + // TODO: check option inconsistencies, ignored options etc if(options.get_bool_option("havoc")) status() << "Havocking loops and function calls" << eom; else if(options.get_bool_option("equalities")) @@ -484,39 +473,45 @@ int summarizer_parse_optionst::doit() status() << "Using zones domain"; else if(options.get_bool_option("octagons")) status() << "Using octagons domain"; - else assert(false); + else + assert(false); + if(options.get_bool_option("enum-solver")) status() << " with enumeration solver"; else if(options.get_bool_option("binsearch-solver")) status() << " with binary search solver"; - else assert(false); + else + assert(false); + status() << eom; } - + try { - summary_checker_baset *summary_checker = NULL; - if(!options.get_bool_option("k-induction") && + std::unique_ptr checker; + if(!options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_ait(options); - if(options.get_bool_option("k-induction") && - !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_kindt(options); - if(!options.get_bool_option("k-induction") && - options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_bmct(options); - - summary_checker->set_message_handler(get_message_handler()); - summary_checker->simplify=!cmdline.isset("no-simplify"); - summary_checker->fixed_point=!cmdline.isset("no-fixed-point"); + checker=std::unique_ptr( + new summary_checker_ait(options)); + if(options.get_bool_option("k-induction") && + !options.get_bool_option("incremental-bmc")) + checker=std::unique_ptr( + new summary_checker_kindt(options)); + if(!options.get_bool_option("k-induction") && + options.get_bool_option("incremental-bmc")) + checker=std::unique_ptr( + new summary_checker_bmct(options)); + + checker->set_message_handler(get_message_handler()); + checker->simplify=!cmdline.isset("no-simplify"); + checker->fixed_point=!cmdline.isset("no-fixed-point"); int retval; if(cmdline.isset("show-vcc")) { std::cout << "VERIFICATION CONDITIONS:\n\n"; - summary_checker->show_vcc=true; - (*summary_checker)(goto_model); - delete summary_checker; + checker->show_vcc=true; + (*checker)(goto_model); return 0; } @@ -524,76 +519,80 @@ int summarizer_parse_optionst::doit() { status() << "Horn-clause encoding" << eom; namespacet ns(symbol_table); - + std::string out_file=cmdline.get_value("horn-encoding"); - + if(out_file=="-") { horn_encoding(goto_model, std::cout); } else { - #ifdef _MSC_VER +#ifdef _MSC_VER std::ofstream out(widen(out_file).c_str()); - #else +#else std::ofstream out(out_file.c_str()); - #endif - +#endif + if(!out) { error() << "Failed to open output file " << out_file << eom; - delete summary_checker; return 1; } - + horn_encoding(goto_model, out); } - - delete summary_checker; + return 0; } - - bool report_assertions = + + bool report_assertions= !options.get_bool_option("preconditions") && !options.get_bool_option("termination"); // do actual analysis - switch((*summary_checker)(goto_model)) + switch((*checker)(goto_model)) { case property_checkert::PASS: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); + if(report_assertions) + report_properties(options, goto_model, checker->property_map); report_success(); - retval = 0; + if(cmdline.isset("graphml-witness") && + !options.get_bool_option("termination")) + output_graphml_proof(options, goto_model, *checker); + retval=0; break; - + case property_checkert::FAIL: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); + if(report_assertions) + report_properties(options, goto_model, checker->property_map); report_failure(); - retval = 10; + if(cmdline.isset("graphml-witness")) + { + output_graphml_cex(options, goto_model, *checker); + } + retval=10; break; case property_checkert::UNKNOWN: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); - retval = 5; + if(report_assertions) + report_properties(options, goto_model, checker->property_map); + retval=5; report_unknown(); break; - + default: assert(false); } if(cmdline.isset("instrument-output")) { - summary_checker->instrument_and_output(goto_model); + checker->instrument_and_output(goto_model); } - delete summary_checker; return retval; } - + catch(const std::string error_msg) { error() << error_msg << messaget::eom; @@ -606,51 +605,60 @@ int summarizer_parse_optionst::doit() return 8; } - #if 0 +#if 0 // let's log some more statistics debug() << "Memory consumption:" << messaget::endl; memory_info(debug()); debug() << eom; - #endif +#endif } +/*******************************************************************\ +Function: twols_parse_optionst::type_stats_rec -void summarizer_parse_optionst::type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns) -{ + Inputs: + + Outputs: + + Purpose: +\*******************************************************************/ + +void twols_parse_optionst::type_stats_rec( + const typet &type, + expr_statst &stats, + const namespacet &ns) +{ if(type.id()==ID_symbol) type_stats_rec(ns.follow(type), stats, ns); - - if(type.id()==ID_pointer || type.id()==ID_array) - { - stats.has_array=true; - - const typet &subtype=ns.follow(type.subtype()); - - if(subtype.id()==ID_signedbv || + + if(type.id()==ID_pointer || type.id()==ID_array) + { + stats.has_array=true; + + const typet &subtype=ns.follow(type.subtype()); + + if(subtype.id()==ID_signedbv || subtype.id()==ID_unsignedbv) { - stats.has_string=(to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); - } - } - + stats.has_string= + (to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); + } + } + if(type.has_subtypes()) { - forall_subtypes(it, type) - { - type_stats_rec(*it, stats, ns); - } - } + forall_subtypes(it, type) + { + type_stats_rec(*it, stats, ns); + } + } } - /*******************************************************************\ -Function: summarizer_parse_optionst::expr_stats_rec +Function: twols_parse_optionst::expr_stats_rec Inputs: @@ -660,12 +668,10 @@ Function: summarizer_parse_optionst::expr_stats_rec \*******************************************************************/ - -void summarizer_parse_optionst::expr_stats_rec( - const exprt &expr, - expr_statst &stats) +void twols_parse_optionst::expr_stats_rec( + const exprt &expr, + expr_statst &stats) { - if(expr.id()==ID_side_effect) { const side_effect_exprt &side_effect_expr=to_side_effect_expr(expr); @@ -681,24 +687,19 @@ void summarizer_parse_optionst::expr_stats_rec( } } - if(expr.id()==ID_symbol ) - { - - } - if(expr.has_operands()) { forall_operands(it, expr) { - expr_stats_rec(*it, stats); + expr_stats_rec(*it, stats); } } } - + /*******************************************************************\ -Function: summarizer_parse_optionst::show_stats +Function: twols_parse_optionst::show_stats Inputs: @@ -708,10 +709,10 @@ Function: summarizer_parse_optionst::show_stats \*******************************************************************/ -void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) +void twols_parse_optionst::show_stats( + const goto_modelt &goto_model, + std::ostream &out) { - const namespacet ns(goto_model.symbol_table); expr_statst stats; @@ -723,63 +724,66 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, // analyze all the functions forall_goto_functions(f_it, goto_model.goto_functions) { - if(!f_it->second.body_available()) continue; + if(!f_it->second.body_available()) + continue; ++nr_functions; const goto_programt &goto_program=f_it->second.body; #if 0 - statistics() << "function size of " << f_it->first << ": " + statistics() << "function size of " << f_it->first << ": " << goto_program.instructions.size() << eom; #endif for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) { nr_instructions++; const goto_programt::instructiont &instruction=*i_it; - if(i_it->is_backwards_goto()) nr_loops++; + if(i_it->is_backwards_goto()) + nr_loops++; switch(instruction.type) { - case ASSIGN: - { - const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); - } - break; - case ASSUME: - expr_stats_rec(instruction.guard, stats); - break; - case ASSERT: - expr_stats_rec(instruction.guard, stats); - break; - case GOTO: - expr_stats_rec(instruction.guard, stats); - break; - - case DECL: - // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - - break; - - default: - // skip - break; + case ASSIGN: + { + const code_assignt &assign=to_code_assign(instruction.code); + expr_stats_rec(assign.lhs(), stats); + expr_stats_rec(assign.rhs(), stats); + } + break; + case ASSUME: + expr_stats_rec(instruction.guard, stats); + break; + case ASSERT: + expr_stats_rec(instruction.guard, stats); + break; + case GOTO: + expr_stats_rec(instruction.guard, stats); + break; + + case DECL: + // someone declaring an array + type_stats_rec( + to_code_decl(instruction.code).symbol().type(), stats, ns); + + break; + + default: + // skip + break; } // switch } // forall instructions } // forall functions - out << " =============== STATS =============== " << std::endl; - out << " nr of instructions: " << nr_instructions << std::endl; - out << " nr of functions: " << nr_functions << std::endl; - out << " nr of loops: " << nr_loops << std::endl; + out << "=============== STATS=============== " << std::endl; + out << " nr of instructions: " << nr_instructions << std::endl; + out << " nr of functions: " << nr_functions << std::endl; + out << " nr of loops: " << nr_loops << std::endl; out << " malloc: " << (stats.has_malloc ? "YES" : "NO") << std::endl; out << " arrays: " << (stats.has_array ? "YES" : "NO") << std::endl; out << " strings: " << (stats.has_string ? "YES" : "NO") << std::endl; @@ -788,7 +792,7 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, /*******************************************************************\ -Function: summarizer_parse_optionst::set_properties +Function: twols_parse_optionst::set_properties Inputs: @@ -798,7 +802,7 @@ Function: summarizer_parse_optionst::set_properties \*******************************************************************/ -bool summarizer_parse_optionst::set_properties(goto_modelt &goto_model) +bool twols_parse_optionst::set_properties(goto_modelt &goto_model) { try { @@ -817,18 +821,18 @@ bool summarizer_parse_optionst::set_properties(goto_modelt &goto_model) error() << e << eom; return true; } - + catch(int) { return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::require_entry +Function: twols_parse_optionst::require_entry Inputs: @@ -837,19 +841,20 @@ Function: summarizer_parse_optionst::require_entry Purpose: \*******************************************************************/ - -void summarizer_parse_optionst::require_entry( + +void twols_parse_optionst::require_entry( const goto_modelt &goto_model) { irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(goto_model.symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) - throw "The program has no entry point; please complete linking"; + + if(goto_model.symbol_table.symbols.find(entry_point)== + symbol_table.symbols.end()) + throw "the program has no entry point; please complete linking"; } /*******************************************************************\ -Function: summarizer_parse_optionst::get_goto_program +Function: twols_parse_optionst::get_goto_program Inputs: @@ -858,8 +863,8 @@ Function: summarizer_parse_optionst::get_goto_program Purpose: \*******************************************************************/ - -bool summarizer_parse_optionst::get_goto_program( + +bool twols_parse_optionst::get_goto_program( const optionst &options, goto_modelt &goto_model) { @@ -876,10 +881,10 @@ bool summarizer_parse_optionst::get_goto_program( { status() << "Reading GOTO program from file" << eom; - if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) + if(read_goto_binary( + cmdline.args[0], goto_model, get_message_handler())) return true; - + config.set_from_symbol_table(goto_model.symbol_table); if(cmdline.isset("show-symbol-table")) @@ -895,48 +900,51 @@ bool summarizer_parse_optionst::get_goto_program( error() << "Please give one source file only" << eom; return true; } - + std::string filename=cmdline.args[0]; - - #ifdef _MSC_VER + +#ifdef _MSC_VER std::ifstream infile(widen(filename).c_str()); - #else +#else std::ifstream infile(filename.c_str()); - #endif - +#endif + if(!infile) { error() << "failed to open input file `" << filename << "'" << eom; return true; } - + languaget *language=get_language_from_filename(filename); - + if(language==NULL) { - error() << "failed to figure out type of file `" << filename << "'" << eom; + error() << "failed to figure out type of file `" << filename << "'" + << eom; return true; } - + language->set_message_handler(get_message_handler()); - + status("Parsing", filename); - + if(language->parse(infile, filename)) { error() << "PARSING ERROR" << eom; return true; } - + language->show_parse(std::cout); return true; } else { - - if(parse()) return true; - if(typecheck()) return true; - if(final()) return true; + if(parse()) + return true; + if(typecheck()) + return true; + if(final()) + return true; // we no longer need any parse trees or language files clear_parse(); @@ -947,16 +955,16 @@ bool summarizer_parse_optionst::get_goto_program( return true; } - #if 0 +#if 0 irep_idt entry_point=goto_model.goto_functions.entry_point(); - + if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) { error() << "No entry point; please provide a main function" << eom; return true; } - #endif - +#endif + status() << "Generating GOTO Program" << eom; goto_convert(symbol_table, goto_model, ui_message_handler); @@ -981,24 +989,24 @@ bool summarizer_parse_optionst::get_goto_program( error() << e << eom; return true; } - + catch(int) { return true; } - + catch(std::bad_alloc) { error() << "Out of memory" << eom; return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::process_goto_program +Function: twols_parse_optionst::process_goto_program Inputs: @@ -1007,73 +1015,80 @@ Function: summarizer_parse_optionst::process_goto_program Purpose: \*******************************************************************/ - -bool summarizer_parse_optionst::process_goto_program( + +bool twols_parse_optionst::process_goto_program( const optionst &options, goto_modelt &goto_model) { try { + status() << "Function Pointer Removal" << eom; + remove_function_pointers( + goto_model, cmdline.isset("pointer-check")); + // do partial inlining if(options.get_bool_option("inline-partial")) { - unsigned limit = options.get_unsigned_int_option("inline-partial"); + unsigned limit=options.get_unsigned_int_option("inline-partial"); status() << "Performing partial inlining (" << limit << ")" << eom; goto_partial_inline(goto_model, ui_message_handler, limit/2); - //TODO: where is limit multiplied by 2??? + // TODO: where is limit multiplied by 2??? - //remove inlined functions + // remove inlined functions Forall_goto_functions(f_it, goto_model.goto_functions) if(f_it->first!=ID__start && f_it->second.body.instructions.size()<=2*(limit/2)) - { + { f_it->second.body.clear(); - } + } } - - // add generic checks - status() << "Generic Property Instrumentation" << eom; - goto_check(options, goto_model); - status() << "Function Pointer Removal" << eom; - remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); +#if IGNORE_THREADS + threads_detected=has_threads(goto_model); +#endif // remove returns (must be done after function pointer removal) remove_returns(goto_model); - - -#if UNWIND_GOTO_INTO_LOOP - goto_unwind(goto_model,2); -#endif - - remove_skip(goto_model.goto_functions); - goto_model.goto_functions.update(); // now do full inlining, if requested if(options.get_bool_option("inline")) { status() << "Performing full inlining" << eom; - if(goto_inline(goto_model, ui_message_handler)) - { - report_unknown(); - return 5; - } + const namespacet ns(goto_model.symbol_table); + goto_inlinet goto_inline( + goto_model.goto_functions, ns, ui_message_handler); + goto_inline(); +#if IGNORE_RECURSION + recursion_detected=goto_inline.recursion_detected(); +#endif } - //preprocessing to improve the structure of the SSA for the unwinder +#if REMOVE_MULTIPLE_DEREFERENCES + remove_multiple_dereferences(goto_model); +#endif + + // add generic checks + status() << "Generic Property Instrumentation" << eom; + goto_check(options, goto_model); + +#if UNWIND_GOTO_INTO_LOOP + unwind_goto_into_loop(goto_model, 2); +#endif + + remove_skip(goto_model.goto_functions); + goto_model.goto_functions.update(); + + // preprocessing to improve the structure of the SSA for the unwinder split_loopheads(goto_model); - //explicitly initialize all local variables +#if EXPLICIT_NONDET_LOCALS + // explicitly initialize all local variables nondet_locals(goto_model); - -#if 1 - //TODO: find a better place for that - replace_malloc(goto_model,""); #endif -#if REMOVE_MULTIPLE_DEREFERENCES - remove_multiple_dereferences(goto_model); +#if 1 + // TODO: find a better place for that + replace_malloc(goto_model, ""); #endif // recalculate numbers, etc. @@ -1082,12 +1097,23 @@ bool summarizer_parse_optionst::process_goto_program( // add loop ids goto_model.goto_functions.compute_loop_numbers(); - //inline __CPROVER_initialize and main + // inline __CPROVER_initialize and main if(cmdline.isset("inline-main")) { - inline_main(goto_model); + inline_main(goto_model); } +#ifdef ASSUME_AFTER_ASSERT + if(!cmdline.isset("independent-properties")) + { + add_assumptions_after_assertions(goto_model); + } +#endif + +#ifdef FILTER_ASSERTIONS + filter_assertions(goto_model); +#endif + if(!cmdline.isset("no-propagation")) { status() << "Constant Propagation" << eom; @@ -1106,14 +1132,6 @@ bool summarizer_parse_optionst::process_goto_program( return true; } - // do array abstraction - if(cmdline.isset("array-abstraction")) - { - status() << "Performing array abstraction" << eom; - array_abstraction(goto_model.symbol_table, ui_message_handler, - goto_model.goto_functions); - } - label_properties(goto_model); if(cmdline.isset("show-properties")) @@ -1145,24 +1163,24 @@ bool summarizer_parse_optionst::process_goto_program( error() << e << eom; return true; } - + catch(int) { return true; } - + catch(std::bad_alloc) { error() << "Out of memory" << eom; return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::report_properties +Function: twols_parse_optionst::report_properties Inputs: @@ -1172,24 +1190,32 @@ Function: summarizer_parse_optionst::report_properties \*******************************************************************/ -void summarizer_parse_optionst::report_properties( +void twols_parse_optionst::report_properties( const optionst &options, const goto_modelt &goto_model, const property_checkert::property_mapt &property_map) { for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { - if(it->first=="") //TODO: some properties do not show up in initialize_property_map - continue; +#if 1 + // TODO: some properties do not show up in initialize_property_map + if(it->first=="") + continue; +#endif + + if(!options.get_bool_option("all-properties") && + it->second.result!=property_checkert::FAIL) + continue; if(get_ui()==ui_message_handlert::XML_UI) { xmlt xml_result("result"); xml_result.set_attribute("property", id2string(it->first)); - xml_result.set_attribute("status", property_checkert::as_string(it->second.result)); + xml_result.set_attribute( + "status", property_checkert::as_string(it->second.result)); std::cout << xml_result << "\n"; } else @@ -1204,14 +1230,13 @@ void summarizer_parse_optionst::report_properties( if(cmdline.isset("show-trace") && it->second.result==property_checkert::FAIL) show_counterexample(goto_model, it->second.error_trace); - if(cmdline.isset("graphml-cex") && - it->second.result==property_checkert::FAIL) - output_graphml_cex(options,goto_model, it->second.error_trace); if(cmdline.isset("json-cex") && it->second.result==property_checkert::FAIL) - output_json_cex(options, - goto_model, it->second.error_trace, - id2string(it->first)); + output_json_cex( + options, + goto_model, + it->second.error_trace, + id2string(it->first)); } if(!cmdline.isset("property")) @@ -1222,7 +1247,7 @@ void summarizer_parse_optionst::report_properties( unsigned unknown=0; for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { @@ -1231,19 +1256,19 @@ void summarizer_parse_optionst::report_properties( if(it->second.result==property_checkert::FAIL) failed++; } - + status() << "** " << unknown << " of " << property_map.size() << " unknown" - << eom; + << eom; status() << "** " << failed << " of " << property_map.size() << " failed" - << eom; + << eom; } } /*******************************************************************\ -Function: summarizer_parse_optionst::report_success +Function: twols_parse_optionst::report_success Inputs: @@ -1253,7 +1278,7 @@ Function: summarizer_parse_optionst::report_success \*******************************************************************/ -void summarizer_parse_optionst::report_success() +void twols_parse_optionst::report_success() { result() << "VERIFICATION SUCCESSFUL" << eom; @@ -1261,16 +1286,16 @@ void summarizer_parse_optionst::report_success() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="SUCCESS"; - std::cout << xml; - std::cout << std::endl; - } - break; - + { + xmlt xml("cprover-status"); + xml.data="SUCCESS"; + std::cout << xml; + std::cout << std::endl; + } + break; + default: assert(false); } @@ -1278,7 +1303,7 @@ void summarizer_parse_optionst::report_success() /*******************************************************************\ -Function: summarizer_parse_optionst::show_counterexample +Function: twols_parse_optionst::show_counterexample Inputs: @@ -1288,7 +1313,7 @@ Function: summarizer_parse_optionst::show_counterexample \*******************************************************************/ -void summarizer_parse_optionst::show_counterexample( +void twols_parse_optionst::show_counterexample( const goto_modelt &goto_model, const goto_tracet &error_trace) { @@ -1300,23 +1325,64 @@ void summarizer_parse_optionst::show_counterexample( std::cout << std::endl << "Counterexample:" << std::endl; show_goto_trace(std::cout, ns, error_trace); break; - + case ui_message_handlert::XML_UI: + { + xmlt xml; + convert(ns, error_trace, xml); + std::cout << xml << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::output_graphml_cex + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::output_graphml_cex( + const optionst &options, + const goto_modelt &goto_model, + const summary_checker_baset &summary_checker) +{ + for(const auto &p : summary_checker.property_map) + { + if(p.second.result!=property_checkert::FAIL) + continue; + + const namespacet ns(goto_model.symbol_table); + const std::string graphml=options.get_option("graphml-witness"); + if(!graphml.empty()) { - xmlt xml; - convert(ns, error_trace, xml); - std::cout << xml << std::endl; + graphml_witnesst graphml_witness(ns); + graphml_witness(p.second.error_trace); + + if(graphml=="-") + write_graphml(graphml_witness.graph(), std::cout); + else + { + std::ofstream out(graphml.c_str()); + write_graphml(graphml_witness.graph(), out); + } } break; - - default: - assert(false); } } /*******************************************************************\ -Function: summarizer_parse_optionst::output_graphml_cex +Function: twols_parse_optionst::output_graphml_proof Inputs: @@ -1326,31 +1392,30 @@ Function: summarizer_parse_optionst::output_graphml_cex \*******************************************************************/ -void summarizer_parse_optionst::output_graphml_cex( +void twols_parse_optionst::output_graphml_proof( const optionst &options, const goto_modelt &goto_model, - const goto_tracet &error_trace) + const summary_checker_baset &summary_checker) { const namespacet ns(goto_model.symbol_table); - const std::string graphml=options.get_option("graphml-cex"); + const std::string graphml=options.get_option("graphml-witness"); if(!graphml.empty()) { - graphmlt cex_graph; - convert(ns, error_trace, cex_graph); + graphml_witness_extt graphml_witness(ns); + graphml_witness(summary_checker); if(graphml=="-") - write_graphml(cex_graph, std::cout); + write_graphml(graphml_witness.graph(), std::cout); else { std::ofstream out(graphml.c_str()); - write_graphml(cex_graph, out); + write_graphml(graphml_witness.graph(), out); } } } - /*******************************************************************\ -Function: summarizer_parse_optionst::output_json_cex +Function: twols_parse_optionst::output_json_cex Inputs: @@ -1360,7 +1425,7 @@ Function: summarizer_parse_optionst::output_json_cex \*******************************************************************/ -void summarizer_parse_optionst::output_json_cex( +void twols_parse_optionst::output_json_cex( const optionst &options, const goto_modelt &goto_model, const goto_tracet &error_trace, @@ -1371,22 +1436,23 @@ void summarizer_parse_optionst::output_json_cex( const namespacet ns(goto_model.symbol_table); jsont json_trace; convert(ns, error_trace, json_trace); - + if(options.get_option("json-cex")=="-") { std::cout << json_trace; } else { - std::ofstream out((options.get_option("json-cex")+"-"+property_id+".json").c_str()); + std::ofstream out( + (options.get_option("json-cex")+"-"+property_id+".json").c_str()); out << json_trace << '\n'; } - } + } } /*******************************************************************\ -Function: summarizer_parse_optionst::report_failure +Function: twols_parse_optionst::report_failure Inputs: @@ -1396,7 +1462,7 @@ Function: summarizer_parse_optionst::report_failure \*******************************************************************/ -void summarizer_parse_optionst::report_failure() +void twols_parse_optionst::report_failure() { result() << "VERIFICATION FAILED" << eom; @@ -1404,16 +1470,16 @@ void summarizer_parse_optionst::report_failure() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="FAILURE"; - std::cout << xml; - std::cout << std::endl; - } - break; - + { + xmlt xml("cprover-status"); + xml.data="FAILURE"; + std::cout << xml; + std::cout << std::endl; + } + break; + default: assert(false); } @@ -1421,7 +1487,7 @@ void summarizer_parse_optionst::report_failure() /*******************************************************************\ -Function: summarizer_parse_optionst::report_unknown +Function: twols_parse_optionst::report_unknown Inputs: @@ -1431,7 +1497,7 @@ Function: summarizer_parse_optionst::report_unknown \*******************************************************************/ -void summarizer_parse_optionst::report_unknown() +void twols_parse_optionst::report_unknown() { result() << "VERIFICATION INCONCLUSIVE" << eom; @@ -1439,16 +1505,16 @@ void summarizer_parse_optionst::report_unknown() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="UNKNOWN"; - std::cout << xml; - std::cout << std::endl; - } - break; - + { + xmlt xml("cprover-status"); + xml.data="UNKNOWN"; + std::cout << xml; + std::cout << std::endl; + } + break; + default: assert(false); } @@ -1456,7 +1522,7 @@ void summarizer_parse_optionst::report_unknown() /*******************************************************************\ -Function: summarizer_parse_optionst::help +Function: twols_parse_optionst::help Inputs: @@ -1466,16 +1532,16 @@ Function: summarizer_parse_optionst::help \*******************************************************************/ -void summarizer_parse_optionst::help() +void twols_parse_optionst::help() { std::cout << "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2015 * *\n" + "* * 2LS " TWOLS_VERSION "-Copyright (C) 2014-2017 * *\n" // NOLINT(*) "* * (based on CBMC " CBMC_VERSION " "; std::cout << "(" << (sizeof(void *)*8) << "-bit version))"; - + std::cout << " * *\n"; - + std::cout << "* * Daniel Kroening, Peter Schrammel * *\n" "* * University of Oxford * *\n" @@ -1483,15 +1549,15 @@ void summarizer_parse_optionst::help() "\n" "Usage: Purpose:\n" "\n" - " summarizer [-?] [-h] [--help] show help\n" - " summarizer file.c ... source file names\n" + " 2ls [-?] [-h] [--help] show help\n" + " 2ls file.c ... source file names\n" "\n" "Frontend options:\n" " -I path set include path (C/C++)\n" " -D macro define preprocessor macro (C/C++)\n" " --preprocess stop after preprocessing\n" " --16, --32, --64 set width of int\n" - " --LP64, --ILP64, --LLP64,\n" + " --LP64, --ILP64, --LLP64, \n" " --ILP32, --LP32 set width of int, long and pointers\n" " --little-endian allow little-endian word-byte conversions\n" " --big-endian allow big-endian word-byte conversions\n" @@ -1500,45 +1566,36 @@ void summarizer_parse_optionst::help() " --show-parse-tree show parse tree\n" " --show-symbol-table show symbol table\n" " --show-goto-functions show goto program\n" - " --arch set architecture (default: " - << configt::this_architecture() << ")\n" - " --os set operating system (default: " - << configt::this_operating_system() << ")\n" - #ifdef _WIN32 + " --arch set architecture (default: " << configt::this_architecture() << ")\n" // NOLINT(*) + " --os set operating system (default: " << configt::this_operating_system() << ")\n" // NOLINT(*) +#ifdef _WIN32 " --gcc use GCC as preprocessor\n" - #endif +#endif " --no-arch don't set up an architecture\n" " --no-library disable built-in abstract C library\n" - " --round-to-nearest IEEE floating point rounding mode (default)\n" + " --round-to-nearest IEEE floating point rounding mode (default)\n" // NOLINT(*) " --round-to-plus-inf IEEE floating point rounding mode\n" " --round-to-minus-inf IEEE floating point rounding mode\n" " --round-to-zero IEEE floating point rounding mode\n" "\n" "Program instrumentation options:\n" - " --bounds-check enable array bounds checks\n" - " --pointer-check enable pointer checks\n" - " --array-abstraction use array and string abstraction for bounds checks\n" - " --div-by-zero-check enable division by zero checks\n" - " --memory-leak-check enable memory leak checks\n" - " --signed-overflow-check enable arithmetic over- and underflow checks\n" - " --unsigned-overflow-check enable arithmetic over- and underflow checks\n" - " --nan-check check floating-point for NaN\n" + GOTO_CHECK_HELP " --error-label label check that label is unreachable\n" " --show-properties show the properties\n" " --no-assertions ignore user assertions\n" " --no-assumptions ignore user assumptions\n" " --inline inline all functions into main\n" - " --inline-partial nr inline functions smaller than the given nr of instructions\n" + " --inline-partial nr inline functions smaller than the given nr of instructions\n" // NOLINT(*) "\n" "Backend options:\n" " --all-functions check each function as entry point\n" - " --no-all-properties stop on first failing assertion\n" - " --context-sensitive context-sensitive analysis from entry point\n" - " --termination compute ranking functions to prove termination\n" + " --stop-on-fail stop on first failing assertion\n" + " --context-sensitive context-sensitive analysis from entry point\n" // NOLINT(*) + " --termination compute ranking functions to prove termination\n" // NOLINT(*) " --k-induction use k-induction\n" " --incremental-bmc use incremental-bmc\n" " --preconditions compute preconditions\n" - " --sufficient sufficient preconditions (default: necessary)\n" + " --sufficient sufficient preconditions (default: necessary)\n" // NOLINT(*) " --havoc havoc loops and function calls\n" " --intervals use interval domain\n" " --equalities use equalities and disequalities domain\n" diff --git a/src/summarizer/summarizer_parse_options.h b/src/2ls/2ls_parse_options.h similarity index 63% rename from src/summarizer/summarizer_parse_options.h rename to src/2ls/2ls_parse_options.h index 944755af9..1a2037028 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -1,13 +1,13 @@ /*******************************************************************\ -Module: Command Line Parsing +Module: 2LS Command Line Parsing -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_PARSE_OPTIONS_H -#define CPROVER_SUMMARIZER_PARSE_OPTIONS_H +#ifndef CPROVER_2LS_2LS_2LS_PARSE_OPTIONS_H +#define CPROVER_2LS_2LS_2LS_PARSE_OPTIONS_H #include #include @@ -15,20 +15,19 @@ Author: Daniel Kroening, kroening@kroening.com #include +#include + class goto_modelt; class optionst; #include "summary_checker_base.h" -#define SUMMARIZER_OPTIONS \ +#define TWOLS_OPTIONS \ "(xml-ui)" \ "(function):" \ "D:I:" \ "(depth):(context-bound):(unwind):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)(memory-leak-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)" \ - "(float-overflow-check)(nan-check)" \ - "(array-abstraction)" \ + GOTO_CHECK_OPTIONS \ "(non-incremental)" \ "(no-assertions)(no-assumptions)" \ "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ @@ -46,19 +45,20 @@ class optionst; "(lexicographic-ranking-function):(monolithic-ranking-function)" \ "(max-inner-ranking-iterations):" \ "(preconditions)(sufficient)" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-fixed-points)(show-stats)" \ + "(show-locs)(show-vcc)(show-properties)(show-trace)(show-stats)" \ "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ "(no-spurious-check)(all-functions)" \ "(no-simplify)(no-fixed-point)" \ - "(graphml-cex):(json-cex):" \ - "(no-spurious-check)(no-all-properties)" \ - "(competition-mode)(slice)(no-propagation)" \ + "(graphml-witness):(json-cex):" \ + "(no-spurious-check)(stop-on-fail)" \ + "(spurious-check):" \ + "(competition-mode)(slice)(no-propagation)(independent-properties)" \ "(no-unwinding-assertions)" // the last line is for CBMC-regression testing only -class summarizer_parse_optionst: +class twols_parse_optionst: public parse_options_baset, public language_uit { @@ -66,13 +66,18 @@ class summarizer_parse_optionst: virtual int doit(); virtual void help(); - summarizer_parse_optionst(int argc, const char **argv); - summarizer_parse_optionst( + twols_parse_optionst(int argc, const char **argv); + twols_parse_optionst( int argc, const char **argv, const std::string &extra_options); protected: + ui_message_handlert ui_message_handler; + bool recursion_detected; + bool threads_detected; + virtual void register_languages(); + void get_command_line_options(optionst &options); bool get_goto_program( @@ -82,7 +87,7 @@ class summarizer_parse_optionst: bool process_goto_program( const optionst &options, goto_modelt &goto_model); - + bool set_properties(goto_modelt &); void report_success(); @@ -91,7 +96,7 @@ class summarizer_parse_optionst: void report_properties( const optionst &options, const goto_modelt &, - const summary_checker_baset::property_mapt &); + const summary_checker_baset::property_mapt &); void show_counterexample( const goto_modelt &, @@ -100,57 +105,76 @@ class summarizer_parse_optionst: void output_graphml_cex( const optionst &options, const goto_modelt &, - const class goto_tracet &); + const summary_checker_baset &summary_checker); + + void output_graphml_proof( + const optionst &options, + const goto_modelt &goto_model, + const summary_checker_baset &summary_checker); void output_json_cex( const optionst &options, const goto_modelt &goto_model, const goto_tracet &error_trace, const std::string &property_id); - - struct expr_statst { + + struct expr_statst + { bool has_malloc; bool has_string; bool has_array; bool has_pointer; - - expr_statst() - : has_malloc(false) - , has_string(false) - , has_array(false) - , has_pointer(false) - {} - }; - + + expr_statst(): + has_malloc(false), + has_string(false), + has_array(false), + has_pointer(false) + { + } + }; + void type_stats_rec( const typet &type, expr_statst &stats, const namespacet &ns); - + void expr_stats_rec( const exprt &expr, - expr_statst &stats); - + expr_statst &stats); + void show_stats( const goto_modelt &, - std::ostream &); - + std::ostream &); + void eval_verbosity(); void report_unknown(); - void require_entry( - const goto_modelt &goto_model); + void require_entry(const goto_modelt &goto_model); + + bool has_threads(const goto_modelt &goto_model); // diverse preprocessing void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void goto_unwind(goto_modelt &goto_model, unsigned k); + void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); - exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, - bool &valid); + exprt evaluate_casts_in_constants( + const exprt &expr, + const typet& parent_type, + bool &valid); void remove_multiple_dereferences(goto_modelt &goto_model); - void remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen); + void remove_multiple_dereferences( + goto_modelt &goto_model, + goto_programt &body, + goto_programt::targett t, + exprt &expr, + unsigned &var_counter, + bool deref_seen); + void add_assumptions_after_assertions(goto_modelt &goto_model); + void filter_assertions(goto_modelt &goto_model); + void split_loopheads(goto_modelt &goto_model); }; #endif diff --git a/src/2ls/Makefile b/src/2ls/Makefile new file mode 100644 index 000000000..b3a785bfd --- /dev/null +++ b/src/2ls/Makefile @@ -0,0 +1,58 @@ +include ../config.inc +include $(CBMC)/src/config.inc +include $(CBMC)/src/common + +SRC = 2ls_main.cpp 2ls_parse_options.cpp \ + 2ls_languages.cpp \ + show.cpp summary_checker_base.cpp \ + summary_checker_ai.cpp summary_checker_bmc.cpp \ + summary_checker_kind.cpp \ + cover_goals_ext.cpp horn_encoding.cpp \ + preprocessing_util.cpp \ + instrument_goto.cpp dynamic_cfg.cpp \ + graphml_witness_ext.cpp + +OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ + $(CBMC)/src/linking/linking$(LIBEXT) \ + $(CBMC)/src/assembler/assembler$(LIBEXT) \ + $(CBMC)/src/big-int/big-int$(LIBEXT) \ + $(CBMC)/src/goto-programs/goto-programs$(LIBEXT) \ + $(CBMC)/src/goto-symex/goto-symex$(LIBEXT) \ + $(CBMC)/src/analyses/analyses$(LIBEXT) \ + $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ + $(CBMC)/src/langapi/langapi$(LIBEXT) \ + $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ + $(CBMC)/src/json/json$(LIBEXT) \ + $(CBMC)/src/solvers/solvers$(LIBEXT) \ + $(CBMC)/src/util/util$(LIBEXT) \ + $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ + ../domains/domains$(LIBEXT) \ + ../ssa/ssa$(LIBEXT) \ + ../solver/solver$(LIBEXT) \ + # empty last line + +CP_CXXFLAGS+= $(TWOLSFLAGS) + +INCLUDES= -I $(CBMC)/src -I .. + +LIBS = + +CLEANFILES = 2ls$(EXEEXT) $(DELTA_OBJ) + +all: 2ls$(EXEEXT) + +ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) + OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) + CP_CXXFLAGS += -DHAVE_CPP +endif + +ifneq ($(wildcard $(CBMC)/src/java/Makefile),) + OBJ += $(CBMC)/src/java/java$(LIBEXT) + CXXFLAGS += -DHAVE_JAVA +endif + +############################################################################### + +2ls$(EXEEXT): $(OBJ) + $(LINKBIN) + diff --git a/src/summarizer/cover_goals_ext.cpp b/src/2ls/cover_goals_ext.cpp similarity index 55% rename from src/summarizer/cover_goals_ext.cpp rename to src/2ls/cover_goals_ext.cpp index 83a6fa127..fc8c3a234 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/2ls/cover_goals_ext.cpp @@ -9,7 +9,8 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include "../ssa/ssa_build_goto_trace.h" +#include + #include "cover_goals_ext.h" /*******************************************************************\ @@ -43,7 +44,7 @@ Function: cover_goals_extt::mark void cover_goals_extt::mark() { for(std::list::iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && @@ -53,10 +54,10 @@ void cover_goals_extt::mark() _number_covered++; } } - + /*******************************************************************\ -Function: cover_goals_extt::constaint +Function: cover_goals_extt::constraint Inputs: @@ -71,7 +72,7 @@ void cover_goals_extt::constraint() exprt::operandst disjuncts; for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && !g_it->condition.is_false()) @@ -96,7 +97,7 @@ Function: cover_goals_extt::freeze_goal_variables void cover_goals_extt::freeze_goal_variables() { for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->condition.is_constant()) @@ -118,21 +119,21 @@ Function: cover_goals_extt::operator() void cover_goals_extt::operator()() { _iterations=_number_covered=0; - + decision_proceduret::resultt dec_result; - + // We use incremental solving, so need to freeze some variables - // to prevent them from being eliminated. + // to prevent them from being eliminated. freeze_goal_variables(); do { // We want (at least) one of the remaining goals, please! _iterations++; - + constraint(); - - dec_result = solver(); + + dec_result=solver(); switch(dec_result) { @@ -141,12 +142,13 @@ void cover_goals_extt::operator()() case decision_proceduret::D_SATISFIABLE: // mark the goals we got - mark(); - + mark(); + // notify assignment(); - if(!all_properties) return; //exit on first failure if requested + if(!all_properties) + return; // exit on first failure if requested break; default: @@ -172,83 +174,76 @@ Function: cover_goals_extt::assignment void cover_goals_extt::assignment() { - //check loop head choices in model - bool invariants_involved = false; - if(spurious_check) + std::list::const_iterator g_it=goals.begin(); + for(goal_mapt::const_iterator it=goal_map.begin(); + it!=goal_map.end(); it++, g_it++) { - for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); - l_it != loophead_selects.end(); l_it++) + if(property_map[it->first].result==property_checkert::UNKNOWN && + solver.l_get(g_it->condition).is_true()) { - if(solver.get(l_it->op0()).is_true()) +#if 1 + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); +#else // THE ASSERTIONS THAT FAIL COULD BE RATHER ARBITRARY SINCE THE FORMULA + // IS NOT "ROOTED" IN AN INITIAL STATE. + assert((g_it->cond_expression).id()==ID_not); + exprt conjunct_expr=(g_it->cond_expression).op0(); +#if 0 + std::cout << "FAILED EXPR: " + << from_expr(SSA.ns, "", conjunct_expr) << std::endl; +#endif + + if(conjunct_expr.id()!=ID_and) { - invariants_involved = true; - break; + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); } - } - } - if(!invariants_involved || !spurious_check) - { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) - { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.l_get(g_it->condition).is_true()) + else { - property_map[it->first].result = property_checkert::FAIL; - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); - if(!all_properties) - break; - } + // filter out assertion instances that are not violated + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it= + conjunct_expr.operands().begin(); + c_it!=conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal=solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_false()) + { + failed_exprs.push_back(*c_it); +#if 0 + std::cout << "failed_expr: " + << from_expr(SSA.ns, "", *c_it) << std::endl; +#endif + } + } + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + + summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); } +#endif } - return; - } - - solver.new_context(); - // force avoiding paths going through invariants - - solver << conjunction(loophead_selects); - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) + if(property_map[it->first].result==property_checkert::FAIL) { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.l_get(g_it->condition).is_true()) + if(build_error_trace) { - property_map[it->first].result = property_checkert::FAIL; - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); - -#if 0 - show_raw_countermodel(it->first,SSA,solver,debug(),get_message_handler()); -#endif - if(!all_properties) - break; - } + ssa_build_goto_tracet build_goto_trace(SSA, solver.get_solver()); + build_goto_trace(property_map[it->first].error_trace); } } - break; - } - case decision_proceduret::D_UNSATISFIABLE: - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } - - solver.pop_context(); + if(!all_properties && + property_map[it->first].result==property_checkert::FAIL) + break; + } - _iterations++; //statistics + _iterations++; // statistics } diff --git a/src/summarizer/cover_goals_ext.h b/src/2ls/cover_goals_ext.h similarity index 63% rename from src/summarizer/cover_goals_ext.h rename to src/2ls/cover_goals_ext.h index beb9046ac..9a40f4693 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/2ls/cover_goals_ext.h @@ -6,15 +6,16 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_COVER_GOALS_H -#define CPROVER_SUMMARIZER_COVER_GOALS_H +#ifndef CPROVER_2LS_2LS_COVER_GOALS_EXT_H +#define CPROVER_2LS_2LS_COVER_GOALS_EXT_H #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/unwindable_local_ssa.h" -#include "../domains/incremental_solver.h" +#include +#include +#include +#include /*******************************************************************\ @@ -26,7 +27,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//cover goals extended with spuriousness check +// cover goals extended with spuriousness check struct goalt { @@ -38,7 +39,7 @@ struct goalt { description=id2string(instruction.source_location.get_comment()); } - + goalt() { } @@ -47,21 +48,22 @@ struct goalt class cover_goals_extt:public messaget { public: - explicit inline cover_goals_extt(unwindable_local_SSAt &_SSA, - incremental_solvert &_solver, - const exprt::operandst& _loophead_selects, - property_checkert::property_mapt &_property_map, - bool _spurious_check, bool _all_properties, - bool _build_error_trace): - SSA(_SSA), - solver(_solver), - property_map(_property_map), - spurious_check(_spurious_check), - all_properties(_all_properties), - build_error_trace(_build_error_trace), - loophead_selects(_loophead_selects) - {} - + cover_goals_extt( + unwindable_local_SSAt &_SSA, + incremental_solvert &_solver, + property_checkert::property_mapt &_property_map, + bool _all_properties, + bool _build_error_trace, + summarizer_bw_cex_baset &_summarizer_bw_cex): + SSA(_SSA), + solver(_solver), + property_map(_property_map), + all_properties(_all_properties), + build_error_trace(_build_error_trace), + summarizer_bw_cex(_summarizer_bw_cex) + { + } + virtual ~cover_goals_extt(); void operator()(); @@ -72,7 +74,8 @@ class cover_goals_extt:public messaget { literalt condition; bool covered; - + exprt cond_expression; + cover_goalt():covered(false) { } @@ -80,8 +83,8 @@ class cover_goals_extt:public messaget typedef std::list goalst; goalst goals; - - typedef std::map goal_mapt; + + typedef std::map goal_mapt; goal_mapt goal_map; // statistics @@ -90,32 +93,33 @@ class cover_goals_extt:public messaget { return _number_covered; } - + inline unsigned iterations() const { return _iterations; } - + inline goalst::size_type size() const { return goals.size(); } - + // managing the goals - inline void add(const literalt condition) + inline void add(const exprt cond_expression) { goals.push_back(cover_goalt()); - goals.back().condition=condition; + goals.back().condition=!solver.convert(cond_expression); + goals.back().cond_expression=cond_expression; } - + protected: unwindable_local_SSAt &SSA; unsigned _number_covered, _iterations; incremental_solvert &solver; property_checkert::property_mapt &property_map; - bool spurious_check, all_properties, build_error_trace; - exprt::operandst loophead_selects; + bool all_properties, build_error_trace; + summarizer_bw_cex_baset &summarizer_bw_cex; // this method is called for each satisfying assignment virtual void assignment(); diff --git a/src/2ls/dynamic_cfg.cpp b/src/2ls/dynamic_cfg.cpp new file mode 100644 index 000000000..c747a59e2 --- /dev/null +++ b/src/2ls/dynamic_cfg.cpp @@ -0,0 +1,302 @@ +/*******************************************************************\ + +Module: Dynamic Control Flow Graph + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "dynamic_cfg.h" + +#include + +/*******************************************************************\ + +Function: dynamic_cfgt::operator() + + Inputs: + + Outputs: + + Purpose: generates the dynamic CFG + +\*******************************************************************/ + +void dynamic_cfgt::operator()( + const ssa_local_unwindert &ssa_unwinder, + const unwindable_local_SSAt &ssa, + const summaryt &summary) +{ + const goto_programt &goto_program=ssa.goto_function.body; + build_cfg(goto_program, ssa_unwinder); + + assumptionst assumptions; + build_from_invariants(ssa, summary, assumptions); + add_assumptions(assumptions); +} + +/*******************************************************************\ + +Function: operator== + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator==(const dynamic_cfg_idt &a, const dynamic_cfg_idt &b) +{ + return a.pc==b.pc && a.iteration_stack==b.iteration_stack; +} + +/*******************************************************************\ + +Function: dynamic_cfgt::add_assumptions + + Inputs: + + Outputs: + + Purpose: annotates the nodes with assumptions + +\*******************************************************************/ + +void dynamic_cfgt::add_assumptions(const assumptionst &assumptions) +{ + for(const auto &a : assumptions) + { + (*this)[a.first].assumption=a.second; + } +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_cfg + + Inputs: + + Outputs: + + Purpose: extracts assumptions from invariants + +\*******************************************************************/ + +void dynamic_cfgt::build_cfg( + const goto_programt &goto_program, + const ssa_local_unwindert &ssa_unwinder) +{ + std::vector iteration_stack; + std::vector loop_node_stack; + std::vector max_iteration_stack; + std::map > incoming_edges; + + forall_goto_program_instructions(it, goto_program) + { + node_indext node=add_node(); + nodes[node].id.pc=it; + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=false; + nodes[node].assumption=nil_exprt(); + + // this is the end of a loop + // (sink self-loops are translated into assume false in the SSA) + if(it->is_backwards_goto() && + it->get_target()!=it) + { +#if 0 + // Sanity checks + const ssa_local_unwindert::loopt *loop=nullptr; + if(!ssa_unwinder.find_loop(it->get_target()->location_number, loop)) + { + std::cout << "NON-LOOP BACKEDGE? " << it->location_number + << " --> " << it->get_target()->location_number << std::endl; + assert(false); + } + assert(!iteration_stack.empty()); + assert(!max_iteration_stack.empty()); +#endif + + // max_unwind reached + if(iteration_stack.back()==max_iteration_stack.back()) + { + iteration_stack.pop_back(); + max_iteration_stack.pop_back(); + + // add back-edge + add_edge(node, loop_node_stack.back()); + + loop_node_stack.pop_back(); + } + // max_unwind not reached + else + { + // add edges for end of loop + const std::set &iedges=incoming_edges[it]; + for(const auto &from : iedges) + add_edge(from, node); + incoming_edges.erase(it); + + // jump back to loop head + it=it->get_target(); + iteration_stack.back()++; + node_indext new_node=add_node(); + nodes[new_node].id.iteration_stack=iteration_stack; + nodes[new_node].id.pc=it; + nodes[new_node].is_loop_head=false; + nodes[new_node].assumption=nil_exprt(); + + // add forward edge to unwound loop head + add_edge(node, new_node); + + // remember forward gotos + if(it->is_goto() && !it->is_backwards_goto()) + incoming_edges[it->get_target()].insert(new_node); + goto_programt::const_targett next=it; ++next; + if(next!=goto_program.instructions.end() && + (!it->is_goto() || !it->guard.is_true())) + incoming_edges[next].insert(new_node); + + continue; + } + } + // reconstruct sink self-loops + else if(it->is_backwards_goto() && + it->get_target()==it) + { + nodes[node].is_loop_head=true; + add_edge(node, node); + continue; + } + + // remember forward gotos + if(it->is_goto() && !it->is_backwards_goto()) + incoming_edges[it->get_target()].insert(node); + goto_programt::const_targett next=it; ++next; + if(next!=goto_program.instructions.end() && + (!it->is_goto() || !it->guard.is_true())) + incoming_edges[next].insert(node); + + // this is a loop head + const ssa_local_unwindert::loopt *loop=nullptr; + if(ssa_unwinder.find_loop(it->location_number, loop)) + { + iteration_stack.push_back(0); + loop_node_stack.push_back(node); + assert(loop->current_unwinding>=0); + max_iteration_stack.push_back(loop->current_unwinding); + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=true; + } + + const std::set &iedges=incoming_edges[it]; + for(const auto &from : iedges) + add_edge(from, node); + incoming_edges.erase(it); + } +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_from_invariant + + Inputs: + + Outputs: + + Purpose: extracts assumption from invariant + +\*******************************************************************/ + +void dynamic_cfgt::build_from_invariant( + const unwindable_local_SSAt &ssa, + const exprt &invariant, + assumptionst &assumptions) +{ + dynamic_cfg_idt id; + if(invariant.op0().id()==ID_symbol) + ssa.get_full_ssa_name( + to_symbol_expr(invariant.op0()), + id.pc, + id.iteration_stack); + else if(invariant.op0().id()==ID_and && + invariant.op0().op0().id()==ID_symbol) + ssa.get_full_ssa_name( + to_symbol_expr(invariant.op0().op0()), + id.pc, + id.iteration_stack); + else + assert(false); + + for(auto &a : assumptions) + { + // update existing + if(a.first==id) + { + exprt::operandst e; + if(a.second.id()==ID_and) + e=a.second.operands(); + else + e.push_back(a.second); + + exprt cexpr=invariant.op1(); // copy + clean_expr(cexpr); + e.push_back(cexpr); + a.second=conjunction(e); + return; + } + } + + // create new + assumptions.push_back(assumptiont()); + assumptions.back().first=id; + assumptions.back().second=invariant.op1(); // copy + clean_expr(assumptions.back().second); +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_from_invariants + + Inputs: + + Outputs: + + Purpose: extracts assumptions from invariants + +\*******************************************************************/ + +void dynamic_cfgt::build_from_invariants( + const unwindable_local_SSAt &ssa, + const summaryt &summary, + assumptionst &assumptions) +{ + if(summary.fw_invariant.is_nil() || + summary.fw_invariant.is_true()) + return; + + // expected format /\_i g_i=> inv_i + if(summary.fw_invariant.id()==ID_implies) + { + build_from_invariant( + ssa, + summary.fw_invariant, + assumptions); + } + else if(summary.fw_invariant.id()==ID_and) + { + for(unsigned i=0; i +#include +#include + +#include +#include +#include + + +/*******************************************************************\ + + Class: dynamic_cfgt + + Purpose: + +\*******************************************************************/ + +struct dynamic_cfg_edget +{ +}; + +struct dynamic_cfg_idt +{ + goto_programt::const_targett pc; + std::vector iteration_stack; + // TODO: thread id + + std::string to_string() const + { + std::ostringstream sid; + sid << i2string(pc->location_number); + for(const auto &i : iteration_stack) + sid << "." < +{ + dynamic_cfg_idt id; + bool is_loop_head; + exprt assumption; +}; + +class dynamic_cfgt:public graph +{ +public: + inline dynamic_cfg_nodet& operator[](const dynamic_cfg_idt &id) + { + for(auto &n : nodes) + { + if(n.id==id) + return n; + } + + node_indext node=add_node(); + nodes[node].id=id; + return nodes[node]; + } + + inline const nodet &operator[](node_indext n) const + { + return nodes[n]; + } + + // TODO: generalise this to non-inlined programs + void operator()( + const ssa_local_unwindert &ssa_unwinder, + const unwindable_local_SSAt &ssa, + const summaryt &summary); + +protected: + typedef std::pair assumptiont; + typedef std::vector assumptionst; + + void build_cfg( + const goto_programt &goto_program, + const ssa_local_unwindert &ssa_unwinder); + + void build_from_invariants( + const unwindable_local_SSAt &ssa, + const summaryt &summary, + assumptionst &assumptions); + void build_from_invariant( + const unwindable_local_SSAt &ssa, + const exprt &invariant, + assumptionst &assumptions); + + void add_assumptions(const assumptionst &assumptions); +}; + +#endif // CPROVER_2LS_SUMMARIZER_DYNAMIC_CFG_H diff --git a/src/2ls/graphml_witness_ext.cpp b/src/2ls/graphml_witness_ext.cpp new file mode 100644 index 000000000..11fa4bc3b --- /dev/null +++ b/src/2ls/graphml_witness_ext.cpp @@ -0,0 +1,148 @@ +/*******************************************************************\ + +Module: SSA CFA extension for GraphML output + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "graphml_witness_ext.h" + +/*******************************************************************\ + +Function: graphml_witness_extt::operator() + + Inputs: + + Outputs: + + Purpose: proof witness + TODO: works only for inlined programs + +\*******************************************************************/ + +void graphml_witness_extt::operator()( + const summary_checker_baset &summary_checker) +{ + irep_idt function_name=ID__start; + const unwindable_local_SSAt &ssa= + static_cast( + summary_checker.ssa_db.get(function_name)); + summaryt summary; + if(summary_checker.summary_db.exists(function_name)) + summary=summary_checker.summary_db.get(function_name); + const ssa_local_unwindert &ssa_unwinder= + summary_checker.ssa_unwinder.get(function_name); + + graphml.key_values["sourcecodelang"]="C"; + + dynamic_cfgt cfg; + cfg(ssa_unwinder, ssa, summary); + + // CFG to CFA + const graphmlt::node_indext sink=graphml.add_node(); + graphml[sink].node_name="sink"; + graphml[sink].thread_nr=0; + graphml[sink].is_violation=false; + graphml[sink].has_invariant=false; + + std::vector index_map; + index_map.resize(cfg.size()); + for(std::size_t i=0; isource_location; + + if(source_location.is_nil() || + source_location.get_file().empty() || + source_location.get_file()=="" || + source_location.get_line().empty()) + { + index_map[i]=sink; + continue; + } + + const graphmlt::node_indext node=add_node(cfg[i]); + index_map[i]=node; + } + for(std::size_t i=0; ifunction); + } + return node; +} + +/*******************************************************************\ + +Function: graphml_witness_extt::add_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void graphml_witness_extt::add_edge( + const graphmlt::node_indext &from, + const dynamic_cfg_nodet &from_cfg_node, + const graphmlt::node_indext &to, + const dynamic_cfg_nodet &to_cfg_node) +{ + const source_locationt &source_location=from_cfg_node.id.pc->source_location; + + xmlt edge("edge"); + edge.set_attribute("source", graphml[from].node_name); + edge.set_attribute("target", graphml[to].node_name); + + { + xmlt &data_f=edge.new_element("data"); + data_f.set_attribute("key", "originfile"); + data_f.data=id2string(source_location.get_file()); + + xmlt &data_l=edge.new_element("data"); + data_l.set_attribute("key", "startline"); + data_l.data=id2string(source_location.get_line()); + + if(to_cfg_node.is_loop_head) + { + xmlt &data_l=edge.new_element("data"); + data_l.set_attribute("key", "enterLoopHead"); + data_l.data="true"; + } + } + + graphml[to].in[from].xml_node=edge; + graphml[from].out[to].xml_node=edge; +} diff --git a/src/2ls/graphml_witness_ext.h b/src/2ls/graphml_witness_ext.h new file mode 100644 index 000000000..0b573dba5 --- /dev/null +++ b/src/2ls/graphml_witness_ext.h @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: SSA CFA extension for GraphML output + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_2LS_GRAPHML_WITNESS_EXT_H +#define CPROVER_2LS_2LS_GRAPHML_WITNESS_EXT_H + +#include + +#include + +#include "dynamic_cfg.h" +#include "summary_checker_base.h" + + +class graphml_witness_extt:public graphml_witnesst +{ +public: + explicit graphml_witness_extt(const namespacet &ns): + graphml_witnesst(ns) {} + + // correctness witness + void operator()(const summary_checker_baset &summary_checker); + +protected: + typedef std::map loc_to_node_mapt; + + graphmlt::node_indext add_node( + loc_to_node_mapt &loc_to_node, + goto_programt::const_targett it); + + void add_edge( + const graphmlt::node_indext &from, + const dynamic_cfg_nodet &from_cfg_node, + const graphmlt::node_indext &to, + const dynamic_cfg_nodet &to_cfg_node); + + graphmlt::node_indext add_node( + const dynamic_cfg_nodet &cfg_node); +}; + +#endif // CPROVER_2LS_SUMMARIZER_GRAPHML_WITNESS_EXT_H diff --git a/src/summarizer/horn_encoding.cpp b/src/2ls/horn_encoding.cpp similarity index 91% rename from src/summarizer/horn_encoding.cpp rename to src/2ls/horn_encoding.cpp index 481f336ee..fb986a691 100644 --- a/src/summarizer/horn_encoding.cpp +++ b/src/2ls/horn_encoding.cpp @@ -4,26 +4,16 @@ Module: Horn-clause Encoding Author: Daniel Kroening -Date: June 2015 - \*******************************************************************/ #include #include -#include "../ssa/local_ssa.h" +#include #include "horn_encoding.h" -/*******************************************************************\ - - Class: horn_encodingt - - Purpose: - -\*******************************************************************/ - class horn_encodingt { public: @@ -36,16 +26,16 @@ class horn_encodingt smt2_conv(ns, "", "Horn-clause encoding", "", smt2_convt::Z3, _out) { } - + void operator()(); protected: const goto_functionst &goto_functions; const namespacet ns; std::ostream &out; - + smt2_convt smt2_conv; - + void translate(const goto_functionst::function_mapt::const_iterator); }; @@ -94,7 +84,7 @@ void horn_encodingt::translate( local_SSAt local_SSA(f_it->second, ns, ""); const goto_programt &body=f_it->second.body; - + // first generate the predicates for all locations for(goto_programt::instructionst::const_iterator loc=body.instructions.begin(); @@ -109,7 +99,8 @@ void horn_encodingt::translate( o_it!=local_SSA.ssa_objects.objects.end(); o_it++) { - if(o_it!=local_SSA.ssa_objects.objects.begin()) out << ' '; + if(o_it!=local_SSA.ssa_objects.objects.begin()) + out << ' '; out << '('; smt2_conv.convert_expr(o_it->symbol_expr()); out << ' '; @@ -119,10 +110,10 @@ void horn_encodingt::translate( out << ") Bool)\n"; } - + out << '\n'; - // now encode transitions + // now encode transitions for(goto_programt::instructionst::const_iterator loc=body.instructions.begin(); loc!=body.instructions.end(); @@ -141,7 +132,8 @@ void horn_encodingt::translate( o_it!=local_SSA.ssa_objects.objects.end(); o_it++) { - if(o_it!=local_SSA.ssa_objects.objects.begin()) out << ' '; + if(o_it!=local_SSA.ssa_objects.objects.begin()) + out << ' '; out << '('; smt2_conv.convert_expr(o_it->symbol_expr()); out << ' '; @@ -150,9 +142,9 @@ void horn_encodingt::translate( } out << ")\n"; - out << " (=> (h-" << f_it->first << '-' + out << " (=> (h-" << f_it->first << '-' << loc->location_number; - + for(ssa_objectst::objectst::const_iterator o_it=local_SSA.ssa_objects.objects.begin(); o_it!=local_SSA.ssa_objects.objects.end(); @@ -161,7 +153,7 @@ void horn_encodingt::translate( out << ' '; smt2_conv.convert_expr(o_it->symbol_expr()); } - + out << ")\n "; if(loc->is_goto()) @@ -208,7 +200,6 @@ void horn_encodingt::translate( } else { - #if 0 for(local_SSAt::nodet::constraintst::const_iterator it=node.constraints.begin(); @@ -229,7 +220,7 @@ void horn_encodingt::translate( } #endif } - + out << ")))"; // =>, forall, assert out << '\n'; diff --git a/src/summarizer/horn_encoding.h b/src/2ls/horn_encoding.h similarity index 79% rename from src/summarizer/horn_encoding.h rename to src/2ls/horn_encoding.h index 33475adfb..a266ed46a 100644 --- a/src/summarizer/horn_encoding.h +++ b/src/2ls/horn_encoding.h @@ -6,8 +6,8 @@ Module: Horn-clause Encoding \*******************************************************************/ -#ifndef CPROVER_HORN_ENCODING_H -#define CPROVER_HORN_ENCODING_H +#ifndef CPROVER_2LS_2LS_HORN_ENCODING_H +#define CPROVER_2LS_2LS_HORN_ENCODING_H #include diff --git a/src/2ls/instrument_goto.cpp b/src/2ls/instrument_goto.cpp new file mode 100644 index 000000000..647aaab10 --- /dev/null +++ b/src/2ls/instrument_goto.cpp @@ -0,0 +1,233 @@ +/*******************************************************************\ + +Module: Instrument Goto Program with Inferred Information + +Author: Peter Schrammel, Björn Wachter + +\*******************************************************************/ + +#include + +// #define DEBUG + +#ifdef DEBUG +#include +#endif + +#include "instrument_goto.h" + +/*******************************************************************\ + +Function: find_loop_by_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +local_SSAt::locationt find_loop_by_guard( + const local_SSAt &SSA, + const symbol_exprt &guard) +{ + std::string gstr=id2string(guard.get_identifier()); + unsigned pos1=gstr.find("#")+1; + unsigned pos2=gstr.find("%", pos1); + unsigned n=safe_string2unsigned(gstr.substr(pos1, pos2)); + + local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + + for(; n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->location->location_number==n) + { + // find end of loop + break; + } + } + + if(n_it->loophead==SSA.nodes.end()) + return n_it->location; + else + return n_it->loophead->location; +} + +/*******************************************************************\ + +Function: instrument_gotot::instrument_instruction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void instrument_gotot::instrument_instruction( + const exprt &expr, + goto_programt &dest, + goto_programt::targett &target) +{ + goto_programt::targett where=target; + +#ifdef DEBUG + std::cout << "target " << target->type << " : " + << target->source_location << std::endl; +#endif + + for(; ; ++where) + { + if(where->is_goto() && where->get_target()==target) + break; + } + + goto_programt tmp; + + goto_programt::targett assumption=tmp.add_instruction(); + assumption->make_assumption(expr); + assumption->source_location=target->source_location; + assumption->source_location.set_comment("invariant generated by 2LS"); + + dest.insert_before_swap(where, tmp); + +#ifdef DEBUG + std::cout << "instrumenting instruction " << std::endl; +#endif +} + +extern void purify_identifiers(exprt &expr); + +/*******************************************************************\ + +Function: instrument_gotot::instrument_body + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void instrument_gotot::instrument_body( + const local_SSAt &SSA, + const exprt &expr, + goto_functionst::goto_functiont &function) +{ + // expected format (/\_j g_j)=> inv + const exprt &impl=expr.op0(); + exprt inv=expr.op1(); // copy + + std::cout << "Invariant " << from_expr(inv) << std::endl; + + purify_identifiers(inv); + + local_SSAt::locationt loc; + if(impl.id()==ID_symbol) + { + loc=find_loop_by_guard(SSA, to_symbol_expr(impl)); + } + else if(impl.id()==ID_and) + { + assert(impl.op0().id()==ID_symbol); + loc=find_loop_by_guard(SSA, to_symbol_expr(impl.op0())); + } + else + assert(false); + + Forall_goto_program_instructions(it, function.body) + { + if(it==loc) + { + instrument_instruction(inv, function.body, it); + break; + } + } +} + +/*******************************************************************\ + +Function: instrument_gotot::instrument_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void instrument_gotot::instrument_function( + const irep_idt &function_name, + goto_functionst::goto_functiont &function) +{ + #ifdef DEBUG + std::cout << "instrumenting function " << function_name << std::endl; + #endif + + if(!summary_db.exists(function_name)) + return; + + const summaryt summary=summary_db.get(function_name); + + if(!ssa_db.exists(function_name)) + return; + + const local_SSAt &SSA=ssa_db.get(function_name); + + if(summary.fw_invariant.is_nil()) + return; + if(summary.fw_invariant.is_true()) + return; + + // expected format /\_i g_i=> inv_i + if(summary.fw_invariant.id()==ID_implies) + { + instrument_body(SSA, summary.fw_invariant, function); + } + else if(summary.fw_invariant.id()==ID_and) + { + for(unsigned i=0; i::function_mapt + function_mapt; + + function_mapt &function_map=goto_functions.function_map; + + for(function_mapt::iterator + fit=function_map.begin(); + fit!=function_map.end(); + ++fit) + { + instrument_function(fit->first, fit->second); + } + + goto_model.goto_functions.update(); +} diff --git a/src/summarizer/instrument_goto.h b/src/2ls/instrument_goto.h similarity index 68% rename from src/summarizer/instrument_goto.h rename to src/2ls/instrument_goto.h index dbc3a1b41..c121994e0 100644 --- a/src/summarizer/instrument_goto.h +++ b/src/2ls/instrument_goto.h @@ -2,29 +2,30 @@ Module: Instrument Goto Program with Inferred Information -Author: Peter Schrammel +Author: Peter Schrammel, Björn Wachter \*******************************************************************/ -#ifndef CPROVER_INSTRUMENT_GOTO_H -#define CPROVER_INSTRUMENT_GOTO_H +#ifndef CPROVER_2LS_2LS_INSTRUMENT_GOTO_H +#define CPROVER_2LS_2LS_INSTRUMENT_GOTO_H #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" -#include "ssa_db.h" -#include "summary_db.h" +#include +#include +#include +#include class instrument_gotot:public messaget { public: - inline instrument_gotot(optionst &_options, - ssa_dbt &_ssa_db, - summary_dbt &_summary_db): + inline instrument_gotot( + optionst &_options, + ssa_dbt &_ssa_db, + summary_dbt &_summary_db): options(_options), - ssa_db(_ssa_db),summary_db(_summary_db) + ssa_db(_ssa_db), summary_db(_summary_db) { } @@ -35,7 +36,7 @@ class instrument_gotot:public messaget ssa_dbt &ssa_db; summary_dbt &summary_db; - + void instrument_function( const irep_idt &function_name, goto_functionst::goto_functiont &function); @@ -49,7 +50,6 @@ class instrument_gotot:public messaget const exprt &expr, goto_programt &dest, goto_programt::targett &target); - }; #endif diff --git a/src/2ls/preprocessing_util.cpp b/src/2ls/preprocessing_util.cpp new file mode 100644 index 000000000..590827693 --- /dev/null +++ b/src/2ls/preprocessing_util.cpp @@ -0,0 +1,468 @@ +/*******************************************************************\ + +Module: 2LS Command Line Options Processing + +Author: Peter Schrammel + +\*******************************************************************/ + +#include +#include +#include + +#include +#include + +#include "2ls_parse_options.h" + + +/*******************************************************************\ + +Function: twols_parse_optionst::inline_main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::inline_main(goto_modelt &goto_model) +{ + goto_programt &main=goto_model.goto_functions.function_map[ID__start].body; + goto_programt::targett target=main.instructions.begin(); + while(target!=main.instructions.end()) + { + if(target->is_function_call()) + { + const code_function_callt &code_function_call= + to_code_function_call(target->code); + irep_idt fname=code_function_call.function().get(ID_identifier); + + debug() << "Inlining " << fname << eom; + + goto_programt tmp; + tmp.copy_from(goto_model.goto_functions.function_map[fname].body); + (--tmp.instructions.end())->make_skip(); + goto_model.goto_functions.function_map.erase(fname); + + goto_programt::targett next_target(target); + target->make_skip(); + next_target++; + main.instructions.splice(next_target, tmp.instructions); + target=next_target; + } + else + target++; + } + + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::propagate_constants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::propagate_constants(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + constant_propagator_ait(f_it->second, ns); + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::nondet_locals + + Inputs: + + Outputs: + + Purpose: explicitly assign a nondet_symbol to local variables + this is required by the unwinder, which would be unable + to recognise in which scope variables have been declared + +\*******************************************************************/ + +void twols_parse_optionst::nondet_locals(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_decl()) + { + const code_declt& decl=to_code_decl(i_it->code); + + goto_programt::const_targett next=i_it; ++next; + if(next!=f_it->second.body.instructions.end() && + next->is_assign() && + to_code_assign(next->code).lhs().id()==ID_symbol && + to_symbol_expr(to_code_assign(next->code).lhs())==decl.symbol()) + continue; + + side_effect_expr_nondett nondet(decl.symbol().type()); + goto_programt::targett t=f_it->second.body.insert_after(i_it); + t->make_assignment(); + code_assignt c(decl.symbol(), nondet); + t->code.swap(c); + t->source_location=i_it->source_location; + } + } + } + goto_model.goto_functions.update(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::unwind_goto_into_loop + + Inputs: + + Outputs: + + Purpose: unwind all loops + +\*******************************************************************/ + +void twols_parse_optionst::unwind_goto_into_loop( + goto_modelt &goto_model, + unsigned k) +{ + typedef std::vector > loopst; + + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; + + loopst loops; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_backwards_goto()) + { + goto_programt::targett loop_head=i_it->get_target(); + goto_programt::targett loop_exit=i_it; + bool has_goto_into_loop=false; + + goto_programt::targett it=loop_head; + if(it!=loop_exit) + it++; + for(; it!=loop_exit; it++) + { + for( std::set::iterator + s_it=it->incoming_edges.begin(); + s_it!=it->incoming_edges.end(); ++s_it) + { + if((*s_it)->is_goto() && + (*s_it)->location_numberlocation_number) + { + has_goto_into_loop=true; + break; + } + } + if(has_goto_into_loop) + break; + } + if(has_goto_into_loop) + { + status() << "Unwinding jump into loop" << eom; + loops.push_back(loopst::value_type(++loop_exit, loop_head)); + } + } + } + + for(loopst::iterator l_it=loops.begin(); l_it!=loops.end(); ++l_it) + { + std::vector iteration_points; + + goto_unwindt goto_unwind; + goto_unwind.unwind( + body, + l_it->second, + l_it->first, + k, + goto_unwindt::PARTIAL, iteration_points); + + assert(iteration_points.size()==2); + goto_programt::targett t=body.insert_before(l_it->first); + t->make_goto(); + t->targets.push_back(iteration_points.front()); + } + } + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_multiple_dereferences + + Inputs: + + Outputs: + + Purpose: temporary fix to circumvent ssa_dereference problem + +\*******************************************************************/ + +void twols_parse_optionst::remove_multiple_dereferences( + goto_modelt &goto_model, + goto_programt &body, + goto_programt::targett t, + exprt &expr, + unsigned &var_counter, + bool deref_seen) +{ + if(expr.id()==ID_member) + { + member_exprt &member_expr=to_member_expr(expr); + if(member_expr.compound().id()==ID_dereference) + { + dereference_exprt &deref_expr=to_dereference_expr(member_expr.compound()); + remove_multiple_dereferences( + goto_model, body, t, deref_expr.pointer(), var_counter, true); + if(deref_seen) + { + symbolt new_symbol; + new_symbol.type=member_expr.type(); + new_symbol.name="$deref"+i2string(var_counter++); + new_symbol.base_name=new_symbol.name; + new_symbol.pretty_name=new_symbol.name; + goto_model.symbol_table.add(new_symbol); + goto_programt::targett t_new=body.insert_before(t); + t_new->make_assignment(); + t_new->code=code_assignt(new_symbol.symbol_expr(), member_expr); + expr=new_symbol.symbol_expr(); + for(std::set::iterator t_it= + t->incoming_edges.begin(); + t_it!=t->incoming_edges.end(); ++t_it) + { + if((*t_it)->is_goto() && (*t_it)->get_target()==t) + { + (*t_it)->targets.clear(); + (*t_it)->targets.push_back(t_new); + } + } + body.compute_location_numbers(); + body.compute_target_numbers(); + body.compute_incoming_edges(); + } + } + else + Forall_operands(o_it, expr) + remove_multiple_dereferences( + goto_model, body, t, *o_it, var_counter, deref_seen); + } + else + Forall_operands(o_it, expr) + remove_multiple_dereferences( + goto_model, body, t, *o_it, var_counter, deref_seen); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_multiple_dereferences + + Inputs: + + Outputs: + + Purpose: temporary fix to circumvent ssa_dereference problem + +\*******************************************************************/ + +void twols_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) +{ + unsigned var_counter=0; + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_goto()) + { + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, + false); + } + else if(i_it->is_assign()) + { + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_assumptions_after_assertions + + Inputs: + + Outputs: + + Purpose: assumes assertions after checking them + +\*******************************************************************/ + +void twols_parse_optionst::add_assumptions_after_assertions( + goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assert() && !i_it->guard.is_true()) + { + goto_programt::targett t_new=f_it->second.body.insert_after(i_it); + t_new->make_assumption(i_it->guard); + f_it->second.body.compute_location_numbers(); + f_it->second.body.compute_target_numbers(); + f_it->second.body.compute_incoming_edges(); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::has_threads + + Inputs: + + Outputs: + + Purpose: checks whether the program has threads + +\*******************************************************************/ + +bool twols_parse_optionst::has_threads(const goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + forall_goto_functions(f_it, goto_model.goto_functions) + { + const goto_programt& program=f_it->second.body; + + forall_goto_program_instructions(i_it, program) + { + const goto_programt::instructiont &instruction=*i_it; + + if(instruction.is_function_call()) + { + const code_function_callt &fct=to_code_function_call(instruction.code); + if(fct.function().id()==ID_symbol) + { + const symbol_exprt &fsym=to_symbol_expr(fct.function()); + + if(ns.lookup(fsym.get_identifier()).base_name=="pthread_create") + return true; + } + } + } + } + return false; +} + +/*******************************************************************\ + +Function: twols_parse_optionst::filter_assertions + + Inputs: + + Outputs: + + Purpose: filter certain assertions for SV-COMP + +\*******************************************************************/ + +void twols_parse_optionst::filter_assertions(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &program=f_it->second.body; + + Forall_goto_program_instructions(i_it, program) + { + if(!i_it->is_assert()) + continue; + + if(i_it->source_location.get_comment()=="free argument is dynamic object") + i_it->make_skip(); + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::split_loopheads + +Inputs: + +Outputs: + +Purpose: insert skip at jump targets if they are goto, + assume or assert instructions + +\*******************************************************************/ + +void twols_parse_optionst::split_loopheads(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(!i_it->is_backwards_goto()) + continue; + goto_programt::targett loophead=i_it->get_target(); + if(i_it->guard.is_true() && !loophead->is_assert()) + continue; + + // inserts the skip + goto_programt::targett new_loophead= + f_it->second.body.insert_before(loophead); + new_loophead->make_skip(); + new_loophead->source_location=loophead->source_location; + new_loophead->function=i_it->function; + + // update jumps to loophead + for(std::set::iterator j_it= + loophead->incoming_edges.begin(); + j_it!=loophead->incoming_edges.end(); j_it++) + { + if(!(*j_it)->is_goto() || (*j_it)->get_target()!=loophead) + continue; + (*j_it)->targets.clear(); + (*j_it)->targets.push_back(new_loophead); + } + } + } +} diff --git a/src/summarizer/show.cpp b/src/2ls/show.cpp similarity index 66% rename from src/summarizer/show.cpp rename to src/2ls/show.cpp index deef5d0a9..c81d0db95 100644 --- a/src/summarizer/show.cpp +++ b/src/2ls/show.cpp @@ -10,25 +10,21 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include -#include -#include -#include -#include #include #include #include #include -#include "../ssa/ssa_domain.h" -#include "../ssa/guard_map.h" -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_value_set.h" +#include +#include +#include +#include +#include -#include "../domains/ssa_fixed_point.h" +#include -#include "summary.h" +#include "show.h" /*******************************************************************\ @@ -87,9 +83,9 @@ void show_assignments( forall_goto_functions(f_it, goto_model.goto_functions) { out << ">>>> Function " << f_it->first << "\n"; - + show_assignments(f_it->second, ns, out); - + out << "\n"; } } @@ -139,7 +135,7 @@ void show_defs( message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -154,9 +150,9 @@ void show_defs( forall_goto_functions(f_it, goto_model.goto_functions) { out << ">>>> Function " << f_it->first << "\n"; - + show_defs(f_it->second, ns, out); - + out << "\n"; } } @@ -202,7 +198,7 @@ void show_guards( message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -217,9 +213,9 @@ void show_guards( forall_goto_functions(f_it, goto_model.goto_functions) { out << ">>>> Function " << f_it->first << "\n"; - + show_guards(f_it->second, ns, out); - + out << "\n"; } } @@ -244,7 +240,8 @@ void show_ssa( std::ostream &out) { local_SSAt local_SSA(goto_function, ns); - if(simplify) ::simplify(local_SSA, ns); + if(simplify) + ::simplify(local_SSA, ns); local_SSA.output_verbose(out); } @@ -268,7 +265,7 @@ void show_ssa( message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - + if(!function.empty()) { out << ">>>> Function " << function << "\n"; @@ -283,13 +280,15 @@ void show_ssa( { forall_goto_functions(f_it, goto_model.goto_functions) { - if(f_it->first=="assert") continue; - if(f_it->first=="__CPROVER_assume") continue; + if(f_it->first=="assert") + continue; + if(f_it->first=="__CPROVER_assume") + continue; out << ">>>> Function " << f_it->first << "\n"; - + show_ssa(f_it->second, simplify, ns, out); - + out << "\n"; } } @@ -297,7 +296,7 @@ void show_ssa( /*******************************************************************\ -Function: show_fixed_point +Function: print_symbol_values Inputs: @@ -307,21 +306,28 @@ Function: show_fixed_point \*******************************************************************/ -void show_fixed_point( - const goto_functionst::goto_functiont &goto_function, - bool simplify, - const namespacet &ns, - std::ostream &out) +void print_symbol_values( + const local_SSAt &SSA, + prop_convt &solver, + std::ostream &out, + const exprt &expr) { - local_SSAt local_SSA(goto_function, ns); - if(simplify) ::simplify(local_SSA, ns); - ssa_fixed_point(local_SSA); - local_SSA.output(out); + if(expr.id()==ID_symbol) + { + out << from_expr(SSA.ns, "", expr) << "==" + << from_expr(SSA.ns, "", solver.get(expr)) << "\n"; + return; + } + for(exprt::operandst::const_iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) + { + print_symbol_values(SSA, solver, out, *it); + } } /*******************************************************************\ -Function: show_fixed_points +Function: show_raw_countermodel Inputs: @@ -331,40 +337,41 @@ Function: show_fixed_points \*******************************************************************/ -void show_fixed_points( - const goto_modelt &goto_model, - const irep_idt &function, - bool simplify, +void show_raw_countermodel( + const irep_idt &property_id, + const local_SSAt &SSA, + prop_convt &solver, std::ostream &out, message_handlert &message_handler) { - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) + out << "\n*** Error trace for property " << property_id << "\n"; + for(local_SSAt::nodest::const_iterator n_it= + SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_fixed_point(f_it->second, simplify, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - out << ">>>> Function " << f_it->first << "\n"; - - show_fixed_point(f_it->second, simplify, ns, out); - - out << "\n"; + print_symbol_values(SSA, solver, out, *e_it); + // out << from_expr(SSA.ns, "", e_it->op0()) << "==" << + // from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; + } + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) + { + print_symbol_values(SSA, solver, out, *c_it); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + print_symbol_values(SSA, solver, out, *a_it); } } + out << "\n"; } /*******************************************************************\ -Function: show_error_trace +Function: find_loc_by_guard Inputs: @@ -374,59 +381,20 @@ Function: show_error_trace \*******************************************************************/ -void print_symbol_values(const local_SSAt &SSA, - prop_convt &solver, - std::ostream &out, - const exprt &expr) +local_SSAt::locationt find_loc_by_guard( + const local_SSAt &SSA, + const symbol_exprt &guard) { -// if(expr.id()==ID_symbol) - { - out << from_expr(SSA.ns, "",expr) << " == " << - from_expr(SSA.ns, "", solver.get(expr)) << "\n"; - // return; - } - for(exprt::operandst::const_iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) - { - print_symbol_values(SSA,solver,out,*it); - } -} - -void show_raw_countermodel(const irep_idt &property_id, - const local_SSAt &SSA, - prop_convt &solver, - std::ostream &out, - message_handlert &message_handler) - -{ - out << "\n*** Error trace for property " << property_id << "\n"; - for (local_SSAt::nodest::const_iterator n_it = - SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) - { - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) - { - print_symbol_values(SSA,solver,out,*e_it); - // out << from_expr(SSA.ns, "", e_it->op0()) << " == " << - // from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; - } - for (local_SSAt::nodet::constraintst::const_iterator c_it = - n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) - { - print_symbol_values(SSA,solver,out,*c_it); - } - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) - { - print_symbol_values(SSA,solver,out,*a_it); - } - } - out << "\n"; + std::string gstr=id2string(guard.get_identifier()); + unsigned pos1=gstr.find("#")+1; + unsigned pos2=gstr.find("%", pos1); + unsigned n=safe_string2unsigned(gstr.substr(pos1, pos2)); + return SSA.get_location(n); } /*******************************************************************\ -Function: show_invariants +Function: purify_identifiers Inputs: @@ -436,22 +404,12 @@ Function: show_invariants \*******************************************************************/ -local_SSAt::locationt find_loc_by_guard(const local_SSAt &SSA, - const symbol_exprt &guard) -{ - std::string gstr = id2string(guard.get_identifier()); - unsigned pos1 = gstr.find("#")+1; - unsigned pos2 = gstr.find("%",pos1); - unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); - return SSA.get_location(n); -} - void purify_identifiers(exprt &expr) { if(expr.id()==ID_symbol) { - std::string idstr = id2string(to_symbol_expr(expr).get_identifier()); - to_symbol_expr(expr).set_identifier(idstr.substr(0,idstr.find("#"))); + std::string idstr=id2string(to_symbol_expr(expr).get_identifier()); + to_symbol_expr(expr).set_identifier(idstr.substr(0, idstr.find("#"))); } for(unsigned i=0; i inv - const exprt &impl = expr.op0(); - exprt inv = expr.op1(); //copy + // expected format (/\_j g_j)=> inv + const exprt &impl=expr.op0(); + exprt inv=expr.op1(); // copy local_SSAt::locationt loc; if(impl.id()==ID_symbol) { - loc = find_loc_by_guard(SSA,to_symbol_expr(impl)); + loc=find_loc_by_guard(SSA, to_symbol_expr(impl)); } else if(impl.id()==ID_and) { assert(impl.op0().id()==ID_symbol); - loc = find_loc_by_guard(SSA,to_symbol_expr(impl.op0())); + loc=find_loc_by_guard(SSA, to_symbol_expr(impl.op0())); } - else assert(false); + else + assert(false); out << "\n** invariant: " << loc->source_location << "\n"; purify_identifiers(inv); - out << " " << from_expr(SSA.ns,"",inv) << "\n"; + out << " " << from_expr(SSA.ns, "", inv) << "\n"; } -void show_invariants(const local_SSAt &SSA, - const summaryt &summary, - std::ostream &out) +/*******************************************************************\ + +Function: show_invariants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_invariants( + const local_SSAt &SSA, + const summaryt &summary, + std::ostream &out) { - if(summary.fw_invariant.is_nil()) return; - if(summary.fw_invariant.is_true()) return; + if(summary.fw_invariant.is_nil()) + return; + if(summary.fw_invariant.is_true()) + return; - //expected format /\_i g_i => inv_i + // expected format /\_i g_i=> inv_i if(summary.fw_invariant.id()==ID_implies) { - show_invariant(SSA,summary.fw_invariant,out); + show_invariant(SSA, summary.fw_invariant, out); } else if(summary.fw_invariant.id()==ID_and) { for(unsigned i=0; i symbols; - out << "\n*** SSA Symbols " << "\n"; - for (local_SSAt::nodest::const_iterator n_it = - SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) + out << "\n*** SSA Symbols " << "\n"; + for(local_SSAt::nodest::const_iterator n_it= + SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - find_symbols(*e_it,symbols); + find_symbols(*e_it, symbols); } - for (local_SSAt::nodet::constraintst::const_iterator c_it = - n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { - find_symbols(*c_it,symbols); + find_symbols(*c_it, symbols); } - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) { - find_symbols(*a_it,symbols); + find_symbols(*a_it, symbols); } - find_symbols(n_it->enabling_expr,symbols); + find_symbols(n_it->enabling_expr, symbols); } - for(std::set::const_iterator it = symbols.begin(); - it != symbols.end(); it++) + for(std::set::const_iterator it=symbols.begin(); + it!=symbols.end(); it++) { - out << from_type(SSA.ns, "",it->type()) << " " << + out << from_type(SSA.ns, "", it->type()) << " " << from_expr(SSA.ns, "", *it) << ";\n"; } out << "\n"; @@ -596,7 +584,7 @@ void show_value_sets( message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -611,9 +599,9 @@ void show_value_sets( forall_goto_functions(f_it, goto_model.goto_functions) { out << ">>>> Function " << f_it->first << "\n"; - + show_value_set(f_it->second, ns, out); - + out << "\n"; } } diff --git a/src/summarizer/show.h b/src/2ls/show.h similarity index 78% rename from src/summarizer/show.h rename to src/2ls/show.h index 7dda411df..2e9557eac 100644 --- a/src/summarizer/show.h +++ b/src/2ls/show.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_SHOW_H -#define CPROVER_SUMMARIZER_SHOW_H +#ifndef CPROVER_2LS_2LS_SHOW_H +#define CPROVER_2LS_2LS_SHOW_H #include #include @@ -16,7 +16,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "summary.h" +#include class message_handlert; @@ -51,28 +51,21 @@ void show_guards( std::ostream &, message_handlert &); -void show_fixed_points( - const goto_modelt &, - const irep_idt &function, - bool simplify, - std::ostream &, - message_handlert &); - -//shows raw error trace +// shows raw error trace void show_raw_countermodel( const irep_idt &property_id, - const local_SSAt &SSA, + const local_SSAt &SSA, prop_convt &solver, std::ostream &, message_handlert &); void show_invariants( - const local_SSAt &SSA, + const local_SSAt &SSA, const summaryt &summary, std::ostream &out); void show_ssa_symbols( - const local_SSAt &SSA, + const local_SSAt &SSA, std::ostream &out); #endif diff --git a/src/2ls/summary_checker_ai.cpp b/src/2ls/summary_checker_ai.cpp new file mode 100644 index 000000000..65259c40c --- /dev/null +++ b/src/2ls/summary_checker_ai.cpp @@ -0,0 +1,183 @@ +/*******************************************************************\ + +Module: Summary Checker for AI + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_ai.h" +#include + +#define TERM_CEX 1 + +/*******************************************************************\ + +Function: summary_checker_ait::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns); + + ssa_unwinder.init(false, false); + + unsigned unwind=options.get_unsigned_int_option("unwind"); + if(unwind>0) + { + status() << "Unwinding" << messaget::eom; + + ssa_unwinder.init_localunwinders(); + + ssa_unwinder.unwind_all(unwind); + } + + // properties + initialize_property_map(goto_model.goto_functions); + + bool preconditions=options.get_bool_option("preconditions"); + bool termination=options.get_bool_option("termination"); + bool trivial_domain=options.get_bool_option("havoc"); + if(!trivial_domain || termination) + { + // forward analysis + summarize(goto_model, true, termination); + } + if(!trivial_domain && preconditions) + { + // backward analysis + summarize(goto_model, false, termination); + } + + if(preconditions) + { + report_statistics(); + report_preconditions(); + return property_checkert::UNKNOWN; + } + + if(termination) + { + report_statistics(); + return report_termination(); + } + +#ifdef SHOW_CALLINGCONTEXTS + if(options.get_bool_option("show-calling-contexts")) + return property_checkert::UNKNOWN; +#endif + + property_checkert::resultt result= + options.get_bool_option("all-functions")? + check_properties(): + check_properties(goto_model.goto_functions.entry_point()); + report_statistics(); + return result; +} + + +/*******************************************************************\ + +Function: summary_checker_ait::report_preconditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_ait::report_preconditions() +{ + result() << eom; + result() << "** " << (options.get_bool_option("sufficient") ? + "Sufficient" : "Necessary") + << " preconditions: " << eom; + ssa_dbt::functionst &functions=ssa_db.functions(); + for(ssa_dbt::functionst::iterator it=functions.begin(); + it!=functions.end(); it++) + { + exprt precondition; + bool computed=summary_db.exists(it->first); + if(computed) + precondition=summary_db.get(it->first).bw_precondition; + if(precondition.is_nil()) + computed=false; + result() << eom << "[" << it->first << "]: " << + (!computed?"not computed":from_expr(it->second->ns, "", precondition)) + << eom; + } +} + +/*******************************************************************\ + +Function: summary_checker_ait::report_termination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::report_termination() +{ + result() << eom; + result() << "** Termination: " << eom; + bool not_computed=true; + bool all_terminate=true; + bool one_nonterminate=false; + ssa_dbt::functionst &functions=ssa_db.functions(); + for(ssa_dbt::functionst::iterator it=functions.begin(); + it!=functions.end(); it++) + { + threevalt terminates=YES; + bool computed=summary_db.exists(it->first); + if(computed) + { + terminates=summary_db.get(it->first).terminates; + not_computed=false; + } + all_terminate=all_terminate && (terminates==YES); + one_nonterminate=one_nonterminate || (terminates==NO); + result() << "[" << it->first << "]: " + << (!computed ? "not computed" : threeval2string(terminates)) << eom; + } + if(not_computed) + return property_checkert::UNKNOWN; + if(all_terminate) + return property_checkert::PASS; + if(one_nonterminate) + { +#if TERM_CEX + if(options.get_option("graphml-witness")!="" && + !functions.empty()) + { + property_map.clear(); + incremental_solvert &solver=ssa_db.get_solver(functions.begin()->first); + if(solver()==decision_proceduret::D_SATISFIABLE) + { + irep_idt pid="non-termination"; + property_map[pid].result=property_checkert::FAIL; + ssa_build_goto_tracet build_goto_trace( + *functions.begin()->second, solver.get_solver(), true); + build_goto_trace(property_map[pid].error_trace); + } + } +#endif + return property_checkert::FAIL; + } + return property_checkert::UNKNOWN; +} diff --git a/src/summarizer/summary_checker_ai.h b/src/2ls/summary_checker_ai.h similarity index 77% rename from src/summarizer/summary_checker_ai.h rename to src/2ls/summary_checker_ai.h index 33eafe538..84eab2bf0 100644 --- a/src/summarizer/summary_checker_ai.h +++ b/src/2ls/summary_checker_ai.h @@ -6,25 +6,24 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_AI_H -#define CPROVER_SUMMARY_CHECKER_AI_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_AI_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_AI_H #include "summary_checker_base.h" class summary_checker_ait:public summary_checker_baset { public: - inline summary_checker_ait(optionst &_options): + explicit summary_checker_ait(optionst &_options): summary_checker_baset(_options) { } - + virtual resultt operator()(const goto_modelt &); protected: void report_preconditions(); property_checkert::resultt report_termination(); - }; #endif diff --git a/src/2ls/summary_checker_base.cpp b/src/2ls/summary_checker_base.cpp new file mode 100644 index 000000000..57f5e8581 --- /dev/null +++ b/src/2ls/summary_checker_base.cpp @@ -0,0 +1,649 @@ +/*******************************************************************\ + +Module: Summary Checker Base + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHOW_CALLING_CONTEXTS +#include +#endif + +#include "show.h" +#include "instrument_goto.h" + +#include "summary_checker_base.h" + +/*******************************************************************\ + +Function: summary_checker_baset::SSA_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::SSA_functions( + const goto_modelt &goto_model, + const namespacet &ns) +{ + // compute SSA for all the functions + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(!f_it->second.body_available()) + continue; + if(has_prefix(id2string(f_it->first), TEMPLATE_DECL)) + continue; + status() << "Computing SSA of " << f_it->first << messaget::eom; + + ssa_db.create(f_it->first, f_it->second, ns); + local_SSAt &SSA=ssa_db.get(f_it->first); + + // simplify, if requested + if(simplify) + { + status() << "Simplifying" << messaget::eom; + ::simplify(SSA, ns); + } + + SSA.output(debug()); debug() << eom; + } + + // properties + initialize_property_map(goto_model.goto_functions); +} + +/*******************************************************************\ + +Function: summary_checker_baset::summarize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::summarize( + const goto_modelt &goto_model, + bool forward, + bool termination) +{ + summarizer_baset *summarizer=nullptr; + +#ifdef SHOW_CALLING_CONTEXTS + if(options.get_bool_option("show-calling-contexts")) + summarizer=new summarizer_fw_contextst( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + else // NOLINT(*) +#endif + { + if(forward && !termination) + summarizer=new summarizer_fwt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + if(forward && termination) + summarizer=new summarizer_fw_termt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + if(!forward && !termination) + summarizer=new summarizer_bwt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + if(!forward && termination) + summarizer=new summarizer_bw_termt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + } + assert(summarizer!=nullptr); + + summarizer->set_message_handler(get_message_handler()); + + if(!options.get_bool_option("context-sensitive") && + options.get_bool_option("all-functions")) + summarizer->summarize(); + else + summarizer->summarize(goto_model.goto_functions.entry_point()); + + // statistics + solver_instances+=summarizer->get_number_of_solver_instances(); + solver_calls+=summarizer->get_number_of_solver_calls(); + summaries_used+=summarizer->get_number_of_summaries_used(); + termargs_computed+=summarizer->get_number_of_termargs_computed(); + + delete summarizer; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: function_name!=nil + checks all functions in the call graph from the entry point + else + checks all functions + + Outputs: + + Purpose: + +\*******************************************************************/ + +summary_checker_baset::resultt summary_checker_baset::check_properties() +{ + std::set seen_function_calls; + return check_properties("", "", seen_function_calls, false); +} + +summary_checker_baset::resultt summary_checker_baset::check_properties(irep_idt entry_function) +{ + std::set seen_function_calls; + return check_properties(entry_function, entry_function, seen_function_calls, false); +} + +summary_checker_baset::resultt summary_checker_baset::check_properties( + irep_idt function_name, + irep_idt entry_function, + std::set seen_function_calls, + bool is_inlined) +{ + if(function_name!="") + { + ssa_dbt::functionst::const_iterator f_it= + ssa_db.functions().find(function_name); + assert(f_it!=ssa_db.functions().end()); + local_SSAt &SSA=*f_it->second; + + // call recursively for all function calls first + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); ++n_it) + { + for(local_SSAt::nodet::function_callst::const_iterator ff_it= + n_it->function_calls.begin(); + ff_it!=n_it->function_calls.end(); ff_it++) + { + assert(ff_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(ff_it->function()).get_identifier(); + + // ENHANCE?: can the return value be exploited? + if(ssa_db.functions().find(fname)!=ssa_db.functions().end() && + (!summary_db.exists(fname) || + summary_db.get(fname).bw_transformer.is_nil())) + { +#if 0 + debug() << "Checking call " << fname << messaget::eom; +#endif + if(seen_function_calls.find(fname)==seen_function_calls.end()) + { + seen_function_calls.insert(fname); + check_properties( + fname, + entry_function, + seen_function_calls, + n_it->function_calls_inlined); + } + } + } + } + + if(!is_inlined) + { + // now check function itself + status() << "Checking properties of " << f_it->first << messaget::eom; + check_properties(f_it, entry_function); + } + } + else // check all the functions + { + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) + { + status() << "Checking properties of " << f_it->first << messaget::eom; + +#if 0 + // for debugging + show_ssa_symbols(*f_it->second, std::cerr); +#endif + + check_properties(f_it, f_it->first); + + if(options.get_bool_option("show-invariants")) + { + if(!summary_db.exists(f_it->first)) + continue; + show_invariants(*(f_it->second), summary_db.get(f_it->first), result()); + result() << eom; + } + } + } + + summary_checker_baset::resultt result=property_checkert::PASS; + if(function_name=="" || function_name==entry_function) + { + // determine overall status + for(property_mapt::const_iterator + p_it=property_map.begin(); p_it!=property_map.end(); p_it++) + { + if(p_it->second.result==FAIL) + return property_checkert::FAIL; + if(p_it->second.result==UNKNOWN) + result=property_checkert::UNKNOWN; + } + } + + return result; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::check_properties( + const ssa_dbt::functionst::const_iterator f_it, + irep_idt entry_function) +{ + unwindable_local_SSAt &SSA=*f_it->second; + + // check whether function has assertions + if(!has_assertion(f_it->first)) + return; + + bool all_properties=options.get_bool_option("all-properties"); + bool build_error_trace= + options.get_bool_option("show-trace") || + options.get_option("graphml-witness")!="" || + options.get_option("json-cex")!=""; + + SSA.output_verbose(debug()); debug() << eom; + + // incremental version + + // solver + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + + exprt enabling_expr=SSA.get_enabling_exprs(); + solver << enabling_expr; + + // invariant, calling contexts + if(summary_db.exists(f_it->first)) + { + if(!summary_db.get(f_it->first).fw_invariant.is_nil()) + solver << summary_db.get(f_it->first).fw_invariant; + if(!summary_db.get(f_it->first).fw_precondition.is_nil()) + solver << summary_db.get(f_it->first).fw_precondition; + } + + // callee summaries + solver << ssa_inliner.get_summaries(SSA); + + // spuriousness checkers + summarizer_bw_cex_baset *summarizer_bw_cex=nullptr; + incremental_solvert *cex_complete_solver= + incremental_solvert::allocate( + SSA.ns, + options.get_bool_option("refine")); +#if 1 + cex_complete_solver->set_message_handler(get_message_handler()); +#endif + if(options.get_option("spurious-check")=="abstract") + { + summarizer_bw_cex=new summarizer_bw_cex_ait( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + entry_function, + f_it->first); + } + else if(options.get_option("spurious-check")=="complete") + { + summarizer_bw_cex=new summarizer_bw_cex_completet( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); + } + else if(options.get_option("spurious-check")=="wp") + { + summarizer_bw_cex=new summarizer_bw_cex_wpt( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); + } + else if(options.get_option("spurious-check")=="all") + { + summarizer_bw_cex=new summarizer_bw_cex_allt( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); + } + else //NOLINT(*) +#if 0 + if(options.get_bool_option("inline") || + options.get_option("spurious-check")=="concrete") +#endif + { + summarizer_bw_cex=new summarizer_bw_cex_concretet( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + entry_function, + f_it->first); + } + assert(summarizer_bw_cex!=nullptr); + summarizer_bw_cex->set_message_handler(get_message_handler()); + + cover_goals_extt cover_goals( + SSA, + solver, + property_map, + all_properties, + build_error_trace, + *summarizer_bw_cex); + +#if 0 + debug() << "(C) " << from_expr(SSA.ns, "", enabling_expr) << eom; +#endif + + const goto_programt &goto_program=SSA.goto_function.body; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + if(!i_it->is_assert()) + continue; + + const source_locationt &location=i_it->source_location; + irep_idt property_id=location.get_property_id(); + + if(i_it->guard.is_true()) + { + property_map[property_id].result=PASS; + continue; + } + + // do not recheck properties that have already been decided + if(property_map[property_id].result!=UNKNOWN) + continue; + + // TODO: some properties do not show up in initialize_property_map + if(property_id=="") + continue; + + std::list assertion_nodes; + SSA.find_nodes(i_it, assertion_nodes); + + unsigned property_counter=0; + for(std::list::const_iterator + n_it=assertion_nodes.begin(); + n_it!=assertion_nodes.end(); + n_it++) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it=(*n_it)->assertions.begin(); + a_it!=(*n_it)->assertions.end(); + a_it++, property_counter++) + { + exprt property=*a_it; + + if(simplify) + property=::simplify_expr(property, SSA.ns); + +#if 0 + std::cout << "property: " << from_expr(SSA.ns, "", property) + << std::endl; +#endif + + property_map[property_id].location=i_it; + cover_goals.goal_map[property_id].conjuncts.push_back(property); + } + } + } + + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++) + { + // Our goal is to falsify a property. + // The following is TRUE if the conjunction is empty. + cover_goals.add(conjunction(it->second.conjuncts)); + } + + status() << "Running " << solver.solver->decision_procedure_text() << eom; + + cover_goals(); + + // set all non-covered goals to PASS except if we do not try + // to cover all goals and we have found a FAIL + if(all_properties || cover_goals.number_covered()==0) + { + std::list::const_iterator g_it= + cover_goals.goals.begin(); + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++, g_it++) + { + if(!g_it->covered) + property_map[it->first].result=PASS; + } + } + + solver.pop_context(); + + debug() << "** " << cover_goals.number_covered() + << " of " << cover_goals.size() << " failed (" + << cover_goals.iterations() << " iterations)" << eom; +} + +/*******************************************************************\ + +Function: summary_checker_baset::report_statistics + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::report_statistics() +{ + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) + { + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + unsigned calls=solver.get_number_of_solver_calls(); + if(calls>0) + solver_instances++; + solver_calls+=calls; + } + statistics() << "** statistics: " << eom; + statistics() << " number of solver instances: " << solver_instances << eom; + statistics() << " number of solver calls: " << solver_calls << eom; + statistics() << " number of summaries used: " + << summaries_used << eom; + statistics() << " number of termination arguments computed: " + << termargs_computed << eom; + statistics() << eom; +} + +/*******************************************************************\ + +Function: summary_checker_baset::do_show_vcc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::do_show_vcc( + const local_SSAt &SSA, + const goto_programt::const_targett i_it, + const local_SSAt::nodet::assertionst::const_iterator &a_it) +{ + std::cout << i_it->source_location << "\n"; + std::cout << i_it->source_location.get_comment() << "\n"; + + std::list ssa_constraints; + ssa_constraints << SSA; + + unsigned i=1; + for(std::list::const_iterator c_it=ssa_constraints.begin(); + c_it!=ssa_constraints.end(); + c_it++, i++) + std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; + + std::cout << "|--------------------------\n"; + + std::cout << "{1} " << from_expr(SSA.ns, "", *a_it) << "\n"; + + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summary_checker_baset::instrument_and_output + + Inputs: + + Outputs: + + Purpose: instruments the code with the inferred information + and outputs it to a goto-binary + +\*******************************************************************/ + +void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) +{ + instrument_gotot instrument_goto(options, ssa_db, summary_db); + instrument_goto(goto_model); + std::string filename=options.get_option("instrument-output"); + status() << "Writing instrumented goto-binary " << filename << eom; + write_goto_binary( + filename, + goto_model.symbol_table, + goto_model.goto_functions, + get_message_handler()); +} + + +/*******************************************************************\ + +Function: summary_checker_baset::has_assertion + + Inputs: + + Outputs: + + Purpose: searches recursively for assertions in inlined functions + +\*******************************************************************/ + +bool summary_checker_baset::has_assertion(irep_idt function_name) +{ + // SSA.goto_function.body.has_assertion() has become too semantic + bool _has_assertion=false; + const local_SSAt &SSA=ssa_db.get(function_name); + + for(local_SSAt::nodest::const_iterator + n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); ++n_it) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); a_it!=n_it->assertions.end(); ++a_it) + { + irep_idt property_id=n_it->location->source_location.get_property_id(); + + if(n_it->location->guard.is_true()) + property_map[property_id].result=PASS; + else + _has_assertion=true; + } + if(!n_it->function_calls_inlined) + continue; + + for(local_SSAt::nodet::function_callst::const_iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); ++f_it) + { + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + if(ssa_db.functions().find(fname)==ssa_db.functions().end()) + continue; + + bool new_has_assertion=has_assertion(fname); // recurse + _has_assertion=_has_assertion || new_has_assertion; + } + } + + return _has_assertion; +} diff --git a/src/summarizer/summary_checker_base.h b/src/2ls/summary_checker_base.h similarity index 61% rename from src/summarizer/summary_checker_base.h rename to src/2ls/summary_checker_base.h index db0f0c6da..c758f4ed9 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/2ls/summary_checker_base.h @@ -6,33 +6,35 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_BASE_H -#define CPROVER_SUMMARY_CHECKER_BASE_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_BASE_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_BASE_H #include - #include #include +#include +#include +#include +#include +#include +#include + #include "cover_goals_ext.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/ssa_inliner.h" -#include "../domains/incremental_solver.h" -#include "ssa_db.h" -#include "summary_db.h" + +class graphml_witness_extt; class summary_checker_baset:public property_checkert { public: - inline summary_checker_baset(optionst &_options): + explicit summary_checker_baset(optionst &_options): show_vcc(false), simplify(false), fixed_point(false), options(_options), - ssa_db(_options),summary_db(), + ssa_db(_options), summary_db(), ssa_unwinder(ssa_db), - ssa_inliner(summary_db), + ssa_inliner(summary_db, ssa_db), solver_instances(0), solver_calls(0), summaries_used(0), @@ -40,7 +42,7 @@ class summary_checker_baset:public property_checkert { ssa_inliner.set_message_handler(get_message_handler()); } - + bool show_vcc, simplify, fixed_point; irep_idt function_to_check; @@ -73,23 +75,25 @@ class summary_checker_baset:public property_checkert void SSA_functions(const goto_modelt &, const namespacet &ns); - void summarize(const goto_modelt &, - bool forward=true, bool termination=false); + void summarize( + const goto_modelt &, + bool forward=true, + bool termination=false); property_checkert::resultt check_properties(); + property_checkert::resultt check_properties(irep_idt entry_function); + property_checkert::resultt check_properties( + irep_idt function_name, + irep_idt entry_function, + std::set seen_function_calls, + bool is_inlined); void check_properties( - const ssa_dbt::functionst::const_iterator f_it); - - exprt::operandst get_loophead_selects( - const irep_idt &function_name, const local_SSAt &, prop_convt &); - bool is_spurious(const exprt::operandst& loophead_selects, - incremental_solvert&); - exprt::operandst get_loop_continues( - const irep_idt &function_name, const local_SSAt &, prop_convt &); - bool is_fully_unwound( - const exprt::operandst& loop_continues, - const exprt::operandst& loophead_selects, - incremental_solvert&); + const ssa_dbt::functionst::const_iterator f_it, + irep_idt entry_function=""); + + bool has_assertion(irep_idt function_name); + + friend graphml_witness_extt; }; #endif diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/2ls/summary_checker_bmc.cpp similarity index 58% rename from src/summarizer/summary_checker_bmc.cpp rename to src/2ls/summary_checker_bmc.cpp index c16c29477..38e9e925e 100644 --- a/src/summarizer/summary_checker_bmc.cpp +++ b/src/2ls/summary_checker_bmc.cpp @@ -1,6 +1,6 @@ /*******************************************************************\ -Module: Summarizer Checker for BMC +Module: Summary Checker for BMC Author: Peter Schrammel @@ -26,31 +26,31 @@ property_checkert::resultt summary_checker_bmct::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model,ns); + SSA_functions(goto_model, ns); - ssa_unwinder.init(false,true); + ssa_unwinder.init(false, true); - property_checkert::resultt result = property_checkert::UNKNOWN; - unsigned max_unwind = options.get_unsigned_int_option("unwind"); + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); status() << "Max-unwind is " << max_unwind << eom; ssa_unwinder.init_localunwinders(); - for(unsigned unwind = 0; unwind<=max_unwind; unwind++) + for(unsigned unwind=0; unwind<=max_unwind; unwind++) { status() << "Unwinding (k=" << unwind << ")" << messaget::eom; summary_db.mark_recompute_all(); ssa_unwinder.unwind_all(unwind); - result = check_properties(); - if(result == property_checkert::PASS) + result=check_properties(); + if(result==property_checkert::PASS) { - status() << "incremental BMC proof found after " - << unwind << " unwinding(s)" << messaget::eom; + status() << "incremental BMC proof found after " + << unwind << " unwinding(s)" << messaget::eom; break; } - else if(result == property_checkert::FAIL) + else if(result==property_checkert::FAIL) { - status() << "incremental BMC counterexample found after " - << unwind << " unwinding(s)" << messaget::eom; + status() << "incremental BMC counterexample found after " + << unwind << " unwinding(s)" << messaget::eom; break; } } diff --git a/src/summarizer/summary_checker_bmc.h b/src/2ls/summary_checker_bmc.h similarity index 73% rename from src/summarizer/summary_checker_bmc.h rename to src/2ls/summary_checker_bmc.h index 8345a41fe..5885f6b36 100644 --- a/src/summarizer/summary_checker_bmc.h +++ b/src/2ls/summary_checker_bmc.h @@ -6,21 +6,20 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_BMC_H -#define CPROVER_SUMMARY_CHECKER_BMC_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_BMC_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_BMC_H #include "summary_checker_base.h" class summary_checker_bmct:public summary_checker_baset { public: - inline summary_checker_bmct(optionst &_options): + explicit summary_checker_bmct(optionst &_options): summary_checker_baset(_options) { } - - virtual resultt operator()(const goto_modelt &); + virtual resultt operator()(const goto_modelt &); }; #endif diff --git a/src/2ls/summary_checker_kind.cpp b/src/2ls/summary_checker_kind.cpp new file mode 100644 index 000000000..5c13a3a92 --- /dev/null +++ b/src/2ls/summary_checker_kind.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Summary Checker for k-induction + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_kind.h" + +#define GIVE_UP_INVARIANTS 4 + +/*******************************************************************\ + +Function: summary_checker_kindt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_kindt::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns); + + ssa_unwinder.init(true, false); + + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind=0; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << eom; + + // TODO: recompute only functions with loops + summary_db.mark_recompute_all(); + + ssa_unwinder.unwind_all(unwind); + + result=check_properties(); + bool magic_limit_not_reached= + unwind $@ - -############################################################################### - -deltacheck$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/deltacheck/analyzer.cpp b/src/deltacheck/analyzer.cpp deleted file mode 100644 index d5d00af92..000000000 --- a/src/deltacheck/analyzer.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -//#include -//#include - -#include "../html/html_escape.h" -#include "../functions/path_util.h" - -#include "html_report.h" -#include "ssa_fixed_point.h" -#include "statistics.h" -#include "report_source_code.h" -#include "analyzer.h" -#include "change_impact.h" - -class deltacheck_analyzert:public messaget -{ -public: - deltacheck_analyzert( - const std::string &_path_old, - const goto_modelt &_goto_model_old, - const std::string &_path_new, - const goto_modelt &_goto_model_new, - const optionst &_options, - message_handlert &message_handler): - messaget(message_handler), - path_old(_path_old), - path_new(_path_new), - goto_model_old(_goto_model_old), - goto_model_new(_goto_model_new), - options(_options) - { - } - - statisticst statistics; - - void operator()(); - -protected: - const std::string &path_old; - const std::string &path_new; - const goto_modelt &goto_model_old; - const goto_modelt &goto_model_new; - const optionst &options; - - change_impactt change_impact; - - void check_function( - const irep_idt &, - std::ostream &global_report); - - void check_all(std::ostream &global_report); - - unsigned errors_in_file, passed_in_file, - unknown_in_file, unaffected_in_file, - LOCs_in_file; - - void collect_statistics(const propertiest &); - void collect_statistics(const goto_functionst::goto_functiont &); -}; - -/*******************************************************************\ - -Function: deltacheck_analyzert::check_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::check_function( - const irep_idt &function, - std::ostream &global_report) -{ - const goto_functionst::function_mapt::const_iterator - fmap_it_new=goto_model_new.goto_functions.function_map.find(function); - - if(fmap_it_new==goto_model_new.goto_functions.function_map.end()) - { - error() << "failed to find function `" << function - << "'" << eom; - return; - } - - const goto_functionst::goto_functiont &fkt_new= - fmap_it_new->second; - - // update statistics - LOCs_in_file+=fkt_new.body.instructions.size(); - collect_statistics(fkt_new); - statistics.number_map["Functions"]++; - - // Is this function at all affected? - if(!change_impact.function_map[function].is_affected()) - { - status() << "Function \"" << function << "\" is not affected" << eom; - - unsigned count=0; - forall_goto_program_instructions(i_it, fkt_new.body) - if(i_it->is_assert()) - count++; - - unaffected_in_file+=count; - statistics.number_map["Unaffected"]+=count; - return; // next function - } - - status() << "Checking \"" << function << "\"" << eom; - - const namespacet ns_new(goto_model_new.symbol_table); - const namespacet ns_old(goto_model_old.symbol_table); - - const symbolt &symbol_new=ns_new.lookup(function); - - // get corresponding goto_model_old function, if available - - const goto_functionst::function_mapt::const_iterator - fmap_it_old=goto_model_old.goto_functions.function_map.find(function); - - goto_functionst::goto_functiont fkt_old_dummy; - symbolt symbol_old_dummy; - - const goto_functionst::goto_functiont &fkt_old= - fmap_it_old==goto_model_old.goto_functions.function_map.end()?fkt_old_dummy: - fmap_it_old->second; - - const symbolt &symbol_old= - fmap_it_old==goto_model_old.goto_functions.function_map.end()?symbol_old_dummy: - ns_old.lookup(function); - - // set up report - - std::string report_file_name= - make_relative_path(path_new, "deltacheck."+id2string(function)+".html"); - - std::ofstream function_report(report_file_name.c_str()); - - html_report_header("Function "+id2string(symbol_new.display_name()), function_report); - - // build SSA for each - status() << "Building SSA" << eom; - statistics.start("SSA"); - local_SSAt SSA_old(fkt_old, ns_old, "@old"); - local_SSAt SSA_new(fkt_new, ns_new); - statistics.stop("SSA"); - - // add assertions in old version as assumptions - SSA_old.assertions_to_constraints(); - - // now do _joint_ fixed-point - namespacet joint_ns( - ns_new.get_symbol_table(), - ns_old.get_symbol_table()); - status() << "Joint data-flow fixed-point" << eom; - statistics.start("Fixed-point"); - ssa_fixed_pointt ssa_fixed_point(SSA_old, SSA_new, joint_ns); - statistics.stop("Fixed-point"); - - // now report on assertions - std::string description_old= - options.get_option("description-old"); - - std::string description_new= - options.get_option("description-new"); - - status() << "Reporting" << eom; - statistics.start("Reporting"); - //report_properties(ssa_fixed_point.properties, function_report); - report_properties(ssa_fixed_point.properties, *this); - report_countermodels(SSA_old, SSA_new, - ssa_fixed_point.properties, function_report); - report_source_code( - path_old, symbol_old.location, fkt_old.body, description_old, - path_new, symbol_new.location, fkt_new.body, description_new, - ssa_fixed_point.properties, - function_report, get_message_handler()); - statistics.stop("Reporting"); - - // dump statistics - statistics.html_report_last(function_report); - - // collect some more data - #if 0 - collect_statistics(ssa_fixed_point.properties); - #endif - - function_report << "\n"; - - #if 0 - global_report << "\n" - << "" - << "" - << "" - << "\n"; - #endif - - #if 0 - // add link to global report - global_report << "" - << "" - << "" - << "\n"; - } - - global_report << "
FileLOCs# Errors
" << html_escape(file_it->first) - << "" << LOCs_in_file << "" << errors_in_file << "
\n\n"; - #endif -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::check_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::check_all(std::ostream &global_report) -{ - // we do this by function in the new goto_model - for(goto_functionst::function_mapt::const_iterator - fmap_it=goto_model_new.goto_functions.function_map.begin(); - fmap_it!=goto_model_new.goto_functions.function_map.end(); - fmap_it++) - { - check_function(fmap_it->first, global_report); - } -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::collect_statistics - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::collect_statistics( - const goto_functionst::goto_functiont &goto_function) -{ - statistics.number_map["LOCs"]+=goto_function.body.instructions.size(); -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::collect_statistics - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::collect_statistics( - const propertiest &properties) -{ - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++) - { - if(p_it->status.is_false()) - { - errors_in_file++; - statistics.number_map["Errors"]++; - } - else if(p_it->status.is_true()) - { - passed_in_file++; - statistics.number_map["Passed"]++; - } - else - { - unknown_in_file++; - statistics.number_map["Unknown"]++; - } - } -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::operator()() -{ - statistics.start("Total-time"); - - std::string report_file_name= - make_relative_path(path_new, "deltacheck.html"); - - std::ofstream out(report_file_name.c_str()); - - if(!out) - { - error() << "failed to write to \"" - << report_file_name << "\"" << eom; - return; - } - - status() << "Writing report into \"" - << report_file_name << "\"" << eom; - - std::string title="DeltaCheck Summary"; - - html_report_header( - out, options.get_option("description-old"), - options.get_option("description-new"), title); - - statistics.start("Change-impact"); - status() << "Computing syntactic difference" << eom; - change_impact.diff(goto_model_old, goto_model_new); - status() << "Change-impact analysis" << eom; - change_impact.change_impact(goto_model_new); - statistics.stop("Change-impact"); - - status() << "Starting analysis" << eom; - - if(options.get_option("function")!="") - check_function(options.get_option("function"), out); - else - check_all(out); - - statistics.stop("Total-time"); - - // Report grand totals - - out << "

Summary statistics

\n"; - statistics.html_report_total(out); - - result() << "Properties unaffected: " << statistics.number_map["Unaffected"] << eom; - result() << "Properties passed: " << statistics.number_map["Passed"] << eom; - result() << "Properties failed: " << statistics.number_map["Errors"] << eom; - result() << "Properties warned: " << statistics.number_map["Unknown"] << eom; - - messaget::statistics() << "LOCs analyzed: " << statistics.number_map["LOCs"] << eom; - messaget::statistics() << "Functions analyzed: " << statistics.number_map["Functions"] << eom; - - memory_info(messaget::statistics()); - messaget::statistics() << eom; - - html_report_footer(out); - - // Write some statistics into a JSON file, for the benefit - // of other programs. - - std::string stat_file_name= - make_relative_path(path_new, "deltacheck-stat.json"); - std::ofstream json_out(stat_file_name.c_str()); - - json_out << "{\n"; - json_out << " \"properties\": {\n"; - json_out << " \"unaffected\": " << statistics.number_map["Unaffected"] << ",\n"; - json_out << " \"passed\": " << statistics.number_map["Passed"] << ",\n"; - json_out << " \"failed\": " << statistics.number_map["Errors"] << ",\n"; - json_out << " \"warned\": " << statistics.number_map["Unknown"] << "\n"; - json_out << " },\n"; - json_out << " \"program\": {\n"; - json_out << " \"LOCs\": " << statistics.number_map["LOCs"] << ",\n"; - json_out << " \"functions\": " << statistics.number_map["Functions"] << "\n"; - json_out << " }\n"; - json_out << "}\n"; -} - -/*******************************************************************\ - -Function: deltacheck_analyzer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzer( - const std::string &path1, - const goto_modelt &goto_model1, - const std::string &path2, - const goto_modelt &goto_model2, - const optionst &options, - message_handlert &message_handler) -{ - deltacheck_analyzert checker( - path1, goto_model1, - path2, goto_model2, - options, message_handler); - checker(); -} diff --git a/src/deltacheck/analyzer.h b/src/deltacheck/analyzer.h deleted file mode 100644 index a83d0e163..000000000 --- a/src/deltacheck/analyzer.h +++ /dev/null @@ -1,26 +0,0 @@ -/*******************************************************************\ - -Module: Main Checker Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CHECKER_H -#define CPROVER_DELTACHECK_CHECKER_H - -#include - -#include - -class message_handlert; - -void deltacheck_analyzer( - const std::string &path1, - const goto_modelt &goto_model1, - const std::string &path2, - const goto_modelt &goto_model2, - const optionst &options, - message_handlert &); - -#endif diff --git a/src/deltacheck/change_impact.cpp b/src/deltacheck/change_impact.cpp deleted file mode 100644 index 533512603..000000000 --- a/src/deltacheck/change_impact.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include "../functions/get_function.h" -#include "change_impact.h" - -/*******************************************************************\ - -Function: change_impactt::diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::diff( - const goto_modelt &old_model, - const goto_modelt &new_model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=new_model.goto_functions.function_map.begin(); - new_fkt_it!=new_model.goto_functions.function_map.end(); - new_fkt_it++) - { - // try to find 'corresponding function' in old_model - goto_functionst::function_mapt::const_iterator - old_fkt_it=old_model.goto_functions.function_map.find(new_fkt_it->first); - - if(old_fkt_it==old_model.goto_functions.function_map.end()) - function_map[new_fkt_it->first].fully_changed=true; - else - diff_functions(new_fkt_it->first, old_fkt_it->second, new_fkt_it->second); - } -} - -/*******************************************************************\ - -Function: change_impactt::diff_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::diff_functions( - const irep_idt &function_id, - const goto_functionst::goto_functiont &old_f, - const goto_functionst::goto_functiont &new_f) -{ - const goto_programt &old_body=old_f.body; - const goto_programt &new_body=new_f.body; - - // build branch target maps - - std::map old_target_map; - - forall_goto_program_instructions(it, old_body) - { - unsigned nr=old_target_map.size(); - old_target_map[it->location_number]=nr; - } - - std::map new_target_map; - - forall_goto_program_instructions(it, new_body) - { - unsigned nr=new_target_map.size(); - new_target_map[it->location_number]=nr; - } - - // now diff - datat &data=function_map[function_id]; - - goto_programt::instructionst::const_iterator - old_it=old_body.instructions.begin(); - - forall_goto_program_instructions(new_it, new_body) - { - if(new_it->is_skip() || - new_it->is_location() || - new_it->is_end_function()) - continue; - - while(old_it!=old_body.instructions.end() && - (old_it->is_skip() || - old_it->is_location() || - old_it->is_end_function())) - old_it++; - - if(new_it->type!=old_it->type || - new_it->guard!=old_it->guard || - new_it->code!=old_it->code || - (new_it->is_goto() && - new_target_map[new_it->get_target()->location_number]!= - old_target_map[old_it->get_target()->location_number])) - data.locs_changed.insert(new_it->location_number); - - old_it++; - } -} - -/*******************************************************************\ - -Function: change_impactt::output_diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::output_diff(std::ostream &out) -{ - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - { - if(fkt_it->second.fully_changed) - out << fkt_it->first << ": *\n"; - else if(!fkt_it->second.locs_changed.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_changed.begin(); - l_it!=fkt_it->second.locs_changed.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: change_impactt::output_change_impact - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::output_change_impact(std::ostream &out) -{ - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - { - if(fkt_it->second.fully_affected) - out << fkt_it->first << "\n"; - else if(!fkt_it->second.locs_affected.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_affected.begin(); - l_it!=fkt_it->second.locs_affected.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: change_impactt::change_impact - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::change_impact(const goto_modelt &new_model) -{ - std::stack working; - - // stash everything with change into the working set - for(function_mapt::const_iterator - function_it=function_map.begin(); - function_it!=function_map.end(); - function_it++) - { - if(function_it->second.has_change()) - { - working.push(function_it->first); - } - } - - // main loop - while(!working.empty()) - { - const irep_idt f_id=working.top(); - working.pop(); - - propagate_affected(new_model, f_id, working); - } -} - -/*******************************************************************\ - -Function: change_impactt::propagate_affected - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::propagate_affected( - const goto_modelt &new_model, - const irep_idt &f_id, - std::stack &working_fkts) -{ - datat &data=function_map[f_id]; - - if(data.fully_affected) return; // done already - - // get it - goto_functionst::function_mapt::const_iterator f_it= - new_model.goto_functions.function_map.find(f_id); - - if(f_it==new_model.goto_functions.function_map.end()) - return; // give up - - const goto_programt &body=f_it->second.body; - if(body.empty()) return; // give up - - std::stack working_locs; - - // put anything changed into working_locs - forall_goto_program_instructions(l, body) - if(data.locs_changed.find(l->location_number)!=data.locs_changed.end()) - working_locs.push(l); - - // put anything with an affected function call into working_locs - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - irep_idt called_f_id=symbol.get_identifier(); - if(function_map[called_f_id].is_affected()) - working_locs.push(l); - } - } - - while(!working_locs.empty()) - { - goto_programt::const_targett l=working_locs.top(); - working_locs.pop(); - - if(data.locs_affected.find(l->location_number)!=data.locs_affected.end()) - continue; // done already - - data.locs_affected.insert(l->location_number); - - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - irep_idt called_f_id=symbol.get_identifier(); - make_fully_affected(called_f_id); - } - } - - goto_programt::const_targetst successors; - - body.get_successors(l, successors); - - for(goto_programt::const_targetst::const_iterator - it=successors.begin(); - it!=successors.end(); - it++) - { - assert(body.instructions.end()!=*it); - working_locs.push(*it); - } - } -} - -/*******************************************************************\ - -Function: change_impactt::make_fully_affected - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::make_fully_affected(const irep_idt &f_id) -{ - std::stack working; - - working.push(f_id); - - while(!working.empty()) - { - const irep_idt f_id=working.top(); - working.pop(); - - datat &data=function_map[f_id]; - if(data.fully_affected) continue; - data.fully_affected=true; - - // recursively make all functions that are called fully affected - for(std::set::const_iterator - called_it=data.calls.begin(); - called_it!=data.calls.end(); - called_it++) - { - working.push(*called_it); - } - } -} - -/*******************************************************************\ - -Function: change_impactt::do_call_graph - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::do_call_graph( - const goto_modelt &model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=model.goto_functions.function_map.begin(); - new_fkt_it!=model.goto_functions.function_map.end(); - new_fkt_it++) - { - datat &data=function_map[new_fkt_it->first]; - - const goto_programt &body=new_fkt_it->second.body; - - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - const irep_idt called_f_id=symbol.get_identifier(); - data.calls.insert(called_f_id); - } - } - } -} diff --git a/src/deltacheck/change_impact.h b/src/deltacheck/change_impact.h deleted file mode 100644 index 3860841c3..000000000 --- a/src/deltacheck/change_impact.h +++ /dev/null @@ -1,73 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CHANGE_IMPACT_H -#define CPROVER_DELTACHECK_CHANGE_IMPACT_H - -#include - -class change_impactt:public messaget -{ -public: - void diff( - const goto_modelt &old_model, - const goto_modelt &new_model); - - void change_impact( - const goto_modelt &new_model); - - void output_diff(std::ostream &); - void output_change_impact(std::ostream &); - - struct datat - { - datat(): - fully_changed(false), - fully_affected(false) - { - } - - bool has_change() const - { - return fully_changed || !locs_changed.empty(); - } - - bool is_affected() const - { - return fully_affected || !locs_affected.empty(); - } - - bool fully_changed, fully_affected; - std::set locs_changed, locs_affected; - - std::set calls; - std::set called_by; - }; - - // functions to 'datat' map - typedef std::map function_mapt; - function_mapt function_map; - -protected: - void diff_functions( - const irep_idt &function_id, - const goto_functionst::goto_functiont &, - const goto_functionst::goto_functiont &); - - void propagate_affected( - const goto_modelt &new_index, - const irep_idt &id, - std::stack &working); - - void make_fully_affected(const irep_idt &); - - void do_call_graph( - const goto_modelt &); -}; - -#endif diff --git a/src/deltacheck/deltacheck_main.cpp b/src/deltacheck/deltacheck_main.cpp deleted file mode 100644 index c369d3d58..000000000 --- a/src/deltacheck/deltacheck_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "deltacheck_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - deltacheck_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - deltacheck_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/deltacheck/deltacheck_parse_options.cpp b/src/deltacheck/deltacheck_parse_options.cpp deleted file mode 100644 index 4938e4300..000000000 --- a/src/deltacheck/deltacheck_parse_options.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "deltacheck_parse_options.h" -#include "version.h" -#include "analyzer.h" -#include "change_impact.h" -#include "../functions/path_util.h" - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::deltacheck_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -deltacheck_parse_optionst::deltacheck_parse_optionst( - int argc, const char **argv): - parse_options_baset(DELTACHECK_OPTIONS, argc, argv), - xml_interfacet(cmdline), - ui_message_handler( - cmdline.isset("xml-ui")?ui_message_handlert::XML_UI:ui_message_handlert::PLAIN, - "DeltaCheck " DELTACHECK_VERSION) -{ -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::eval_verbosity - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::eval_verbosity() -{ - // our default verbosity - int v=messaget::M_STATISTICS; - - if(cmdline.isset("verbosity")) - { - v=unsafe_string2int(cmdline.get_value("verbosity")); - if(v<0) - v=0; - else if(v>10) - v=10; - } - - ui_message_handler.set_verbosity(v); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::get_command_line_options - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::get_command_line_options(optionst &options) -{ - if(config.set(cmdline)) - { - usage_error(); - exit(1); - } - - if(cmdline.isset("debug-level")) - options.set_option("debug-level", cmdline.get_value("debug-level")); - - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // do we do inlining? - if(cmdline.isset("no-inlining")) - options.set_option("partial-inlining", false); - else - options.set_option("partial-inlining", true); - - // check assertions - options.set_option("assertions", true); - - // use assumptions - options.set_option("assumptions", true); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::register_langauges - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::register_languages() -{ - register_language(new_ansi_c_language); - register_language(new_cpp_language); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int deltacheck_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - register_languages(); - - // command line options - - optionst options; - get_command_line_options(options); - set_message_handler(ui_message_handler); - eval_verbosity(); - - try - { - options.set_option("simplify", true); - options.set_option("assertions", true); - options.set_option("assumptions", true); - - if(cmdline.isset("function")) - options.set_option("function", cmdline.get_value("function")); - - if(cmdline.args.size()!=2) - { - usage_error(); - return 10; - } - - if(cmdline.isset("description-old")) - options.set_option("description-old", cmdline.get_value("description-old")); - else - options.set_option("description-old", cmdline.args[0]); - - if(cmdline.isset("description-new")) - options.set_option("description-new", cmdline.get_value("description-new")); - else - options.set_option("description-new", cmdline.args[1]); - - status() << "Reading first GOTO program from file" << eom; - - goto_modelt goto_model1; - - if(read_goto_binary(cmdline.args[0], - goto_model1, get_message_handler())) - return 10; - - status() << "Reading second GOTO program from file" << eom; - - goto_modelt goto_model2; - - if(read_goto_binary(cmdline.args[1], - goto_model2, get_message_handler())) - return 10; - - if(cmdline.isset("show-diff")) - { - change_impactt change_impact; - change_impact.set_message_handler(get_message_handler()); - - change_impact.diff(goto_model1, goto_model2); - change_impact.output_diff(std::cout); - } - else if(cmdline.isset("show-change-impact")) - { - change_impactt change_impact; - change_impact.set_message_handler(get_message_handler()); - - status() << "Computing syntactic difference" << eom; - change_impact.diff(goto_model1, goto_model2); - - status() << "Change-impact analysis" << eom; - change_impact.change_impact(goto_model2); - - change_impact.output_change_impact(std::cout); - } - else - { - std::string path1=get_directory(cmdline.args[0]); - std::string path2=get_directory(cmdline.args[1]); - - deltacheck_analyzer( - path1, goto_model1, - path2, goto_model2, - options, get_message_handler()); - } - - return 0; - } - - catch(const char *e) - { - error() << e << eom; - return 13; - } - - catch(const std::string &e) - { - error() << e << eom; - return 13; - } - - catch(int) - { - return 13; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return 14; - } - - return 0; -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void deltacheck_parse_optionst::help() -{ - std::cout << - "\n" - "* * DELTACHECK " DELTACHECK_VERSION " - Copyright (C) 2011-2015 * *\n" - "* * based on CBMC " CBMC_VERSION " * *\n" - "* * Daniel Kroening * *\n" - "* * Oxford University, Computer Science Department * *\n" - "* * kroening@kroening.com * *\n" - "\n" - "Usage: Purpose:\n" - "\n" - " deltacheck [-?] [-h] [--help] show help\n" - " deltacheck prog1 prog2 delta check two programs\n" - "\n" - "Delta checking options:\n" - " --show-change-impact show syntactic change-impact\n" - " --description-old text description of old version\n" - " --description-new text description of new version\n" - "\n" - "Safety checks:\n" - " --bounds-check add array bounds checks\n" - " --div-by-zero-check add division by zero checks\n" - " --pointer-check add pointer checks\n" - " --signed-overflow-check add arithmetic over- and underflow checks\n" - " --unsigned-overflow-check add arithmetic over- and underflow checks\n" - " --nan-check add floating-point NaN checks\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - " --xml-interface stdio-XML interface\n" - "\n"; -} diff --git a/src/deltacheck/deltacheck_parse_options.h b/src/deltacheck/deltacheck_parse_options.h deleted file mode 100644 index dbeb2fa6d..000000000 --- a/src/deltacheck/deltacheck_parse_options.h +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H -#define CPROVER_DELTACHECK_PARSE_OPTIONS_H - -#include -#include -#include - -#include -#include - -#define DELTACHECK_OPTIONS \ - "(function):" \ - "(debug-level):" \ - "(xml-ui)(xml-interface)" \ - "(verbosity):(version)(index):(description-old):(description-new):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)(nan-check)" \ - "(show-ssa)(show-defs)(show-guards)(show-fixed-points)" \ - "(show-properties)(show-change-impact)(show-diff)" \ - "(no-inline)(sat)" - -class deltacheck_parse_optionst: - public parse_options_baset, - public xml_interfacet, - public messaget -{ -public: - virtual int doit(); - virtual void help(); - - deltacheck_parse_optionst( - int argc, const char **argv); - -protected: - virtual void register_languages(); - - virtual void get_command_line_options(optionst &options); - - void eval_verbosity(); - - ui_message_handlert ui_message_handler; -}; - -#endif diff --git a/src/deltacheck/get_source.cpp b/src/deltacheck/get_source.cpp deleted file mode 100644 index 228a3f77c..000000000 --- a/src/deltacheck/get_source.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/*******************************************************************\ - -Module: Get Source Code - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include - -#include "../functions/path_util.h" -#include "get_source.h" - -/*******************************************************************\ - -Function: fast_forward - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fast_forward(unsigned lines, std::istream &in) -{ - for(unsigned int i=0; i::max(), '\n'); -} - -/*******************************************************************\ - -Function: get_source - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_source( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - std::list &dest, - message_handlert &message_handler) -{ - messaget message(message_handler); - const irep_idt &file=location.get_file(); - - if(file=="") return; - if(goto_program.instructions.empty()) return; - - // split up path_prefix into directories - std::list directories; - - { - std::string tmp; - for(unsigned i=0; i=0; i--) - { - std::string prefix; - for(std::list::const_iterator - p_it=directories.begin(); - p_it!=directories.end(); - p_it++) - prefix+=*p_it; - - full_path= - make_relative_path(prefix, id2string(file)); - - if(access(full_path.c_str(), R_OK)==0) break; // found! - } - - std::ifstream in; - in.open(full_path.c_str()); - - if(!in) - { - message.error() << "failed to open source `" - << file << "'" << messaget::eom; - if(!directories.empty()) - message.error() << "also tried prefixes of `" << path_prefix << "'" - << messaget::eom; - dest.push_back(linet(file, 1, "/* failed to open source file */")); - dest.push_back(linet(file, 2, "/* "+full_path+" */")); - return; - } - - unsigned first_line=safe_string2unsigned(id2string(location.get_line())); - - if(first_line!=0) - fast_forward(first_line-1, in); - - // get last line of function - - const source_locationt &last=goto_program.instructions.back().source_location; - - if(last.get_file()!=file) - { - // Hm, function ends in a different file than it starts. - // Possible, but unusual. - return; - } - - unsigned end_line=safe_string2unsigned(id2string(last.get_line())); - - for(unsigned line_no=first_line; line_no<=end_line; line_no++) - { - std::string s; - if(!std::getline(in, s)) break; - dest.push_back(linet(file, line_no, s)); - } - -} diff --git a/src/deltacheck/get_source.h b/src/deltacheck/get_source.h deleted file mode 100644 index 081f40815..000000000 --- a/src/deltacheck/get_source.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Extract Source Code - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GET_SOURCE_H -#define CPROVER_GET_SOURCE_H - -#include -#include - -#include -#include - -struct linet -{ - explicit linet():line_no(0) { } - linet(const irep_idt &_file, unsigned _line_no, const std::string &_line): - file(_file), line_no(_line_no), line(_line) { } - irep_idt file; - unsigned line_no; - std::string line; -}; - -void get_source( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - std::list &dest, - message_handlert &message_handler); - -#endif diff --git a/src/deltacheck/html_report.cpp b/src/deltacheck/html_report.cpp deleted file mode 100644 index 455845b21..000000000 --- a/src/deltacheck/html_report.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../html/html_escape.h" -#include "../html/logo.h" - -#include "html_report.h" -#include "version.h" - -/*******************************************************************\ - -Function: html_report_header - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const char header[]= -#include "report_header.inc" -; - -void html_report_header( - const std::string &title, - std::ostream &out) -{ - out << "\n\n"; - - out << "\n"; - - out << "" << html_escape(title) << "\n\n"; - - out << header; - - out << "\n"; - - out << "\n" - "\n"; -} - -/*******************************************************************\ - -Function: html_report_header - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void html_report_header( - std::ostream &out, - const std::string &old_desc, - const std::string &new_desc, - const std::string &title) -{ - html_report_header(title, out); - - out << "\"DeltaCheck\n\n"; - - out << "

" << html_escape(title) << "

\n\n"; - - out << "

DeltaCheck version: " << DELTACHECK_VERSION << "

\n"; - - out << "

Software under analysis

\n"; - - out << "

\n" - << "\n"; - out << "\n" - << "
Old version:" << html_escape(old_desc) << "
New version:" << html_escape(new_desc) << "

\n"; -} - -/*******************************************************************\ - -Function: html_report_footer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void html_report_footer(std::ostream &out) -{ - out << "
\n" - "\n" - "
\n" - "DeltaCheck is © 2011–2015 Daniel Kroening, University of Oxford.\n" - "
\n" - "\n" - "\n" - "\n"; -} diff --git a/src/deltacheck/html_report.h b/src/deltacheck/html_report.h deleted file mode 100644 index 226e4e3b6..000000000 --- a/src/deltacheck/html_report.h +++ /dev/null @@ -1,27 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_HTML_REPORT_H -#define CPROVER_HTML_REPORT_H - -#include - -#include - -void html_report_header( - std::ostream &out, - const std::string &old_desc, const std::string &new_desc, - const std::string &title); - -void html_report_header( - const std::string &title, - std::ostream &out); - -void html_report_footer(std::ostream &out); - -#endif diff --git a/src/deltacheck/old/call_graph.cpp b/src/deltacheck/old/call_graph.cpp deleted file mode 100644 index f7d7501b5..000000000 --- a/src/deltacheck/old/call_graph.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: Call Graph - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "call_graph.h" - -/*******************************************************************\ - -Function: summary_to_call_graph - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_to_call_graph(const xmlt &xml, call_grapht &dest) -{ - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - irep_idt caller=f_it->get_attribute("id"); - - for(xmlt::elementst::const_iterator - c_it=f_it->elements.begin(); - c_it!=f_it->elements.end(); - c_it++) - { - if(c_it->name=="called") - { - irep_idt callee=c_it->get_attribute("id"); - dest.add(caller, callee); - } - } - } - } -} diff --git a/src/deltacheck/old/call_graph.h b/src/deltacheck/old/call_graph.h deleted file mode 100644 index 25c5079ec..000000000 --- a/src/deltacheck/old/call_graph.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CALL_GRAPH_H -#define CPROVER_DELTACHECK_CALL_GRAPH_H - -#include -#include - -void summary_to_call_graph(const xmlt &xml, call_grapht &dest); - -#endif diff --git a/src/deltacheck/old/canonicalize.cpp b/src/deltacheck/old/canonicalize.cpp deleted file mode 100644 index 18ce4b2d0..000000000 --- a/src/deltacheck/old/canonicalize.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/*******************************************************************\ - -Module: Partial Canonicalization of a Predicate - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include "canonicalize.h" - -/*******************************************************************\ - -Function: canonicalize_rec - -Inputs: - -Outputs: - -Purpose: - -\*******************************************************************/ - -void canonicalize_rec(exprt &expr, bool &negation) -{ - if(expr.id()==ID_not) - { - if(expr.operands().size()==1) - { - exprt tmp; - tmp.swap(expr.op0()); - negation=!negation; - canonicalize_rec(tmp, negation); - expr.swap(tmp); - } - } - else if(expr.id()==ID_notequal) - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_equal); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_ge) // we only use le and lt - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_lt); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_gt) // we only use le and lt - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_le); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_le || expr.id()==ID_lt) - { - if(expr.operands().size()==2) - { - - } - } - else if(expr.id()==ID_equal) - { - // we order the operands with < - assert(expr.operands().size()==2); - - if(expr.op0() -#include - -#include - -#include "cgraph_builder.h" - -cgraph_buildert::cgraph_buildert() -{ -} - -cgraph_buildert::~cgraph_buildert() -{ -} - -void -cgraph_buildert::analyze_module(const symbol_tablet& symbol_table, - const goto_functionst& functions) -{ - Forall_analyses(it, analyses) - { - (*it)->set_symbol_table(symbol_table); - } - - forall_goto_functions(it, functions) - { - const goto_functionst::goto_functiont& function = it->second; - - if (function.body_available) - { - std::cout << "=================================" << std::endl; - std::cout << "Analyzing function: " << it->first << std::endl; - analyze_function(it->first, function); - } - } - - std::cout << "===== AFTER COLLECTION =====" << std::endl; - print_analyses(std::cout); - - compute_fixpoints(); - - std::cout << "===== AFTER FIXPOINT =====" << std::endl; - print_analyses(std::cout); - - remove_invisible(); - - std::cout << "===== AFTER FILTERING =====" << std::endl; - print_analyses(std::cout); -} - -void -cgraph_buildert::analyze_function( - irep_idt current_function, - const goto_functionst::goto_functiont& function) -{ - Forall_analyses(it, analyses) - { - (*it)->enter_function(current_function); - forall_goto_program_instructions(it2, function.body) - { - (*it)->visit(*it2); - } - (*it)->exit_function(); - } -} - -void -cgraph_buildert::print_analyses(std::ostream& out) const -{ - forall_analyses(it, analyses) - { - out << " *** Analysis: " << (*it)->get_analysis_id() << std::endl; - (*it)->print(out); - out << std::endl; - } -} - -void -cgraph_buildert::compute_fixpoints() -{ - Forall_analyses(it, analyses) - { - (*it)->compute_fixpoint(); - } -} - -void -cgraph_buildert::remove_invisible() -{ - Forall_analyses(it, analyses) - { - (*it)->remove_invisible(); - } -} - -void -cgraph_buildert::serialize(const std::string& orig_file) -{ - forall_analyses(it, analyses) - { - std::string analysis_file = orig_file + (*it)->get_default_suffix(); - std::ofstream out(analysis_file.c_str()); - if (!out) - throw "Failed to write partial analysis result: " + analysis_file; - - (*it)->serialize(out); - - if (!out) { - out.close(); - throw "Failed to write the partial call graph file: " + analysis_file; - } - out.close(); - } -} - -void -cgraph_buildert::deserialize(const std::string& orig_file) -{ - Forall_analyses(it, analyses) - { - std::string analysis_file = orig_file + (*it)->get_default_suffix(); - std::ifstream in(analysis_file.c_str()); - if (!in) - throw "Failed to read the partial call graph file: " + analysis_file; - - (*it)->deserialize(in); - - if (!in) - { - in.close(); - throw "Failed to read the partial call graph file: " + analysis_file; - } - in.close(); - } -} - -void -cgraph_buildert::deserialize_list(std::istream& in) -{ - std::string line_str; - - while (getline(in, line_str)) - { - if (!line_str.empty()) - deserialize(line_str); - } - - if (in.bad()) - { - throw "Failed to read the list of call graph files."; - } - - std::cout << "===== AFTER LOAD =====" << std::endl; - print_analyses(std::cout); - compute_fixpoints(); - std::cout << "===== AFTER FIXPOINT =====" << std::endl; - print_analyses(std::cout); -} diff --git a/src/deltacheck/old/cgraph_builder.h b/src/deltacheck/old/cgraph_builder.h deleted file mode 100644 index 8cbe1dfb4..000000000 --- a/src/deltacheck/old/cgraph_builder.h +++ /dev/null @@ -1,69 +0,0 @@ -/*******************************************************************\ - -Module: Call graph builder, builds and stores partial call graphs -(per C file). - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CGRAPH_BUILDER_H -#define CPROVER_DELTACHECK_CGRAPH_BUILDER_H - -#include - -#include - -#include -#include - -#include "modular_code_analysis.h" - -#define Forall_analyses(it, analyses) \ - for(analysest::iterator it=(analyses).begin(); \ - it!=(analyses).end(); ++it) - -#define forall_analyses(it, analyses) \ - for(analysest::const_iterator it=(analyses).begin(); \ - it!=(analyses).end(); ++it) - -class cgraph_buildert { -public: - cgraph_buildert(); - ~cgraph_buildert(); - - void analyze_module(const symbol_tablet &symbol_table, - const goto_functionst& functions); - void analyze_function(irep_idt current_function, - const goto_functionst::goto_functiont& function); - - void add_analysis(modular_code_analysist* analysis) - { - analyses.push_back(analysis); - } - - void add_analyses(modular_code_analysist** analyses) - { - while (*analyses != 0) { - add_analysis(*analyses++); - } - } - - void serialize(const std::string& orig_file); - - void deserialize(const std::string& orig_file); - - void deserialize_list(std::istream& in); - -private: - typedef std::vector analysest; - - analysest analyses; - - void print_analyses(std::ostream& out) const; - void compute_fixpoints(); - void remove_invisible(); -}; - -#endif - diff --git a/src/deltacheck/old/collect_symbols.cpp b/src/deltacheck/old/collect_symbols.cpp deleted file mode 100644 index 9d4e2b7b2..000000000 --- a/src/deltacheck/old/collect_symbols.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_COLLECT_SYMBOLS_H -#define CPROVER_DELTACHECK_COLLECT_SYMBOLS_H - -#include - -#include - -void collect_symbols( - const goto_functionst::goto_functiont &goto_function, - find_symbols_sett &dest) -{ - -} - -#endif diff --git a/src/deltacheck/old/collect_symbols.h b/src/deltacheck/old/collect_symbols.h deleted file mode 100644 index b8c40bd1d..000000000 --- a/src/deltacheck/old/collect_symbols.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_COLLECT_SYMBOLS_H -#define CPROVER_DELTACHECK_COLLECT_SYMBOLS_H - -#include - -#include - -void collect_symbols( - const goto_functionst::goto_functiont &, - find_symbols_sett &); - -#endif diff --git a/src/deltacheck/old/data_flow.cpp b/src/deltacheck/old/data_flow.cpp deleted file mode 100644 index 3da5948e1..000000000 --- a/src/deltacheck/old/data_flow.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include "data_flow.h" - -/*******************************************************************\ - -Function: data_flowt::rename - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt data_flowt::rename( - kindt kind, - const exprt &src, - goto_programt::const_targett t) -{ - exprt tmp=src; - - if(tmp.id()==ID_symbol) - { - irep_idt identifier=to_symbol_expr(tmp).get_identifier(); - irep_idt new_identifier= - id2string(identifier)+"#"+i2string(t->location_number)+"v"+i2string(version)+ - (kind==OUT?"O":kind==OUT_TAKEN?"Ot":"I"); - to_symbol_expr(tmp).set_identifier(new_identifier); - } - else - { - Forall_operands(it, tmp) - *it=rename(kind, *it, t); - } - - return tmp; -} - -/*******************************************************************\ - -Function: data_flowt::guard - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -symbol_exprt data_flowt::guard(goto_programt::const_targett t) -{ - irep_idt id= - "data_flow::\\guard"+i2string(t->location_number)+"v"+i2string(version); - return symbol_exprt(id, bool_typet()); -} - -/*******************************************************************\ - -Function: data_flowt::out_is_in - - Inputs: - - Outputs: - - Purpose: adds the skip transformer for location t - -\*******************************************************************/ - -void data_flowt::out_is_in(goto_programt::const_targett t) -{ - // this says that v_OUT = v_IN - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); -} - -/*******************************************************************\ - -Function: data_flowt::transformer - - Inputs: - - Outputs: - - Purpose: adds the transformer for location t - -\*******************************************************************/ - -void data_flowt::transformer(goto_programt::const_targett t) -{ - if(t->is_assign()) - { - const code_assignt &assignment=to_code_assign(t->code); - - std::set assigned; - - if(assignment.lhs().id()==ID_symbol) - { - const exprt &lhs=assignment.lhs(); - const exprt &rhs=assignment.rhs(); - assigned.insert(lhs); - solver.set_equal(rename(OUT, lhs, t), rename(IN, rhs, t)); - } - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - if(assigned.find(*o_it)!=assigned.end()) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); - } - else if(t->is_assert()) - { - //if(assert_to_assume) - // solver.set_to_true(rename(OUT, t->guard, t)); - - out_is_in(t); - } - else if(t->is_function_call()) - { - const code_function_callt &function_call= - to_code_function_call(t->code); - - std::set assigned; - - if(function_call.lhs().id()==ID_symbol) - { - const exprt &lhs=function_call.lhs(); - assigned.insert(lhs); - } - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - if(assigned.find(*o_it)!=assigned.end()) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); - } - else - out_is_in(t); - - // Do guard. This is the OR of the incoming edges. -// for( - -} - -/*******************************************************************\ - -Function: data_flowt::collect_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::collect_objects(const exprt &src) -{ - forall_operands(it, src) - collect_objects(*it); - - if(src.id()==ID_symbol) - objects.insert(src); -} - -/*******************************************************************\ - -Function: data_flowt::collect_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::collect_objects(const goto_programt &goto_program) -{ - forall_goto_program_instructions(it, goto_program) - { - collect_objects(it->guard); - collect_objects(it->code); - } -} - -/*******************************************************************\ - -Function: data_flowt::join - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool data_flowt::join(goto_programt::const_targett t) -{ - // find facts that are true at all predecessors of t - // and make them true at t - - // build 'in' vector - solvert::var_sett in; - in.reserve(objects.size()); - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - { - in.push_back(rename(IN, *o_it, t)); - } - - // build list of matching 'out' vectors - std::list src; - - const loct &loc=loc_map[t]; - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - { - goto_programt::const_targett succ=*s_it; - goto_programt::const_targett succ_next=*s_it; - succ_next++; - - src.push_back(solvert::var_sett()); - solvert::var_sett &out=src.back(); - out.reserve(objects.size()); - - // connect branches to correct output - kindt kind= - (succ->is_goto() && succ_next!=t)?OUT_TAKEN:OUT; - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - { - out.push_back(rename(kind, *o_it, succ)); - } - - } - - return solver.join(src, in); -} - -/*******************************************************************\ - -Function: data_flowt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::operator()(const goto_programt &goto_program) -{ - // collect objects to track - collect_objects(goto_program); - - // collect locations - forall_goto_program_instructions(it, goto_program) - { - loct &loc=loc_map[it]; - - // build successors - goto_program.get_successors(it, loc.succ); - - // build predecessors - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - loc_map[*s_it].pred.push_back(it); - } - - // add the transformers for all instructions - forall_goto_program_instructions(it, goto_program) - transformer(it); - - // now do data flow equations - forall_goto_program_instructions(it, goto_program) - work_queue.push_back(it); - - while(!work_queue.empty()) - { - goto_programt::const_targett t=work_queue.back(); - work_queue.pop_back(); - - if(join(t)) - { - const loct &loc=loc_map[t]; - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - work_queue.push_back(*s_it); - } - } -} diff --git a/src/deltacheck/old/data_flow.h b/src/deltacheck/old/data_flow.h deleted file mode 100644 index ab3e1fbcf..000000000 --- a/src/deltacheck/old/data_flow.h +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "solver.h" - -class data_flowt -{ -public: - explicit data_flowt(solvert &_solver): - version(0), - assert_to_assume(false), - solver(_solver) - { - } - - unsigned version; - bool assert_to_assume; - - void operator()(const goto_programt &); - -protected: - solvert &solver; - - typedef enum { OUT, OUT_TAKEN, IN } kindt; - - exprt rename(kindt kind, const exprt &src, goto_programt::const_targett t); - typet rename(kindt kind, const typet &src, goto_programt::const_targett t); - - class symbol_exprt guard(goto_programt::const_targett t); - - void transformer(goto_programt::const_targett t); - void out_is_in(goto_programt::const_targett t); - - void collect_objects(const goto_programt &); - void collect_objects(const exprt &); - - typedef std::vector work_queuet; - work_queuet work_queue; - - struct loct - { - goto_programt::const_targetst succ, pred; - }; - - bool join(goto_programt::const_targett t); - - typedef std::map loc_mapt; - loc_mapt loc_map; - - typedef std::set objectst; - objectst objects; -}; diff --git a/src/deltacheck/old/delta_check.cpp b/src/deltacheck/old/delta_check.cpp deleted file mode 100644 index 8b94bf563..000000000 --- a/src/deltacheck/old/delta_check.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -#include "index.h" -#include "ssa_data_flow.h" -#include "html_report.h" -#include "get_function.h" -#include "delta_check.h" - -/*******************************************************************\ - -Function: delta_check_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void delta_check_all( - const indext &index1, - const indext &index2, - std::ostream &report, - message_handlert &message_handler) -{ - // we do this by file in index2 - - messaget message(message_handler); - - get_functiont get_function1(index1); - get_function1.set_message_handler(message_handler); - - for(indext::file_to_functiont::const_iterator - file_it=index2.file_to_function.begin(); - file_it!=index2.file_to_function.end(); - file_it++) - { - message.status() << "Processing \"" << file_it->first << "\"" - << messaget::eom; - - // read the file - goto_modelt model2; - read_goto_binary(id2string(file_it->first), model2, message_handler); - - const std::set &functions=file_it->second; - - // now do all functions from model2 - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - const goto_functionst::goto_functiont *index2_fkt= - &model2.goto_functions.function_map.find(id)->second; - - // get corresponding index1 function, if available - - const goto_functionst::goto_functiont *index1_fkt= - get_function1(id); - - if(index1_fkt!=NULL) - { - message.status("Delta Checking \""+id2string(id)+"\""); - - report << "

Function " << id << " in " << file_it->first - << "

\n"; - - #if 0 - function_delta(id, *index1_fkt, *index2_fkt, report, message_handler); - #endif - } - } - } -} - -/*******************************************************************\ - -Function: delta_check - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void delta_check( - const indext &index1, - const indext &index2, - message_handlert &message_handler) -{ - messaget message(message_handler); - - std::string report_file_name="deltacheck.html"; - std::ofstream out(report_file_name.c_str()); - - if(!out) - { - message.error() << "failed to write to \"" - << report_file_name << "\"" << messaget::eom; - return; - } - - message.status() << "Writing report into \"" - << report_file_name << "\"" << messaget::eom; - - html_report_header(out, index1, index2); - - delta_check_all(index1, index2, out, message_handler); - - html_report_footer(out, index1, index2); -} - -#if 0 -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "get_goto_program.h" -#include "xml_conversion.h" -#include "summarization.h" -#include "dependencies.h" -#include "function_transformer.h" - -//#include "cgraph_builder.h" -//#include "modular_fptr_analysis.h" -//#include "modular_globals_analysis.h" - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls_rec( - const namespacet &ns, - const goto_functionst &goto_functions, - const exprt &function, - std::set &called_functions) -{ - if(function.id()==ID_symbol) - { - irep_idt id=to_symbol_expr(function).get_identifier(); - const symbolt &symbol=ns.lookup(id); - if(!symbol.is_file_local) - called_functions.insert(id); - } - else if(function.id()==ID_dereference) - { - } - else if(function.id()==ID_if) - { - } -} - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - std::ostream &out) -{ - std::set called_functions; - - forall_goto_program_instructions(it, goto_function.body) - { - if(it->is_function_call()) - { - const exprt &function=to_code_function_call(it->code).function(); - - summarize_function_calls_rec( - ns, goto_functions, function, called_functions); - } - } - - for(std::set::const_iterator - it=called_functions.begin(); - it!=called_functions.end(); - it++) - { - out << " "; - out << "\n"; - } -} - -/*******************************************************************\ - -Function: summarize_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function( - const namespacet &ns, - const goto_functionst &goto_functions, - const symbolt &symbol, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - out << "\n"; - - if(symbol.location.is_not_nil() && - symbol.location.get_file()!="") - out << " " << xml(symbol.location); - - summarize_function_calls(ns, goto_functions, goto_function, out); - - function_transformer(ns, goto_functions, goto_function, message_handler, out); - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: dump_exported_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_exported_functions( - const namespacet &ns, - const goto_functionst &goto_functions, - message_handlert &message_handler, - std::ostream &out) -{ - out << "\n"; - - // do this for each function - forall_goto_functions(f_it, goto_functions) - { - if(!f_it->second.body_available) - continue; - - if(has_prefix(id2string(f_it->first), CPROVER_PREFIX)) - continue; - - const symbolt &symbol=ns.lookup(f_it->first); - - if(symbol.is_file_local) - continue; - - messaget message(message_handler); - message.status("Summarizing "+id2string(f_it->first)); - - summarize_function( - ns, goto_functions, symbol, f_it->second, message_handler, out); - } - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: dump_state_variables - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_state_variables( - const symbol_tablet &symbol_table, - std::ostream &out) -{ - out << "\n"; - - forall_symbols(s_it, symbol_table.symbols) - { - const symbolt &symbol=s_it->second; - - if(has_prefix(id2string(symbol.name), CPROVER_PREFIX)) - continue; - - if(symbol.type.id()==ID_code || - symbol.is_type) - continue; - - if(symbol.is_file_local) - continue; - - out << "\n"; - - if(symbol.location.is_not_nil() && symbol.location.get_file()!="") - out << xml(symbol.location); - - out << "\n"; - } - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const symbol_tablet &symbol_table, - const goto_functionst &goto_functions, - const optionst &options, - message_handlert &message_handler, - std::ostream &out) -{ - // first collect non-static function symbols that - // have a body - - namespacet ns(symbol_table); - - dump_exported_functions(ns, goto_functions, message_handler, out); - - dump_state_variables(symbol_table, out); - - #if 0 - cgraph_buildert cg_builder; - modular_fptr_analysist fptr_analysis; - modular_globals_analysist globals_analysis; - - cg_builder.add_analysis(&fptr_analysis); - cg_builder.add_analysis(&globals_analysis); - - cg_builder.analyze_module(symbol_table, goto_functions); - cg_builder.serialize(file_name); - #endif -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - // first check dependencies - if(!options.get_bool_option("force") && - dependencies(function_file_map, file_name, message_handler)==FRESH) - return; - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - //goto_functions.output(ns, std::cout); - - std::string summary_file_name=file_name+".summary"; - std::ofstream summary_file(summary_file_name.c_str(), - std::ios::binary|std::ios::trunc|std::ios::out); - - if(!summary_file) - throw std::string("failed to write summary file"); - - summary_file << "\n"; - - ::summarization( - function_file_map, - symbol_table, - goto_functions, - options, - message_handler, - summary_file); - - summary_file << "\n"; - - messaget message(message_handler); - message.status("Summary written as "+summary_file_name); -} - -#endif diff --git a/src/deltacheck/old/delta_check.h b/src/deltacheck/old/delta_check.h deleted file mode 100644 index 9c1596f7b..000000000 --- a/src/deltacheck/old/delta_check.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTA_CHECK_H -#define CPROVER_DELTA_CHECK_H - -class message_handlert; -class indext; - -void delta_check( - const indext &index1, - const indext &index2, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/dependencies.cpp b/src/deltacheck/old/dependencies.cpp deleted file mode 100644 index cccabbd46..000000000 --- a/src/deltacheck/old/dependencies.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Dependency Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include "dependencies.h" - -/*******************************************************************\ - -Function: dependencies - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -dependency_statet dependencies( - const function_file_mapt &function_file_map, - const std::string &file_name, - message_handlert &message_handler) -{ - std::string summary_file=file_name+".summary"; - - std::ifstream in(summary_file.c_str()); - - if(!in) return STALE; - - xmlt xml; - parse_xml(in, summary_file, message_handler, xml); - - return STALE; -} diff --git a/src/deltacheck/old/dependencies.h b/src/deltacheck/old/dependencies.h deleted file mode 100644 index ae308f2b7..000000000 --- a/src/deltacheck/old/dependencies.h +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: Dependency Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_DEPENDENCIES_H -#define CPROVER_DELTACHECK_DEPENDENCIES_H - -#include - -#include "function_file_map.h" - -typedef enum { STALE, FRESH } dependency_statet; - -dependency_statet dependencies( - const function_file_mapt &function_file_map, - const std::string &file_name, - class message_handlert &message_handler); - -#endif diff --git a/src/deltacheck/old/discover_predicates.cpp b/src/deltacheck/old/discover_predicates.cpp deleted file mode 100644 index 0a2004caf..000000000 --- a/src/deltacheck/old/discover_predicates.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*******************************************************************\ - -Module: Predicate Discovery - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "discover_predicates.h" -#include "canonicalize.h" - -/*******************************************************************\ - -Function: discover_predicates - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#include - -std::list discover_predicates( - const exprt &src, - const namespacet &ns) -{ - if(src.id()==ID_and || src.id()==ID_or) - { - std::list result; - - forall_operands(it, src) - { - std::list tmp=discover_predicates(*it, ns); - result.insert(result.end(), tmp.begin(), tmp.end()); - } - - return result; - } - else if(src.id()==ID_not) - { - assert(src.operands().size()==1); - return discover_predicates(src.op0(), ns); - } - else - { - exprt tmp=src; - bool negation; - canonicalize(tmp, negation, ns); - std::list result; - result.push_back(tmp); - return result; - } -} diff --git a/src/deltacheck/old/discover_predicates.h b/src/deltacheck/old/discover_predicates.h deleted file mode 100644 index 2330b2920..000000000 --- a/src/deltacheck/old/discover_predicates.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Predicate Discovery - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -std::list discover_predicates( - const exprt &src, - const namespacet &ns); diff --git a/src/deltacheck/old/function_delta.cpp b/src/deltacheck/old/function_delta.cpp deleted file mode 100644 index ba9e6dafa..000000000 --- a/src/deltacheck/old/function_delta.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "solver.h" -#include "function_delta.h" - -class function_deltat:public messaget -{ -public: - explicit function_deltat( - std::ostream &_out):out(_out), ns(symbol_table), solver(ns) - { - } - - void operator()( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2); - -protected: - std::ostream &out; - symbol_tablet symbol_table; - namespacet ns; - solvert solver; - - exprt rename_rhs(const exprt &src, goto_programt::const_targett t, unsigned v); - exprt rename_lhs(const exprt &src, goto_programt::const_targett t, unsigned v); - - void encode(const goto_functionst::goto_functiont &goto_function, unsigned v); -}; - -/*******************************************************************\ - -Function: function_deltat::encode - - Inputs: - - Outputs: - - Purpose: builds data-flow equations - -\*******************************************************************/ - -void function_deltat::encode( - const goto_functionst::goto_functiont &f, unsigned v) -{ - const goto_programt &body=f.body; - - forall_goto_program_instructions(i_it, body) - { - const goto_programt::instructiont &instruction=*i_it; - - if(instruction.is_goto()) - { - } - else if(instruction.is_assert()) - { - } - else if(instruction.is_assume()) - { - } - else - { - // treat like 'skip' - } - } -} - -/*******************************************************************\ - -Function: function_deltat::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_deltat::operator()( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2) -{ - if(!f2.body.has_assertion()) - { - status("New version has no properties"); - return; - } - - out << "

Function " << id << "

\n"; - - // encode both programs - encode(f1, 1); - encode(f2, 2); -} - -/*******************************************************************\ - -Function: function_delta - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_delta( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2, - std::ostream &out, - message_handlert &message_handler) -{ - function_deltat function_delta(out); - function_delta.set_message_handler(message_handler); - function_delta(id, f1, f2); -} - diff --git a/src/deltacheck/old/function_delta.h b/src/deltacheck/old/function_delta.h deleted file mode 100644 index 6dfdc7785..000000000 --- a/src/deltacheck/old/function_delta.h +++ /dev/null @@ -1,24 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_FUNCTION_DELTA_H -#define CPROVER_FUNCTION_DELTA_H - -#include - -#include -#include - -void function_delta( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/function_file_map.cpp b/src/deltacheck/old/function_file_map.cpp deleted file mode 100644 index 6870464ea..000000000 --- a/src/deltacheck/old/function_file_map.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Map from function names to the file - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "function_file_map.h" - -/*******************************************************************\ - -Function: build_function_file_map - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void build_function_file_map( - const std::list &files, - message_handlert &message_handler, - function_file_mapt &function_file_map) -{ - for(std::list::const_iterator - file_it=files.begin(); - file_it!=files.end(); - file_it++) - { - xmlt xml; - parse_xml(*file_it+".summary", message_handler, xml); - - irep_idt file=*file_it; - - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - irep_idt id=f_it->get_attribute("id"); - function_file_map[id]=file; - } - } - } -} diff --git a/src/deltacheck/old/function_file_map.h b/src/deltacheck/old/function_file_map.h deleted file mode 100644 index 85dc54e1f..000000000 --- a/src/deltacheck/old/function_file_map.h +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: Map from function names to the file - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_FUNCTION_FILE_MAP_H -#define CPROVER_DELTACHECK_FUNCTION_FILE_MAP_H - -#include - -#include - -typedef std::map function_file_mapt; - -void build_function_file_map( - const std::list &files, - class message_handlert &message_handler, - function_file_mapt &function_file_map); - -#endif diff --git a/src/deltacheck/old/function_transformer.cpp b/src/deltacheck/old/function_transformer.cpp deleted file mode 100644 index d500b44ac..000000000 --- a/src/deltacheck/old/function_transformer.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include - -#include "function_transformer.h" -#include "statement_transformer.h" -#include "collect_symbols.h" -#include "discover_predicates.h" -#include "predicates.h" - -class function_transformert -{ -public: - class locationt - { - public: - // the BDD for the guard - BDD guard; - - goto_programt::const_targett target; - unsigned PC; - }; - - typedef std::vector locationst; - locationst locations; - - typedef std::map location_mapt; - location_mapt location_map; - - unsigned target_PC(goto_programt::const_targett t) - { - location_mapt::const_iterator it=location_map.find(t); - assert(it!=location_map.end()); - return it->second; - } - - function_transformert( - const namespacet &_ns, - const goto_functionst &_goto_functions, - Cudd &_mgr, - message_handlert &_message_handler): - predicates(_ns), - ns(_ns), - goto_functions(_goto_functions), - mgr(_mgr), - message(_message_handler) - { - } - - void operator() (const goto_functionst::goto_functiont &); - - void output(std::ostream &out) const; - - predicatest predicates; - -protected: - const namespacet &ns; - const goto_functionst &goto_functions; - Cudd &mgr; - messaget message; - - void xml(BDD, std::ostream &) const; - - void setup_state_map(const goto_functionst::goto_functiont &goto_function) - { - unsigned PC=0; - locations.resize(goto_function.body.instructions.size()); - - forall_goto_program_instructions(i_it, goto_function.body) - { - locations[PC].target=i_it; - locations[PC].guard=!mgr.bddOne(); - locations[PC].PC=PC; - location_map[i_it]=PC++; - } - } - - void add(const std::list &); - - void discover_predicates( - const goto_functionst::goto_functiont &goto_function); - - void make_entry_state() - { - assert(!locations.empty()); - locations[0].guard=mgr.bddOne(); - } - - std::stack queue; - - void get_successors(unsigned PC); - - void merge(unsigned PC, BDD guard); -}; - -/*******************************************************************\ - -Function: function_transformert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::operator()( - const goto_functionst::goto_functiont &goto_function) -{ - setup_state_map(goto_function); - discover_predicates(goto_function); - message.debug("Predicates: "+predicates.make_list()); - - if(locations.empty()) return; - - // setup entry state, and put into queue - make_entry_state(); - queue.push(0); - - while(!queue.empty()) - { - unsigned PC=queue.top(); - queue.pop(); - - // compute successors and propagate - get_successors(PC); - } -} - -/*******************************************************************\ - -Function: function_transformert::get_successors - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::get_successors(unsigned PC) -{ - // end of function readched? - if(PC>=locations.size()) return; - - const locationt &from=locations[PC]; - - const goto_programt::instructiont &instruction= - *from.target; - - statement_transformert statement_transformer(predicates, mgr, ns); - - if(instruction.is_goto()) - { - // guarded? - if(instruction.guard.is_false()) - merge(PC+1, from.guard); - else - { - if(!instruction.guard.is_true()) - { - BDD new_guard= - statement_transformer.guard_not(from.guard, instruction.guard); - merge(PC+1, new_guard); - } - - // targets - for(goto_programt::instructiont::targetst::const_iterator - it=instruction.targets.begin(); - it!=instruction.targets.end(); - it++) - { - BDD new_guard= - statement_transformer.guard(from.guard, instruction.guard); - - merge(target_PC(*it), new_guard); - } - } - } - else if(instruction.is_assign()) - { - const code_assignt &code_assign=to_code_assign(instruction.code); - BDD new_guard= - statement_transformer.assign(from.guard, code_assign); - merge(PC+1, new_guard); - } - else if(instruction.is_function_call()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_assume()) - { - BDD new_guard= - statement_transformer.guard(new_guard, instruction.guard); - - merge(PC+1, new_guard); - } - else if(instruction.is_assert()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_other()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_start_thread()) - { - // targets - for(goto_programt::instructiont::targetst::const_iterator - it=instruction.targets.begin(); - it!=instruction.targets.end(); - it++) - { - merge(target_PC(*it), from.guard); - } - } - else if(instruction.is_end_thread()) - { - // no successor - } - else if(instruction.is_end_function()) - { - // no successor - } - else if(instruction.is_atomic_begin()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_atomic_end()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_return()) - { - BDD new_guard=from.guard; - // process return value - - // go to end-of-function - merge(locations.size()-1, new_guard); - } - else if(instruction.is_decl()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_dead()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_throw()) - { - // complex successor - } - else if(instruction.is_catch()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_skip() || - instruction.is_location()) - { - merge(PC+1, from.guard); - } - else - { - // treat like skip - merge(PC+1, from.guard); - } -} - -/*******************************************************************\ - -Function: function_transformert::merge - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::merge( - unsigned PC, - BDD new_guard) -{ - // end of function? - if(PC>=locations.size()) return; - - locationt &to=locations[PC]; - BDD old_guard=to.guard; - to.guard|=new_guard; // merge - - if(to.guard!=old_guard) - { - std::cout << "New state at PC " << PC << std::endl; - to.guard.print(predicates.size()*2); - queue.push(PC); // fixpoint not yet reached - } -} - -/*******************************************************************\ - -Function: function_transformert::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::add( - const std::list &new_predicates) -{ - for(std::list::const_iterator - it=new_predicates.begin(); - it!=new_predicates.end(); - it++) - { - predicates.add(mgr, *it); - } -} - -/*******************************************************************\ - -Function: function_transformert::discover_predicates - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::discover_predicates( - const goto_functionst::goto_functiont &goto_function) -{ - forall_goto_program_instructions(i_it, goto_function.body) - { - const goto_programt::instructiont &instruction=*i_it; - - if(instruction.is_assert()) - { - add(::discover_predicates(instruction.guard, ns)); - } - } -} - -/*******************************************************************\ - -Function: function_transformert::xml - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::xml(BDD bdd, std::ostream &out) const -{ - // dump DNF - assert((unsigned)mgr.ReadSize()>=predicates.size()*2); - - out << " " << std::endl; - - CUDD_VALUE_TYPE value; - int *cube; - DdGen *gen; - - Cudd_ForeachCube(mgr.getManager(), bdd.getNode(), gen, cube, value) - { - assert(gen!=NULL); - assert(cube!=NULL); - - out << " " << std::endl; - for(unsigned p=0; p"; - out << "" << std::endl; - if(cube[i]==0) out << ""; - out << std::endl; - break; - - case 2: // don't care - break; - - default: - assert(false); - } - } - out << " " << std::endl; - } - - out << " " << std::endl; -} - -/*******************************************************************\ - -Function: function_transformert::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::output(std::ostream &out) const -{ - out << " " << std::endl; - - // dump DNF - xml(locations.back().guard, out); - - out << " " << std::endl; -} - -/*******************************************************************\ - -Function: transformer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformer( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - Cudd mgr; - function_transformert function_transformer( - ns, goto_functions, mgr, message_handler); - - function_transformer(goto_function); - - function_transformer.output(out); -} diff --git a/src/deltacheck/old/function_transformer.h b/src/deltacheck/old/function_transformer.h deleted file mode 100644 index 098a064fa..000000000 --- a/src/deltacheck/old/function_transformer.h +++ /dev/null @@ -1,21 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include - -void function_transformer( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out); diff --git a/src/deltacheck/old/get_goto_program.cpp b/src/deltacheck/old/get_goto_program.cpp deleted file mode 100644 index 255afb85c..000000000 --- a/src/deltacheck/old/get_goto_program.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/*******************************************************************\ - -Module: Goto Program Preparation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -#include - -#include "get_goto_program.h" - -/*******************************************************************\ - -Function: get_goto_program - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void get_goto_program( - const std::string &file_name, - const optionst &options, - symbol_tablet &symbol_table, - goto_functionst &goto_functions, - message_handlert &message_handler) -{ - messaget message(message_handler); - message.status("Reading goto-program"); - - if(read_goto_binary( - file_name, - symbol_table, goto_functions, message_handler)) - throw std::string("failed to read goto binary ")+file_name; - - config.ansi_c.set_from_symbol_table(symbol_table); - - message.status("Preparing goto-program"); - - // finally add the library - link_to_library( - symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - // do partial inlining - goto_partial_inline(goto_functions, ns, message_handler); - - // add checks - goto_check(ns, options, goto_functions); - - // recalculate numbers, etc. - goto_functions.update(); - - // add loop ids - goto_functions.compute_loop_numbers(); -} - diff --git a/src/deltacheck/old/get_goto_program.h b/src/deltacheck/old/get_goto_program.h deleted file mode 100644 index 8749e600a..000000000 --- a/src/deltacheck/old/get_goto_program.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Goto Program Preparation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include - -void get_goto_program( - const std::string &file_name, - const optionst &options, - symbol_tablet &symbol_table, - goto_functionst &goto_functions, - message_handlert &message_handler); diff --git a/src/deltacheck/old/modular_analysis.h b/src/deltacheck/old/modular_analysis.h deleted file mode 100644 index c909e7681..000000000 --- a/src/deltacheck/old/modular_analysis.h +++ /dev/null @@ -1,246 +0,0 @@ -/*******************************************************************\ - -Module: Partial fixed-point analysis to be used for function-pointer alias -analysis per C file. - -The analysis accepts rules (subsumption among variables), values (constants), -and distinguishes visible and invisible variables. After fixpoint computation -the invisible variables can be removed. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARTIAL_ANALYSIS_H -#define CPROVER_DELTACHECK_PARTIAL_ANALYSIS_H - -#include - -#include - -template -class modular_analysist { -public: - typedef variableT variablet; - typedef valueT valuet; - typedef typename hash_set_cont valuest; - typedef typename hash_set_cont variablest; - typedef typename hash_map_cont value_mapt; - typedef typename hash_map_cont rulest; - - modular_analysist() {} - - bool add_rule(const variablet& subsumes, - const variablet& subsumed) - { - insert_variable(inverse_rules, subsumed, subsumes); - return insert_variable(rules, subsumes, subsumed); - } - - bool add_value(const variablet& variable, const valuet& value) - { - typename value_mapt::iterator it = value_map.find(variable); - - if (it == value_map.end()) - it = value_map.insert(typename value_mapt::value_type( - variable, valuest())).first; - - unsigned count = it->second.size(); - it->second.insert(value); - - return count != it->second.size(); - } - - bool add_values(const variablet& variable, const valuest& values) - { - typename value_mapt::iterator it = value_map.find(variable); - - if (it == value_map.end()) - it = value_map.insert( - typename value_mapt::value_type(variable, valuest())).first; - - unsigned count = it->second.size(); - it->second.insert(values.begin(), values.end()); - - return count != it->second.size(); - } - - void set_visible(const variablet& variable) - { - visible_variables.insert(variable); - } - - void compute_fixpoint() - { - variablest queue; - - // Only variables occurring as rhs of a rule need to be considered - for (typename rulest::const_iterator - it = rules.begin(); - it != rules.end(); - ++it) - { - queue.insert(it->first); - } - - // Iterate until saturation - while (!queue.empty()) - { - variablet current = *queue.begin(); - queue.erase(queue.begin()); - - bool changed = update_variable(current); - - if (changed) - plan_dependants(queue, current); - } - } - - void remove_invisible() - { - remove_invisible_from_values(); - remove_invisible_from_rules(rules); - remove_invisible_from_rules(inverse_rules); - } - - const value_mapt& get_value_map() const { return value_map; } - -private: - - bool insert_variable(rulest& rules, const variablet& key, - const variablet& value) - { - typename rulest::iterator it = rules.find(key); - - if (it == rules.end()) - it = rules.insert(typename rulest::value_type(key, variablest())).first; - - unsigned count = it->second.size(); - it->second.insert(value); - - return count != it->second.size(); - } - - void remove_invisible_from_values() - { - for (typename value_mapt::const_iterator it = value_map.begin(); - it != value_map.end();) - { - if (visible_variables.find(it->first) == visible_variables.end()) - it = value_map.erase(it); - else - ++it; - } - } - - void remove_invisible_from_rules(rulest& rules) - { - for (typename rulest::iterator it = rules.begin(); - it != rules.end();) - { - if (visible_variables.find(it->first) == visible_variables.end()) - { - // Invisible key -> remove the whole entry - it = rules.erase(it); - } - else - { - // Visible key -> remove invisible values - variablest& variables = it->second; - for (typename variablest::const_iterator it2 = variables.begin(); - it2 != variables.end();) - { - if (visible_variables.find(*it2) == visible_variables.end()) - it2 = variables.erase(it2); - else - ++it2; - } - - // All values removed -> remove the whole entry anyway - if (variables.size() == 0) - it = rules.erase(it); - else - ++it; - } - } - } - - bool update_variable(const variablet& variable) - { - bool changed = false; - - typename rulest::const_iterator it = rules.find(variable); - - if (it == inverse_rules.end()) - return false; - - // Merge the values and rules - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - if (*it2 != variable) { - if (visible_variables.find(*it2) == visible_variables.end()) { - changed |= merge_rules(variable, *it2); - } - changed |= merge_values(variable, *it2); - } - } - - return changed; - } - - bool merge_rules(const variablet& dest, const variablet& src) - { - typename rulest::const_iterator it = rules.find(src); - - if (it == rules.end()) - return false; - - bool changed = false; - - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - changed |= add_rule(dest, *it2); - } - return changed; - } - - bool merge_values(const variablet& dest, const variablet& src) - { - typename value_mapt::const_iterator it = value_map.find(src); - - if (it == value_map.end()) - return false; - - return add_values(dest, it->second); - } - - void plan_dependants(variablest& queue, const variablet& variable) - { - typename rulest::const_iterator it = inverse_rules.find(variable); - - if (it == inverse_rules.end()) - return; - - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - if (*it2 != variable) - queue.insert(*it2); - } - } - -protected: - value_mapt value_map; - rulest rules; - rulest inverse_rules; - variablest visible_variables; -}; - -#endif - diff --git a/src/deltacheck/old/modular_code_analysis.cpp b/src/deltacheck/old/modular_code_analysis.cpp deleted file mode 100644 index b3834cd81..000000000 --- a/src/deltacheck/old/modular_code_analysis.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/*******************************************************************\ - -Module: Ancestor of modular (i.e., per C file) fixpoint analysis -of goto-programs. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include "modular_code_analysis.h" - -modular_code_analysist::modular_code_analysist() : symbol_table(NULL) -{ -} - -modular_code_analysist::~modular_code_analysist() -{ -} - -void -modular_code_analysist::visit(const goto_programt::instructiont& instr) -{ - switch(instr.type) { - case ASSUME: - accept_assume(to_code_assume(instr.code)); - break; - case ASSIGN: - accept_assign(to_code_assign(instr.code)); - break; - case ASSERT: - accept_assert(to_code_assert(instr.code)); - break; - case FUNCTION_CALL: - accept_function_call(to_code_function_call(instr.code)); - break; - case RETURN: - accept_return(to_code_return(instr.code)); - break; - case SKIP: // fall through - case DECL: // fall through - case GOTO: // fall through - case END_FUNCTION: // fall through - break; - default: - throw "Unexpected instruction type in modular_code_analysist::visit()"; - break; - } -} - -void -modular_code_analysist::print(std::ostream& out) const -{ - // Values - for (value_mapt::const_iterator it = value_map.begin(); - it != value_map.end(); - ++it) - { - out << "Values for \"" << it->first << "\" <--" << std::endl; - - const valuest& values = it->second; - for (valuest::const_iterator it2 = values.begin(); - it2 != values.end(); - ++it2) - { - out << " \"" << *it2 << "\"" << std::endl; - } - } - // Rules - for (rulest::const_iterator it = rules.begin(); - it != rules.end(); - ++it) - { - out << "Rules for \"" << it->first << "\" <--" << std::endl; - - const variablest& variables = it->second; - for (variablest::const_iterator it2 = variables.begin(); - it2 != variables.end(); - ++it2) - { - out << " \"" << *it2 << "\"" << std::endl; - } - } -} - -void -modular_code_analysist::serialize(std::ostream& out) const -{ - // Values - out << value_map.size() << std::endl; - for (value_mapt::const_iterator it = value_map.begin(); - it != value_map.end(); - ++it) - { - const valuest& values = it->second; - - out << it->first << std::endl; - out << values.size() << std::endl; - - for (valuest::const_iterator it2 = values.begin(); - it2 != values.end(); - ++it2) - { - out << *it2 << std::endl; - } - } - // Rules - out << rules.size() << std::endl; - for (rulest::const_iterator it = rules.begin(); - it != rules.end(); - ++it) - { - const variablest& variables = it->second; - out << it->first << std::endl; - out << it->second.size() << std::endl; - - for (variablest::const_iterator it2 = variables.begin(); - it2 != variables.end(); - ++it2) - { - out << *it2 << std::endl; - } - } - // Visible variables - out << visible_variables.size() << std::endl; - for (variablest::const_iterator it = visible_variables.begin(); - it != visible_variables.end(); - ++it) - { - out << *it << std::endl; - } -} - -void -modular_code_analysist::deserialize(std::istream& in) -{ - // Values - int values_map_size; - in >> values_map_size; - if (!in.good()) return; - - for (int i = 0; i < values_map_size; ++i) - { - int values_size; - std::string var_str; - in >> var_str; - in >> values_size; - variablet var(var_str); - if (!in.good()) return; - - for (int j = 0; j < values_size; ++j) - { - std::string val_str; - in >> val_str; - valuet val(val_str); - - add_value(var, val); - } - } - // Rules - int rules_size; - in >> rules_size; - if (!in.good()) return; - - for (int i = 0; i < rules_size; ++i) - { - int vars_size; - std::string var_str; - in >> var_str; - in >> vars_size; - variablet var(var_str); - if (!in.good()) return; - - for (int j = 0; j < vars_size; ++j) - { - std::string var2_str; - in >> var2_str; - variablet var2(var2_str); - - add_rule(var, var2); - } - } - // Visible variables - int visible_size; - in >> visible_size; - if (!in.good()) return; - - for (int i = 0; i < visible_size; ++i) - { - std::string var_str; - in >> var_str; - variablet var(var_str); - - set_visible(var); - } -} - diff --git a/src/deltacheck/old/modular_code_analysis.h b/src/deltacheck/old/modular_code_analysis.h deleted file mode 100644 index 71a1ac028..000000000 --- a/src/deltacheck/old/modular_code_analysis.h +++ /dev/null @@ -1,67 +0,0 @@ -/*******************************************************************\ - -Module: The ancestor of modular (i.e., per C file) fixpoint analysis -of goto-programs. Based on modular_analysist, this class adds visitor-like -processing of goto-programs. - -The class supports serialization and deserialization to/from a text file. -Multiple deserialization results in merged rules, which is useful to merge -results of the analysis of different individual modules. After such merging, -fixpoint computation should be run to obtain the result of the overall analysis. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_CODE_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_CODE_ANALYSIS_H - -#include - -#include - -#include "modular_analysis.h" - -class modular_code_analysist : public modular_analysist { -public: - modular_code_analysist(); - virtual ~modular_code_analysist(); - - virtual const char* get_default_suffix() const = 0; - virtual const char* get_analysis_id() const = 0; - - // Instruction visitor functions - void visit(const goto_programt::instructiont& instr); - virtual void enter_function( - const irep_idt& function_id) - { - current_function = function_id; - } - virtual void exit_function() - { - current_function.clear(); - } - - void set_symbol_table(const symbol_tablet& _symbol_table) - { - symbol_table = &_symbol_table; - } - - virtual void print(std::ostream& out) const; - virtual void serialize(std::ostream& out) const; - virtual void deserialize(std::istream& in); - -protected: - virtual void accept_assume(const code_assumet& instruction) {}; - virtual void accept_assign(const code_assignt& instruction) {}; - virtual void accept_assert(const code_assertt& instruction) {}; - virtual void accept_function_call(const code_function_callt& instruction) {}; - virtual void accept_return(const code_returnt& instruction) {}; - - irep_idt current_function; - const symbol_tablet* symbol_table; -}; - -#endif - diff --git a/src/deltacheck/old/modular_fptr_analysis.cpp b/src/deltacheck/old/modular_fptr_analysis.cpp deleted file mode 100644 index bab3d6338..000000000 --- a/src/deltacheck/old/modular_fptr_analysis.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of function pointers. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include -#include - -#include - -#include "modular_fptr_analysis.h" -#include "modular_code_analysis.h" - -modular_fptr_analysist::modular_fptr_analysist() : any_variable("__ANY__") -{ - set_visible(any_variable); -} - -modular_fptr_analysist::~modular_fptr_analysist() -{ -} - -void -modular_fptr_analysist::accept_function_call( - const code_function_callt& instruction) -{ - irep_idt function_var = current_function; - set_visible(function_var); - const exprt& target = instruction.function(); - - if (target.id() == ID_symbol) { - // Direct function call, we just mark the call graph edge - const symbol_exprt& symbol = to_symbol_expr(target); - - std::cout << " - direct call to \"" << - symbol.get_identifier() << - "\"" << std::endl; - - irep_idt function_value = symbol.get_identifier(); - - add_value(function_var, function_value); - } - else if (target.id() == ID_dereference) - { - // Indirect call, we find out what is actually dereferenced and add - // the dependency on the corresponding "type [x field]" - std::cout << " - indirect call: " << target.to_string() << - std::endl; - - variablet var; - const dereference_exprt& dereference = to_dereference_expr(target); - - if (try_compute_variable(dereference.pointer(), var)) - { - add_rule(function_var, var); - } - - // FIXME: Handle also parameters here -> hard to do due to yet unknown - // aliasing. - } - else - { - // Unknown function call operand - assert(false); - } -} - -void -modular_fptr_analysist::accept_assign(const code_assignt& instruction) -{ - std::cout << " - rhs: " << instruction.rhs().to_string() << std::endl; - - variablet lhs_var; - if (!try_compute_variable(instruction.lhs(), lhs_var)) - return; - - process_assignment(lhs_var, instruction.rhs()); -} - -void -modular_fptr_analysist::accept_return(const code_returnt& instruction) -{ - std::cout << " - return value: " << instruction.return_value().to_string() << - std::endl; - variablet lhs_var = id2string(current_function) + ".return_value"; - - process_assignment(lhs_var, instruction.return_value()); -} - -void -modular_fptr_analysist::process_assignment(const variablet& lhs_var, - const exprt& rhs) -{ - valuet rhs_val; - variablet rhs_var; - - if (try_compute_constant_value(rhs, rhs_val)) - { - add_value(lhs_var, rhs_val); - } - else - { - if (try_compute_variable(rhs, rhs_var)) - { - add_rule(lhs_var, rhs_var); - } - } -} - -bool -modular_fptr_analysist::try_compute_constant_value(const exprt& expr, - valuet& value) -{ - if (expr.id() == ID_typecast) - { - return try_compute_constant_value(expr.op0(), value); - } - else if (expr.id() == ID_address_of) - { - const address_of_exprt& address_of = to_address_of_expr(expr); - - if (expr.type().id() == ID_pointer && - expr.type().subtype().id() == ID_code && - address_of.object().id() == ID_symbol) - { - // A constant function pointer (i.e., an address of a function) - value = to_symbol_expr(address_of.object()).get_identifier(); - return true; - } - } - return false; -} - -bool -modular_fptr_analysist::try_compute_symbol_variable(const exprt& expr, - variablet& variable) -{ - assert(symbol_table); - - if(expr.id() == ID_symbol) - { - // Directly a symbol - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_lvalue) - { - variable = id; - - if (is_symbol_visible(symbol)) - set_visible(variable); - - return true; - } - } - else if (expr.id() == ID_member) - { - // A field of a structure - const member_exprt& member = to_member_expr(expr); - - if(member.struct_op().id() == ID_symbol) - { - irep_idt id = to_symbol_expr(member.struct_op()).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_lvalue) - { - variable = id2string(id) + "." + id2string(member.get_component_name()); - - if (is_symbol_visible(symbol)) - set_visible(variable); - - return true; - } - } - } - else if (expr.id() == ID_typecast) - { - return try_compute_symbol_variable(expr.op0(), variable); - } - return false; -} - -bool -modular_fptr_analysist::try_compute_field_access_variable(const exprt& expr, - variablet& variable) -{ - if (expr.id() == ID_member) - { - const member_exprt& member = to_member_expr(expr); - const typet& struct_type = member.struct_op().type(); - assert(struct_type.id() == ID_symbol); - - variable = irep_idt( - id2string(to_symbol_type(struct_type).get_identifier()) + "." + - id2string(member.get_component_name())); - - set_visible(variable); - return true; - } - return false; -} - -bool -modular_fptr_analysist::try_compute_type_variable(const exprt& expr, - variablet& variable) -{ - const typet& type = expr.type(); - assert(type.id() == ID_pointer); - - if (type.id() == ID_pointer && type.subtype().id() == ID_code) - { - variable = type2name(to_pointer_type(type)); - set_visible(variable); - return true; - } - return false; -} - -bool -modular_fptr_analysist::try_compute_variable(const exprt& expr, - variablet& variable) -{ - assert(symbol_table); - - // Ignore any non-pointer expression - if (expr.type().id() != ID_pointer) - { - std::cout << "Ignoring type: " << type2name(expr.type()) << std::endl; - return false; - } - - variablet prev_var = any_variable; - variable = prev_var; - - if (try_compute_type_variable(expr, variable)) - { - add_rule(prev_var, variable); - prev_var = variable; - } - if (try_compute_field_access_variable(expr, variable)) { - add_rule(prev_var, variable); - prev_var = variable; - } - if (try_compute_symbol_variable(expr, variable)) { - add_rule(prev_var, variable); - prev_var = variable; - } - return true; -} - -bool -modular_fptr_analysist::is_symbol_visible(const symbolt& symbol) -{ - return symbol.is_static_lifetime && !symbol.is_file_local; -} - -bool -modular_fptr_analysist::get_callees(const irep_idt& id, valuest& functions) -{ - functions.clear(); - - value_mapt::const_iterator it = - value_map.find(id); - - if (it == value_map.end()) - return false; - - functions.insert(it->second.begin(), it->second.end()); - - return functions.size() != 0; -} diff --git a/src/deltacheck/old/modular_fptr_analysis.h b/src/deltacheck/old/modular_fptr_analysis.h deleted file mode 100644 index 47c5b7da2..000000000 --- a/src/deltacheck/old/modular_fptr_analysis.h +++ /dev/null @@ -1,63 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of function pointers. - -The aliasing data is assigned to the following entities with decreasing -priority: -- symbol (variable, variable.field, function) -- fields of structures (i.e., shared among structures of the same type) -- types (i.e., specific type of function pointer) -- wildcard (matches anything unknown) - -FIXME: The following is not addressed at all: -- function parameters, return value -- non-scalar assignment: structures, arrays - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_FPTR_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_FPTR_ANALYSIS_H - -#include "modular_code_analysis.h" - -class modular_fptr_analysist : public modular_code_analysist { -public: - modular_fptr_analysist(); - virtual ~modular_fptr_analysist(); - - virtual const char* get_default_suffix() const - { - return ".dc_fp"; - } - virtual const char* get_analysis_id() const - { - return "Function pointer analysis"; - } - - // Queries for the call graph - bool get_callees(const irep_idt& function, valuest& functions); - -protected: - virtual void accept_assign(const code_assignt& instruction); - virtual void accept_function_call(const code_function_callt& instruction); - virtual void accept_return(const code_returnt& instruction); - -private: - irep_idt any_variable; - - void process_assignment(const variablet& lhs_var, const exprt& rhs); - - bool try_compute_symbol_variable(const exprt& expr, variablet& variable); - bool try_compute_field_access_variable(const exprt& expr, variablet& variable); - bool try_compute_type_variable(const exprt& expr, variablet& variable); - - bool try_compute_constant_value(const exprt& expr, valuet& value); - bool try_compute_variable(const exprt& expr, variablet& variable); - - bool is_symbol_visible(const symbolt& symbol); -}; - -#endif - diff --git a/src/deltacheck/old/modular_globals_analysis.cpp b/src/deltacheck/old/modular_globals_analysis.cpp deleted file mode 100644 index a1f0f492f..000000000 --- a/src/deltacheck/old/modular_globals_analysis.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of globals. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include -#include -#include -#include - -#include - -#include "modular_globals_analysis.h" - -modular_globals_analysist::modular_globals_analysist() -{ -} - -modular_globals_analysist::~modular_globals_analysist() -{ -} - -bool -modular_globals_analysist::get_aliased_globals(const irep_idt& id, - valuest& globals) -{ - assert(symbol_table); - globals.clear(); - - const typet& type = symbol_table->lookup(id).type; - irep_idt type_id = type2name(type); - - value_mapt::const_iterator it = - value_map.find(type_id); - - if (it == value_map.end()) - return false; - - globals.insert(it->second.begin(), it->second.end()); - - return globals.size() != 0; -} - -void -modular_globals_analysist::accept_assign(const code_assignt& instruction) -{ - process_global_dereferences_rec(instruction.rhs()); -} - -void -modular_globals_analysist::accept_function_call( - const code_function_callt& instruction) -{ - const code_function_callt::argumentst& args = instruction.arguments(); - - for(code_function_callt::argumentst::const_iterator it = args.begin(); - it != args.end(); - ++it) - { - process_global_dereferences_rec(*it); - } -} - -void -modular_globals_analysist::accept_return(const code_returnt& instruction) -{ - process_global_dereferences_rec(instruction.return_value()); -} - -bool -modular_globals_analysist::try_compute_value(const exprt& expr, valuet& value) -{ - assert(symbol_table); - if (expr.id() == ID_symbol) - { - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_static_lifetime && symbol.is_lvalue) - { - value = id; - return true; - } - } - return false; -} - -bool -modular_globals_analysist::try_compute_variable( - const exprt& expr, variablet& variable) -{ - assert(symbol_table); - if (expr.id() == ID_symbol) - { - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_static_lifetime && symbol.is_lvalue) - { - variable = type2name(symbol.type); - set_visible(variable); - return true; - } - } - return false; -} - -void -modular_globals_analysist::process_global_dereferences_rec(const exprt& expr) -{ - if (expr.id() == ID_address_of) - { - // Dereference, check if it is a global one - const address_of_exprt& address_of = to_address_of_expr(expr); - valuet value; - variablet variable; - - if (try_compute_value(address_of.object(), value) && - try_compute_variable(address_of.object(), variable)) - { - add_value(variable, value); - return; - } - } - - // Process everything else recursively - forall_operands(it, expr) - { - process_global_dereferences_rec(*it); - } -} diff --git a/src/deltacheck/old/modular_globals_analysis.h b/src/deltacheck/old/modular_globals_analysis.h deleted file mode 100644 index b1f4b3df7..000000000 --- a/src/deltacheck/old/modular_globals_analysis.h +++ /dev/null @@ -1,47 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of globals. - -Simple analysis of aliasing of global variables. Any dereferenced global is -marked as possibly aliasing with pointers to the same type. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_GLOBALS_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_GLOBALS_ANALYSIS_H - -#include "modular_code_analysis.h" - -class modular_globals_analysist : public modular_code_analysist { -public: - modular_globals_analysist(); - virtual ~modular_globals_analysist(); - - virtual const char* get_default_suffix() const - { - return ".dc_gl"; - } - virtual const char* get_analysis_id() const - { - return "Globals analysis"; - } - - // Queries for the aliases of globals, symbol_table has to be set when calling - bool get_aliased_globals(const irep_idt& id, valuest& globals); - -protected: - virtual void accept_assign(const code_assignt& instruction); - virtual void accept_function_call(const code_function_callt& instruction); - virtual void accept_return(const code_returnt& instruction); - -private: - bool try_compute_value(const exprt& expr, valuet& value); - bool try_compute_variable(const exprt& expr, variablet& variable); - - void process_global_dereferences_rec(const exprt& expr); -}; - -#endif - diff --git a/src/deltacheck/old/predicates.cpp b/src/deltacheck/old/predicates.cpp deleted file mode 100644 index f07e0f0af..000000000 --- a/src/deltacheck/old/predicates.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicates.h" - -/*******************************************************************\ - -Function: predicatest::make_list - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string predicatest::make_list() const -{ - std::string result; - - for(predicate_vectort::const_iterator - p_it=predicate_vector.begin(); - p_it!=predicate_vector.end(); - p_it++) - { - if(p_it!=predicate_vector.begin()) result+=", "; - result+=from_expr(ns, "", p_it->expr); - } - - return result; -} - -/*******************************************************************\ - -Function: predicatest::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatest::add(Cudd &mgr, const exprt &src) -{ - if(predicate_map.find(src)!=predicate_map.end()) - return; - - unsigned index=size(); - - predicate_vector.push_back(predicatet()); - predicate_vector.back().expr=src; - - // this will cause the ordering of the variable and - // it's next-state version to be interleaved - predicate_vector.back().var=mgr.bddVar(index*2); - predicate_vector.back().next_var=mgr.bddVar(index*2+1); - predicate_vector.back().string=from_expr(ns, "", src); - - predicate_map[src]=index; -} diff --git a/src/deltacheck/old/predicates.h b/src/deltacheck/old/predicates.h deleted file mode 100644 index 11b8c43ed..000000000 --- a/src/deltacheck/old/predicates.h +++ /dev/null @@ -1,58 +0,0 @@ -/*******************************************************************\ - -Module: Predicates - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PREDICATES_H -#define CPROVER_DELTACHECK_PREDICATES_H - -#include -#include - -#include - -struct predicatet -{ - irep_idt id; - exprt expr; - - // the BDD variable for the predicate and the next-state version - BDD var, next_var; - - std::string string; -}; - -class predicatest -{ -public: - explicit predicatest(const namespacet &_ns):ns(_ns) - { - } - - typedef std::vector predicate_vectort; - typedef std::map predicate_mapt; - predicate_vectort predicate_vector; - predicate_mapt predicate_map; - - inline unsigned size() const - { - return (unsigned)predicate_vector.size(); - } - - void add(Cudd &mgr, const exprt &src); - - std::string make_list() const; - - const predicatet &operator[] (std::size_t i) const - { - return predicate_vector[i]; - } - -protected: - const namespacet &ns; -}; - -#endif diff --git a/src/deltacheck/old/reporting.cpp b/src/deltacheck/old/reporting.cpp deleted file mode 100644 index cc593a709..000000000 --- a/src/deltacheck/old/reporting.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/*******************************************************************\ - -Module: Collation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "reporting.h" -#include "get_goto_program.h" -#include "call_graph.h" - -/*******************************************************************\ - -Function: get_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_functions(const xmlt &xml, std::set &dest) -{ - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - dest.insert(f_it->get_attribute("id")); - } - } -} - -/*******************************************************************\ - -Function: collate - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void collate( - const std::vector &files, - const optionst &options, - message_handlert &message_handler) -{ - -} - -/*******************************************************************\ - -Function: call_graph_dot - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void call_graph_dot( - const std::list &files, - const std::string &dest_file, - message_handlert &message_handler) -{ - messaget message(message_handler); - call_grapht call_graph; - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - { - xmlt xml; - if(parse_xml(*it+".summary", message_handler, xml)) - message.warning("failed to read summary of "+*it); - - summary_to_call_graph(xml, call_graph); - } - - { - message.status("Writing call graph"); - std::ofstream out(dest_file.c_str()); - if(!out) - throw "failed to write call graph DOT"; - call_graph.output_dot(out); - return; - } -} - -/*******************************************************************\ - -Function: reporting_cmdline - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_cmdline( - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - messaget message(message_handler); - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - show_claims(ns, ui_message_handlert::PLAIN, goto_functions); -} - -/*******************************************************************\ - -Function: reporting_cmdline - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_cmdline( - const std::list &files, - const optionst &options, - message_handlert &message_handler) -{ - if(options.get_option("call-graph-dot")!="") - { - call_graph_dot(files, options.get_option("call-graph-dot"), - message_handler); - return; - } - - // report status of claims on a per-file basis - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - reporting_cmdline(*it, options, message_handler); -} - -/*******************************************************************\ - -Function: reporting_html - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_html( - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - #if 0 - messaget message(message_handler); - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - show_claims(ns, ui_message_handlert::PLAIN, goto_functions); - #endif -} - -/*******************************************************************\ - -Function: reporting_html - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_html( - const std::list &files, - const optionst &options, - message_handlert &message_handler) -{ - if(options.get_option("call-graph-dot")!="") - call_graph_dot(files, options.get_option("call-graph-dot"), - message_handler); - - // report status of claims on a per-file basis - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - reporting_html(*it, options, message_handler); -} - diff --git a/src/deltacheck/old/reporting.h b/src/deltacheck/old/reporting.h deleted file mode 100644 index f9a67f355..000000000 --- a/src/deltacheck/old/reporting.h +++ /dev/null @@ -1,28 +0,0 @@ -/*******************************************************************\ - -Module: Collation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_REPORTING_H -#define CPROVER_DELTACHECK_REPORTING_H - -#include -#include - -#include -#include - -void reporting_html( - const std::list &files, - const optionst &, - message_handlert &); - -void reporting_cmdline( - const std::list &files, - const optionst &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/statement_transformer.cpp b/src/deltacheck/old/statement_transformer.cpp deleted file mode 100644 index c15c2ef02..000000000 --- a/src/deltacheck/old/statement_transformer.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include - -#include "canonicalize.h" -#include "statement_transformer.h" - -/*******************************************************************\ - -Function: statement_transformert::assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#include - -BDD statement_transformert::assign( - const BDD &from, - const code_assignt &assignment) -{ - BDD result=from; - - std::cout << "Statement: " << from_expr(ns, "", assignment.lhs()) - << " = " << from_expr(ns, "", assignment.rhs()) << std::endl; - - // Assign all predicates based on WP. - // This is cheap cartesian abstraction. - for(unsigned p=0; psecond].var; - if(negation) result=!result; - return result; - } - - // give up, produce non-determinism - return aux_variable(); -} - -/*******************************************************************\ - -Function: statement_transformert::remove_aux - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -BDD statement_transformert::remove_aux(const BDD &src) -{ - if(aux_variable_start==aux_variable_end) return src; - - // build cube made of auxiliary variables - BDD cube=mgr.bddOne(); - - for(unsigned i=aux_variable_start; i - -#include - -#include -#include -#include - -#include "predicates.h" - -class statement_transformert -{ -public: - statement_transformert( - const predicatest &_predicates, - Cudd &_mgr, - const namespacet &_ns): - predicates(_predicates), - mgr(_mgr), - ns(_ns), - aux_variable_start(0), - aux_variable_end(0) - { - } - - BDD assign( - const BDD &from, const code_assignt &assignment); - - inline BDD guard(const BDD &from, const exprt &g) - { - return remove_aux(from & abstract(g)); - } - - inline BDD guard_not(const BDD &from, const exprt &g) - { - return remove_aux(from & !abstract(g)); - } - -protected: - const predicatest &predicates; - Cudd &mgr; - const namespacet &ns; - - BDD abstract(const exprt &g); - BDD remove_aux(const BDD &src); - - // record any auxiliary variables - unsigned aux_variable_start, aux_variable_end; - - BDD aux_variable(); -}; diff --git a/src/deltacheck/old/summarization.cpp b/src/deltacheck/old/summarization.cpp deleted file mode 100644 index e0e904cca..000000000 --- a/src/deltacheck/old/summarization.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "get_goto_program.h" -#include "xml_conversion.h" -#include "summarization.h" -#include "dependencies.h" -#include "function_transformer.h" - -//#include "cgraph_builder.h" -//#include "modular_fptr_analysis.h" -//#include "modular_globals_analysis.h" - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls_rec( - const namespacet &ns, - const goto_functionst &goto_functions, - const exprt &function, - std::set &called_functions) -{ - if(function.id()==ID_symbol) - { - irep_idt id=to_symbol_expr(function).get_identifier(); - const symbolt &symbol=ns.lookup(id); - if(!symbol.is_file_local) - called_functions.insert(id); - } - else if(function.id()==ID_dereference) - { - } - else if(function.id()==ID_if) - { - } -} - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - std::ostream &out) -{ - std::set called_functions; - - forall_goto_program_instructions(it, goto_function.body) - { - if(it->is_function_call()) - { - const exprt &function=to_code_function_call(it->code).function(); - - summarize_function_calls_rec( - ns, goto_functions, function, called_functions); - } - } - - for(std::set::const_iterator - it=called_functions.begin(); - it!=called_functions.end(); - it++) - { - out << " "; - out << "" << std::endl; - } -} - -/*******************************************************************\ - -Function: summarize_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function( - const namespacet &ns, - const goto_functionst &goto_functions, - const symbolt &symbol, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - out << "" << std::endl; - - if(symbol.location.is_not_nil() && - symbol.location.get_file()!="") - out << " " << xml(symbol.location); - - summarize_function_calls(ns, goto_functions, goto_function, out); - - function_transformer(ns, goto_functions, goto_function, message_handler, out); - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: dump_exported_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_exported_functions( - const namespacet &ns, - const goto_functionst &goto_functions, - message_handlert &message_handler, - std::ostream &out) -{ - out << "" << std::endl; - - // do this for each function - forall_goto_functions(f_it, goto_functions) - { - if(!f_it->second.body_available) - continue; - - if(has_prefix(id2string(f_it->first), CPROVER_PREFIX)) - continue; - - const symbolt &symbol=ns.lookup(f_it->first); - - if(symbol.is_file_local) - continue; - - messaget message(message_handler); - message.status("Summarizing "+id2string(f_it->first)); - - summarize_function( - ns, goto_functions, symbol, f_it->second, message_handler, out); - } - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: dump_state_variables - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_state_variables( - const symbol_tablet &symbol_table, - std::ostream &out) -{ - out << "" << std::endl; - - forall_symbols(s_it, symbol_table.symbols) - { - const symbolt &symbol=s_it->second; - - if(has_prefix(id2string(symbol.name), CPROVER_PREFIX)) - continue; - - if(symbol.type.id()==ID_code || - symbol.is_type) - continue; - - if(symbol.is_file_local) - continue; - - out << "" << std::endl; - - if(symbol.location.is_not_nil() && symbol.location.get_file()!="") - out << xml(symbol.location); - - out << "" << std::endl; - } - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const symbol_tablet &symbol_table, - const goto_functionst &goto_functions, - const optionst &options, - message_handlert &message_handler, - std::ostream &out) -{ - // first collect non-static function symbols that - // have a body - - namespacet ns(symbol_table); - - dump_exported_functions(ns, goto_functions, message_handler, out); - - dump_state_variables(symbol_table, out); - - #if 0 - cgraph_buildert cg_builder; - modular_fptr_analysist fptr_analysis; - modular_globals_analysist globals_analysis; - - cg_builder.add_analysis(&fptr_analysis); - cg_builder.add_analysis(&globals_analysis); - - cg_builder.analyze_module(symbol_table, goto_functions); - cg_builder.serialize(file_name); - #endif -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - // first check dependencies - if(!options.get_bool_option("force") && - dependencies(function_file_map, file_name, message_handler)==FRESH) - return; - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - //goto_functions.output(ns, std::cout); - - std::string summary_file_name=file_name+".summary"; - std::ofstream summary_file(summary_file_name.c_str(), - std::ios::binary|std::ios::trunc|std::ios::out); - - if(!summary_file) - throw std::string("failed to write summary file"); - - summary_file << "" << std::endl; - - ::summarization( - function_file_map, - symbol_table, - goto_functions, - options, - message_handler, - summary_file); - - summary_file << "" << std::endl; - - messaget message(message_handler); - message.status("Summary written as "+summary_file_name); -} - diff --git a/src/deltacheck/old/summarization.h b/src/deltacheck/old/summarization.h deleted file mode 100644 index 6737d5ad1..000000000 --- a/src/deltacheck/old/summarization.h +++ /dev/null @@ -1,24 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SUMMARIZATION_H -#define CPROVER_DELTACHECK_SUMMARIZATION_H - -#include -#include -#include - -#include "function_file_map.h" - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/xml_conversion.cpp b/src/deltacheck/old/xml_conversion.cpp deleted file mode 100644 index 50e976ff6..000000000 --- a/src/deltacheck/old/xml_conversion.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "xml_conversion.h" - -/*******************************************************************\ - -Function: xml - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#if 0 -xmlt xml(const locationt &location) -{ - xmlt xml_location; - xml_location.name="location"; - - if(location.get_file()!="") - xml_location.set_attribute("file", id2string(location.get_file())); - - if(location.get_line()!="") - xml_location.set_attribute("line", id2string(location.get_line())); - - return xml_location; -} -#endif diff --git a/src/deltacheck/old/xml_conversion.h b/src/deltacheck/old/xml_conversion.h deleted file mode 100644 index f23800f03..000000000 --- a/src/deltacheck/old/xml_conversion.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include - -//xmlt xml(const locationt &location); diff --git a/src/deltacheck/properties.cpp b/src/deltacheck/properties.cpp deleted file mode 100644 index e4aa9fddd..000000000 --- a/src/deltacheck/properties.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/*******************************************************************\ - -Module: Property Management - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../html/html_escape.h" -#include "properties.h" - -/*******************************************************************\ - -Function: report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_properties( - const propertiest &properties, - std::ostream &out) -{ - // produce clickable table of properties - - out << "\n"; - - out << "" - << "\n" - << "\n" - << "\n" - << "\n\n"; - - unsigned count; - - count=0; - - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++, count++) - { - const source_locationt &source_location=p_it->loc->source_location; - - out << "\n"; - - out << " \n"; // ✗ - else if(p_it->status.is_true()) - out << "" - "\n"; // ✓ - else - out << "?" - "\n"; - - out << " \n"; - - out << " \n"; - - out << " \n"; - - out << " \n"; - - out << "\n\n"; - } - - out << "
StatusLocationProperty
"; - - if(p_it->status.is_false()) - out << "" - " "; - out << html_escape(source_location.get_file()); - out << ""; - out << html_escape(source_location.get_line()); - out << ""; - out << html_escape(source_location.get_property_class()); - out << ""; - out << html_escape(source_location.get_comment()); - out << "
\n\n"; -} - -/*******************************************************************\ - -Function: report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_properties( - const propertiest &properties, - messaget &message) -{ - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++) - { - message.status() - << "[" << p_it->loc->source_location.get_property_id() << "] " - << p_it->loc->source_location.get_comment() << ": "; - if(p_it->status.is_true()) - message.status() << "OK"; - else if(p_it->status.is_false()) - message.status() << "FAILED"; - else - message.status() << "UNKNOWN"; - message.status() << messaget::eom; - } -} - -/*******************************************************************\ - -Function: get_tracked_expr - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_tracked_expr( - const exprt &src, - std::list &dest) -{ - forall_operands(it, src) - get_tracked_expr(*it, dest); - - if(src.id()==ID_symbol) - dest.push_back(src); -} - -/*******************************************************************\ - -Function: get_tracked_expr_lhs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_tracked_expr_lhs( - const exprt &src, - std::list &dest_lhs, - std::list &dest_rhs) -{ - if(src.id()==ID_symbol) - dest_lhs.push_back(src); - else if(src.id()==ID_member) - { - get_tracked_expr_lhs(to_member_expr(src).struct_op(), dest_lhs, dest_rhs); - } - else if(src.id()==ID_index) - { - get_tracked_expr_lhs(to_index_expr(src).array(), dest_lhs, dest_rhs); - get_tracked_expr(to_index_expr(src).index(), dest_rhs); - } - else - get_tracked_expr(src, dest_rhs); -} - -/*******************************************************************\ - -Function: report_countermodel - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodel( - const propertyt &property, - const namespacet &ns, - const local_SSAt &SSA, - unsigned count, - std::ostream &out) -{ - for(propertyt::value_mapt::const_iterator - v_it=property.value_map.begin(); - v_it!=property.value_map.end(); - v_it++) - { - out << "// " << from_expr(ns, "", v_it->first) - << " -> " - << from_expr(ns, "", v_it->second) - << "\n"; - } - - std::map var_count; - - forall_goto_program_instructions(i_it, SSA.goto_function.body) - { - std::list tracked_expr_rhs, tracked_expr_lhs; - - if(i_it->is_assert() || i_it->is_assume() || i_it->is_goto()) - { - get_tracked_expr(i_it->guard, tracked_expr_rhs); - } - else if(i_it->is_assign()) - { - const code_assignt &code_assign=to_code_assign(i_it->code); - get_tracked_expr_lhs(code_assign.lhs(), tracked_expr_lhs, tracked_expr_rhs); - get_tracked_expr(code_assign.rhs(), tracked_expr_rhs); - } - - // lhs - for(std::list::const_iterator - e_it=tracked_expr_lhs.begin(); - e_it!=tracked_expr_lhs.end(); - e_it++) - { - exprt renamed_lhs=SSA.read_lhs(*e_it, i_it); - - const propertyt::value_mapt::const_iterator v_it_lhs= - property.value_map.find(renamed_lhs); - - if(v_it_lhs!=property.value_map.end()) - { - std::string var=from_expr(ns, "", *e_it); - std::string var_loc=var+"@"+id2string(i_it->source_location.get_line()); - out << "ce" << count << "['" << var_loc - << "." << ++var_count[var_loc] - << "']='" - << from_expr(ns, "", v_it_lhs->second) - << "';\n"; - } - } - - // rhs - for(std::list::const_iterator - e_it=tracked_expr_rhs.begin(); - e_it!=tracked_expr_rhs.end(); - e_it++) - { - exprt renamed_rhs=SSA.read_rhs(*e_it, i_it); - - const propertyt::value_mapt::const_iterator v_it_rhs= - property.value_map.find(renamed_rhs); - - if(v_it_rhs==property.value_map.end()) - continue; - - std::string var=from_expr(ns, "", *e_it); - std::string var_loc=var+"@"+id2string(i_it->source_location.get_line()); - out << "ce" << count << "['" << var_loc - << "." << ++var_count[var_loc] - << "']='" - << from_expr(ns, "", v_it_rhs->second) - << "';\n"; - } - } -} - -/*******************************************************************\ - -Function: report_countermodels - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodels( - const local_SSAt &SSA_old, - const local_SSAt &SSA_new, - const propertiest &properties, - std::ostream &out) -{ - out << "\n\n"; -} - -/*******************************************************************\ - -Function: report_countermodels - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodels( - const local_SSAt &SSA, - const propertiest &properties, - std::ostream &out) -{ - out << "\n\n"; -} - diff --git a/src/deltacheck/properties.h b/src/deltacheck/properties.h deleted file mode 100644 index adda09751..000000000 --- a/src/deltacheck/properties.h +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************************************\ - -Module: Property Management - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_PROPERTIES_H -#define DELTACHECK_PROPERTIES_H - -#include -#include - -#include "../ssa/local_ssa.h" - -class propertyt -{ -public: - goto_programt::const_targett loc; - tvt status; - - // given in SSA form - exprt guard, condition; - - // in case of failed properties: countermodel - typedef std::map value_mapt; - value_mapt value_map; -}; - -typedef std::list propertiest; - -void report_properties( - const propertiest &, - std::ostream &); - -void report_properties( - const propertiest &, - messaget &); - -void report_countermodels( - const local_SSAt &, - const propertiest &, - std::ostream &); - -void report_countermodels( - const local_SSAt &SSA_old, - const local_SSAt &SSA_new, - const propertiest &, - std::ostream &); - -#endif diff --git a/src/deltacheck/rename.cpp b/src/deltacheck/rename.cpp deleted file mode 100644 index b82821a13..000000000 --- a/src/deltacheck/rename.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/*******************************************************************\ - -Module: Symbol Renaming - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "rename.h" - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(symbol_tablet &symbol_table) -{ - for(symbol_tablet::symbolst::iterator - s_it=symbol_table.symbols.begin(); - s_it!=symbol_table.symbols.end(); - s_it++) - { - operator()(s_it->second); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(symbolt &symbol) -{ - operator()(symbol.value); - operator()(symbol.type); -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(exprt &expr) -{ - Forall_operands(it, expr) - operator()(*it); - - operator()(expr.type()); - - if(expr.id()==ID_symbol) - { - const irep_idt old_name=expr.get(ID_identifier); - const irep_idt new_name=id2string(old_name)+suffix; - expr.set(ID_identifier, new_name); - } - - if(expr.find(ID_C_c_sizeof_type).is_not_nil()) - { - irept &c_sizeof_type=expr.add(ID_C_c_sizeof_type); - - if(c_sizeof_type.is_not_nil()) - operator()(static_cast(c_sizeof_type)); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(typet &type) -{ - if(type.has_subtype()) - operator()(type.subtype()); - - Forall_subtypes(it, type) - operator()(*it); - - if(type.id()==ID_struct || - type.id()==ID_union) - { - struct_union_typet &struct_union_type=to_struct_union_type(type); - struct_union_typet::componentst &components=struct_union_type.components(); - - for(struct_union_typet::componentst::iterator - it=components.begin(); - it!=components.end(); - it++) - operator()(*it); - } - else if(type.id()==ID_code) - { - code_typet &code_type=to_code_type(type); - operator()(code_type.return_type()); - - code_typet::parameterst ¶meters=code_type.parameters(); - - for(code_typet::parameterst::iterator - it=parameters.begin(); - it!=parameters.end(); - it++) - { - operator()(*it); - } - } - else if(type.id()==ID_symbol) - { - const irep_idt old_name=type.get(ID_identifier); - const irep_idt new_name=id2string(old_name)+"$old"; - type.set(ID_identifier, new_name); - } - else if(type.id()==ID_array) - { - // do the size -- the subtype is already done - operator()(to_array_type(type).size()); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(goto_functionst &goto_functions) -{ - for(goto_functionst::function_mapt::iterator - f_it=goto_functions.function_map.begin(); - f_it!=goto_functions.function_map.end(); - f_it++) - operator()(f_it->second); -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(goto_functionst::goto_functiont &goto_function) -{ - operator()(goto_function.type); - - Forall_goto_program_instructions(i_it, goto_function.body) - { - operator()(i_it->code); - operator()(i_it->guard); - } -} diff --git a/src/deltacheck/rename.h b/src/deltacheck/rename.h deleted file mode 100644 index 9745210cc..000000000 --- a/src/deltacheck/rename.h +++ /dev/null @@ -1,34 +0,0 @@ -/*******************************************************************\ - -Module: Symbol Renaming - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_RENAME_H -#define CPROVER_DELTACHECK_RENAME_H - -#include -#include - -class renamet -{ -public: - renamet():suffix("$old") - { - } - - void operator()(symbol_tablet &); - void operator()(symbolt &); - void operator()(exprt &); - void operator()(typet &); - void operator()(goto_functionst &); - void operator()(goto_functionst::goto_functiont &); - - std::string suffix; - -protected: -}; - -#endif diff --git a/src/deltacheck/report_header.html b/src/deltacheck/report_header.html deleted file mode 100644 index cc5e08aea..000000000 --- a/src/deltacheck/report_header.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - diff --git a/src/deltacheck/report_source_code.cpp b/src/deltacheck/report_source_code.cpp deleted file mode 100644 index 738db1301..000000000 --- a/src/deltacheck/report_source_code.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#include - -#include "../html/html_escape.h" -#include "../html/syntax_highlighting.h" -#include "report_source_code.h" -#include "get_source.h" -#include "source_diff.h" - -/*******************************************************************\ - -Function: get_errors - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_errors( - const propertiest &properties, - const linet &line) -{ - std::string errors; - - for(propertiest::const_iterator - p_it=properties.begin(); p_it!=properties.end(); p_it++) - { - if(line.file==p_it->loc->source_location.get_file() && - i2string(line.line_no)==as_string(p_it->loc->source_location.get_line())) - { - if(p_it->status.is_false()) - { - if(!errors.empty()) errors+="
"; - - irep_idt property=p_it->loc->source_location.get_property_class(); - irep_idt comment=p_it->loc->source_location.get_comment(); - - if(comment=="") - errors+=as_string(property); - else - errors+=as_string(comment); - } - } - } - - return errors; -} - -/*******************************************************************\ - -Function: report_source_code - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_source_code( - const std::string &path_prefix, - const source_locationt &source_location, - const goto_programt &goto_program, - const propertiest &properties, - std::ostream &out, - message_handlert &message_handler) -{ - std::list lines; - get_source(path_prefix, source_location, goto_program, lines, message_handler); - - out << "

\n"; - out << "\n"; - - // error marking - - out << "\n"; - - // line numbers go into a column - - out << "\n"; - - // now do source text in another column - - out << "\n"; - - out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-  {
-    std::string errors=get_errors(properties, *l_it);
-    if(!errors.empty())
-    {
-      out << ""
-          << "✗"
-          << "";
-    }
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-    out << l_it->line_no << "\n";
-    
-  out << "
\n";
-  
-  syntax_highlightingt syntax_highlighting(out);
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-  {
-    syntax_highlighting.line_no=l_it->line_no;
-    syntax_highlighting(l_it->line);
-  }
-  
-  out << "
\n"; - out << "

\n"; -} - -/*******************************************************************\ - -Function: report_source_code - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_source_code( - const std::string &path_prefix_old, - const source_locationt &location_old, - const goto_programt &goto_program_old, - const std::string &description_old, - const std::string &path_prefix_new, - const source_locationt &location_new, - const goto_programt &goto_program_new, - const std::string &description_new, - const propertiest &properties, - std::ostream &out, - message_handlert &message_handler) -{ - // get sources - std::list lines_new, lines_old; - - get_source(path_prefix_old, location_old, goto_program_old, lines_old, message_handler); - get_source(path_prefix_new, location_new, goto_program_new, lines_new, message_handler); - - // run 'diff' - - diff_it(lines_old, lines_new); - - out << "

\n"; - out << "\n"; - out << "" - "" - "" - "" - "\n"; - - out << "\n"; - - // old version - - std::list::const_iterator l_old_it, l_it; - - out << "\n"; - - out << "\n"; - - // new version - - // error marking - out << "\n"; - - out << "\n"; - - - out << "\n"; - - out << "
" << html_escape(description_old) << "" << html_escape(description_new) << "
\n";
-  
-  for(std::list::const_iterator
-      l_old_it=lines_old.begin(); l_old_it!=lines_old.end(); l_old_it++)
-  {
-    if(l_old_it->line_no!=0) out << l_old_it->line_no;
-    out << "\n";
-  }
-    
-  out << "
\n";
-
-  {  
-    syntax_highlightingt syntax_highlighting(out);
-    syntax_highlighting.identifier_tooltip=true;
-    
-    for(l_old_it=lines_old.begin(), l_it=lines_new.begin();
-        l_old_it!=lines_old.end() && l_it!=lines_new.end();
-        l_old_it++, l_it++)
-    {
-      syntax_highlighting.strong_class=
-        (l_old_it->line!=l_it->line)?"different":"";
-      syntax_highlighting.line_no=l_it->line_no;
-      syntax_highlighting.id_suffix="@old";
-      syntax_highlighting(l_old_it->line);
-    }
-  }
-  
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines_new.begin(); l_it!=lines_new.end(); l_it++)
-  {
-    const linet &line=*l_it;
-
-    unsigned count=0;
-    
-    for(propertiest::const_iterator
-        p_it=properties.begin(); p_it!=properties.end(); p_it++, count++)
-    {
-      if(line.file==p_it->loc->source_location.get_file() &&
-         i2string(line.line_no)==as_string(p_it->loc->source_location.get_line()))
-      {
-        if(p_it->status.is_false())
-        {
-          irep_idt property=p_it->loc->source_location.get_property_class();
-          irep_idt comment=p_it->loc->source_location.get_comment();
-
-          std::string msg;
-
-          if(comment=="")
-            msg=as_string(property);
-          else
-            msg=as_string(comment);
-
-          out << ""
-              << "✗"
-              << "";
-        }
-      }
-    }
-  
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines_new.begin(); l_it!=lines_new.end(); l_it++)
-  {
-    if(l_it->line_no!=0) out << l_it->line_no;
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  {
-    syntax_highlightingt syntax_highlighting(out);
-    syntax_highlighting.identifier_tooltip=true;
-  
-    for(l_old_it=lines_old.begin(), l_it=lines_new.begin();
-        l_old_it!=lines_old.end() && l_it!=lines_new.end();
-        l_old_it++, l_it++)
-    {
-      syntax_highlighting.strong_class=
-        (l_old_it->line!=l_it->line)?"different":"";
-      syntax_highlighting.line_no=l_it->line_no;
-      syntax_highlighting(l_it->line);
-    }
-  }
-  
-  out << "
\n"; - out << "

\n"; -} - diff --git a/src/deltacheck/report_source_code.h b/src/deltacheck/report_source_code.h deleted file mode 100644 index 7987fb4c5..000000000 --- a/src/deltacheck/report_source_code.h +++ /dev/null @@ -1,40 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_REPORT_SOURCE_CODE_H -#define CPROVER_REPORT_SOURCE_CODE_H - -#include - -#include -#include - -#include "properties.h" - -void report_source_code( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - const propertiest &properties, - std::ostream &, - message_handlert &); - -void report_source_code( - const std::string &path_prefix_old, - const source_locationt &location_old, - const goto_programt &goto_program_old, - const std::string &description_old, - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - const std::string &description, - const propertiest &properties, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/show.cpp b/src/deltacheck/show.cpp deleted file mode 100644 index 02a2b2e4f..000000000 --- a/src/deltacheck/show.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/*******************************************************************\ - -Module: Showing various debugging information - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include - -#include "../ssa/ssa_domain.h" -#include "../ssa/guard_map.h" -#include "../ssa/local_ssa.h" -#include "../functions/index.h" -#include "ssa_fixed_point.h" - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - ssa_objectst ssa_objects(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects); - ssa_ait ssa_analysis(assignments); - ssa_analysis(goto_function, ns); - ssa_analysis.output(ns, goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_defs(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - guard_mapt guard_map(goto_function.body); - guard_map.output(goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_guards(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - local_SSA.output(out); -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_ssa(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_point( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - ssa_fixed_pointt ssa_fixed_point(local_SSA, ns); - ssa_fixed_point.output(out); -} - -/*******************************************************************\ - -Function: show_fixed_points - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_points( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_fixed_point(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_properties( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - show_properties(model, ui_message_handlert::PLAIN); - } - -} diff --git a/src/deltacheck/show.h b/src/deltacheck/show.h deleted file mode 100644 index 88617dfaf..000000000 --- a/src/deltacheck/show.h +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: SSA Showing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SHOW_H -#define CPROVER_SHOW_H - -#include -#include - -class message_handlert; -class indext; -class optionst; - -void show_ssa( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_defs( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_guards( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_fixed_points( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_properties( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/source_diff.cpp b/src/deltacheck/source_diff.cpp deleted file mode 100644 index 7cda65b44..000000000 --- a/src/deltacheck/source_diff.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Diffing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#include -#include -#include - -#ifdef DEBUG -#include -#endif - -#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__CYGWIN__) || defined(__MACH__) -#include -#endif - -#include - -#include "get_source.h" - -/*******************************************************************\ - -Function: diff_action - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -class diff_actiont -{ -public: - char action; - unsigned old_from, old_to, old_size; - unsigned new_from, new_to, new_size; - diff_actiont(const std::string &); - - void output(std::ostream &out) - { - if(action!=0) - out << "Action: " << action - << " Old: " << old_from << "-" << old_to << " (" << old_size << ")" - << " New: " << new_from << "-" << new_to << " (" << new_size << ")" - << "\n"; - } -}; - -diff_actiont::diff_actiont(const std::string &src) -{ - old_from=old_to=new_from=new_to=old_size=new_size=0; - action=0; - - // e.g. 4,5c4 - if(src.empty() || !isdigit(src[0])) return; - - std::string old_from_str, old_to_str, new_from_str, new_to_str; - - unsigned i; - - for(i=0; iold_to || new_from>new_to) action=0; -} - -/*******************************************************************\ - -Function: process_diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void process_diff( - std::list &lines_old, - std::list &lines_new, - const std::list &diff) -{ - // constant-time access to the list of lines members - std::vector::iterator> - l_it_old, l_it_new; - - l_it_old.reserve(lines_old.size()); - l_it_new.reserve(lines_new.size()); - - for(std::list::iterator it=lines_old.begin(); - it!=lines_old.end(); it++) - l_it_old.push_back(it); - - for(std::list::iterator it=lines_new.begin(); - it!=lines_new.end(); it++) - l_it_new.push_back(it); - - // now process the diff - for(std::list::const_iterator - d_it=diff.begin(); - d_it!=diff.end(); - d_it++) - { - diff_actiont da(*d_it); - - #ifdef DEBUG - da.output(std::cout); - #endif - - switch(da.action) - { - case 'c': // change - if(da.new_size>da.old_size) // enlarge - { - for(unsigned i=da.old_size; ida.new_size) // shrink - { - for(unsigned i=da.new_size; i &lines1, - std::list &lines2) -{ - std::string tmp1_name=get_temporary_file("delta_diff1", "txt"); - std::string tmp2_name=get_temporary_file("delta_diff2", "txt"); - std::string tmp3_name=get_temporary_file("delta_diff3", "txt"); - - { - std::ofstream out1(tmp1_name.c_str()); - std::ofstream out2(tmp2_name.c_str()); - - for(std::list::const_iterator l_it=lines1.begin(); - l_it!=lines1.end(); l_it++) - out1 << l_it->line << "\n"; - - for(std::list::const_iterator l_it=lines2.begin(); - l_it!=lines2.end(); l_it++) - out2 << l_it->line << "\n"; - } - - std::string cmdline="diff \""+tmp1_name+"\""+ - " \""+tmp2_name+"\""+ - "> \""+tmp3_name+"\""; - - int result=system(cmdline.c_str()); - - // open output - if(result>=0) - { - std::ifstream in(tmp3_name.c_str()); - std::string line; - std::list diff; - while(std::getline(in, line)) diff.push_back(line); - process_diff(lines1, lines2, diff); - } - - // clean up - unlink(tmp1_name.c_str()); - unlink(tmp2_name.c_str()); - unlink(tmp3_name.c_str()); -} - diff --git a/src/deltacheck/source_diff.h b/src/deltacheck/source_diff.h deleted file mode 100644 index 40eff5ad7..000000000 --- a/src/deltacheck/source_diff.h +++ /dev/null @@ -1,13 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Diffing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "get_source.h" - -void diff_it( - std::list &lines1, - std::list &lines2); diff --git a/src/deltacheck/ssa_fixed_point.cpp b/src/deltacheck/ssa_fixed_point.cpp deleted file mode 100644 index 672d22c0e..000000000 --- a/src/deltacheck/ssa_fixed_point.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include -#include - -#include "ssa_fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: ssa_fixed_pointt::tie_inputs_together - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::tie_inputs_together(std::list &dest) -{ - // the following are inputs: - // 1) the parameters of the functions - // 2) any global objects mentioned - // 3) the guard at the entry point - - if(SSA_old.goto_function.body.empty() || - SSA_new.goto_function.body.empty()) - return; - - // 1) function parameters - - const code_typet::parameterst &p_new= - SSA_new.goto_function.type.parameters(); - - const code_typet::parameterst &p_old= - SSA_old.goto_function.type.parameters(); - - for(unsigned p=0; pis_backwards_goto()); - - //locationt to=from->get_target(); - - // Record the objects modified by the loop to get - // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, from); - symbol_exprt out=SSA.read_rhs(*o_it, from); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - - ssa_objectt guard=SSA.guard_symbol(); - fixed_point.pre_state_vars.push_back(SSA.name(guard, local_SSAt::LOOP_BACK, from)); - fixed_point.post_state_vars.push_back(SSA.name(guard, local_SSAt::OUT, from)); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::do_backwards_edges - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::do_backwards_edges() -{ - // old program, if applicable - if(use_old) - { - forall_goto_program_instructions(i_it, SSA_old.goto_function.body) - { - if(i_it->is_backwards_goto()) - do_backwards_edge(SSA_old, i_it); - } - } - - // new program - forall_goto_program_instructions(i_it, SSA_new.goto_function.body) - { - if(i_it->is_backwards_goto()) - do_backwards_edge(SSA_new, i_it); - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::compute_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::compute_fixed_point() -{ - do_backwards_edges(); - setup_properties(); - - // set up transition relation - - // new function - fixed_point.transition_relation << SSA_new; - - if(use_old) - { - // old function, if applicable - fixed_point.transition_relation << SSA_old; - - // tie inputs together, if applicable - tie_inputs_together(fixed_point.transition_relation); - } - - // compute the fixed-point - fixed_point(); - - // we check the properties once we have the fixed point - check_properties(); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::check_properties() -{ - for(propertiest::iterator - p_it=properties.begin(); p_it!=properties.end(); p_it++) - { - #if 0 - solvert solver(ns); - #else - satcheckt satcheck; - bv_pointerst solver(ns, satcheck); - //solver.set_message_handler(get_message_handler()); - #endif - - // feed transition relation into solver - solver << fixed_point.transition_relation; - - // feed in fixed-point - solver << fixed_point.state_predicate; - - #ifdef DEBUG - std::cout << "GUARD: " << from_expr(ns, "", p_it->guard) << "\n"; - std::cout << "CHECKING: " << from_expr(ns, "", p_it->condition) << "\n"; - #endif - - // feed in the assertion - solver.set_to_true(p_it->guard); - solver.set_to_false(p_it->condition); - - // now solve - decision_proceduret::resultt result=solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - tvt status; - - if(result==decision_proceduret::D_UNSATISFIABLE) - status=tvt(true); - else if(result==decision_proceduret::D_SATISFIABLE) - { - status=tvt(false); - generate_countermodel(*p_it, solver); - } - else - status=tvt::unknown(); - - p_it->status=status; - - #ifdef DEBUG - std::cout << "RESULT: " << status << "\n"; - std::cout << "\n"; - #endif - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::countermodel_expr - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::countermodel_expr( - const exprt &src, - std::set &dest) -{ - forall_operands(it, src) - countermodel_expr(*it, dest); - - if(src.id()==ID_symbol) - dest.insert(src); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::generate_countermodel - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::generate_countermodel( - propertyt &property, - const decision_proceduret &solver) -{ - // collect all expressions from SSA that seem interesting - - std::set expressions; - - for(local_SSAt::nodest::const_iterator - n_it=SSA_new.nodes.begin(); - n_it!=SSA_new.nodes.end(); - n_it++) - { - const local_SSAt::nodet &node=n_it->second; - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=node.equalities.begin(); - e_it!=node.equalities.end(); - e_it++) - { - countermodel_expr(e_it->lhs(), expressions); - countermodel_expr(e_it->rhs(), expressions); - } - - for(local_SSAt::nodet::constraintst::const_iterator - e_it=node.constraints.begin(); - e_it!=node.constraints.end(); - e_it++) - { - countermodel_expr(*e_it, expressions); - } - - if(node.assertion.is_not_nil()) - countermodel_expr(node.assertion, expressions); - } - - if(use_old) - { - for(local_SSAt::nodest::const_iterator - n_it=SSA_old.nodes.begin(); - n_it!=SSA_old.nodes.end(); - n_it++) - { - const local_SSAt::nodet &node=n_it->second; - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=node.equalities.begin(); - e_it!=node.equalities.end(); - e_it++) - { - countermodel_expr(e_it->lhs(), expressions); - countermodel_expr(e_it->rhs(), expressions); - } - - for(local_SSAt::nodet::constraintst::const_iterator - e_it=node.constraints.begin(); - e_it!=node.constraints.end(); - e_it++) - { - countermodel_expr(*e_it, expressions); - } - - if(node.assertion.is_not_nil()) - countermodel_expr(node.assertion, expressions); - } - } - - // now collect from property - countermodel_expr(property.guard, expressions); - countermodel_expr(property.condition, expressions); - - // get values for those expressions - for(std::set::const_iterator - e_it=expressions.begin(); - e_it!=expressions.end(); - e_it++) - { - exprt value=solver.get(*e_it); - if(value.is_not_nil()) - property.value_map[*e_it]=value; - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::setup_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::setup_properties() -{ - forall_goto_program_instructions(i_it, SSA_new.goto_function.body) - { - if(i_it->is_assert()) - { - properties.push_back(propertyt()); - properties.back().loc=i_it; - properties.back().condition=SSA_new.read_rhs(i_it->guard, i_it); - properties.back().guard=SSA_new.guard_symbol(i_it); - } - } -} diff --git a/src/deltacheck/ssa_fixed_point.h b/src/deltacheck/ssa_fixed_point.h deleted file mode 100644 index 11f42c5ab..000000000 --- a/src/deltacheck/ssa_fixed_point.h +++ /dev/null @@ -1,88 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SSA_DATA_FLOW_H -#define DELTACHECK_SSA_DATA_FLOW_H - -#include - -#include "../ssa/local_ssa.h" -#include "properties.h" -#include "../solver/fixed_point.h" - -class ssa_fixed_pointt -{ -public: - typedef local_SSAt::locationt locationt; - - explicit ssa_fixed_pointt( - const local_SSAt &_SSA_old, - const local_SSAt &_SSA_new, - const namespacet &_ns): - SSA_old(_SSA_old), - SSA_new(_SSA_new), - ns(_ns), - use_old(true), - fixed_point(_ns) - { - compute_fixed_point(); - } - - explicit ssa_fixed_pointt( - const local_SSAt &_SSA, - const namespacet &_ns): - SSA_old(_SSA), - SSA_new(_SSA), - ns(_ns), - use_old(false), - fixed_point(_ns) - { - compute_fixed_point(); - } - - inline void output(std::ostream &out) const - { - fixed_point.output(out); - } - -protected: - const local_SSAt &SSA_old; - const local_SSAt &SSA_new; - const namespacet &ns; - bool use_old; - -public: - propertiest properties; - fixed_pointt fixed_point; - -protected: - // fixed-point computation - void tie_inputs_together(std::list &dest); - void compute_fixed_point(); - bool iteration(); - void initialize_invariant(); - - void do_backwards_edge( - const local_SSAt &SSA, locationt loc); - - void do_backwards_edges(); - - // properties - void setup_properties(); - void check_properties(); - - void countermodel_expr( - const exprt &src, - std::set &dest); - - void generate_countermodel( - propertyt &property, - const decision_proceduret &solver); -}; - -#endif diff --git a/src/deltacheck/statistics.cpp b/src/deltacheck/statistics.cpp deleted file mode 100644 index 9ac5feb4a..000000000 --- a/src/deltacheck/statistics.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/*******************************************************************\ - -Module: Statistics - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "../html/html_escape.h" -#include "statistics.h" - -/*******************************************************************\ - -Function: statisticst::clear - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::clear() -{ - time_map.clear(); - number_map.clear(); -} - -/*******************************************************************\ - -Function: statisticst::start - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::start(const std::string &what) -{ - timet &t=time_map[what]; - assert(!t.running); - t.start=current_time(); - t.running=true; -} - -/*******************************************************************\ - -Function: statisticst::stop - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::stop(const std::string &what) -{ - timet &t=time_map[what]; - assert(t.running); - time_periodt this_period=current_time()-t.start; - t.last=this_period; - t.total+=this_period; - t.running=false; -} - -/*******************************************************************\ - -Function: statisticst::html_report_total - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::html_report_total(std::ostream &out) const -{ - out << "

\n"; - - out << "\n"; - - for(number_mapt::const_iterator - it=number_map.begin(); it!=number_map.end(); it++) - { - out << "\n"; - } - - for(time_mapt::const_iterator - it=time_map.begin(); it!=time_map.end(); it++) - { - out << "\n"; - } - - out << "
"; - out << html_escape(it->first) << ":" - << it->second - << "
"; - out << html_escape(it->first) << ":" - << it->second.total - << "s
\n"; - - out << "

\n"; -} - -/*******************************************************************\ - -Function: statisticst::html_report_last - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::html_report_last(std::ostream &out) const -{ - out << "

\n"; - - for(time_mapt::const_iterator - it=time_map.begin(); it!=time_map.end(); it++) - { - if(it!=time_map.begin()) out << " "; - out << html_escape(it->first) << ": " - << it->second.last - << "s"; - } - - out << "\n

\n"; -} - diff --git a/src/deltacheck/statistics.h b/src/deltacheck/statistics.h deleted file mode 100644 index 4a61ebec9..000000000 --- a/src/deltacheck/statistics.h +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: Statistics - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_STATISTICS_H -#define DELTACHECK_STATISTICS_H - -#include -#include -#include - -#include - -class statisticst -{ -public: - inline statisticst() - { - clear(); - } - - struct timet - { - timet():running(false), total(0) { } - bool running; - time_periodt total, last; - absolute_timet start; - }; - - typedef std::map number_mapt; - number_mapt number_map; - - typedef std::map time_mapt; - time_mapt time_map; - - void html_report_total(std::ostream &) const; - void html_report_last(std::ostream &) const; - - void clear(); - - void start(const std::string &what); - void stop(const std::string &what); -}; - -#endif diff --git a/src/deltacheck/summarize_function_pointers.cpp b/src/deltacheck/summarize_function_pointers.cpp deleted file mode 100644 index 6c010e1f0..000000000 --- a/src/deltacheck/summarize_function_pointers.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "summarize_function_pointers.h" - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_programt::instructiont &instruction, - function_pointerst &function_pointers) -{ - if(instruction.is_assign()) - { - - } -} - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_programt &goto_program, - function_pointerst &function_pointers) -{ - forall_goto_program_instructions(i_it, goto_program) - { - summarize_function_pointers(context, *i_it, function_pointers); - } -} - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_functionst &goto_functions, - function_pointerst &function_pointers) -{ - forall_goto_functions(f_it, goto_functions) - { - summarize_function_pointers(context, f_it->second.body, function_pointers); - } -} - diff --git a/src/deltacheck/summarize_function_pointers.h b/src/deltacheck/summarize_function_pointers.h deleted file mode 100644 index 177d9d0b1..000000000 --- a/src/deltacheck/summarize_function_pointers.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Summarization of Function Pointers - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SUMMARIZE_FUNCTION_POINTERS_H -#define CPROVER_DELTACHECK_SUMMARIZE_FUNCTION_POINTERS_H - -#include -#include - -#include - -class function_pointerst -{ -public: - // a map from struct.field to a set of function identifiers - - typedef std::set id_sett; - - typedef hash_map_cont field_mapt; - field_mapt field_map; - - void print(std::ostream &); -}; - -void summarize_function_pointers( - const contextt &, - const goto_functionst &, - function_pointerst &dest); - -#endif diff --git a/src/deltacheck/version.h b/src/deltacheck/version.h deleted file mode 100644 index 805317409..000000000 --- a/src/deltacheck/version.h +++ /dev/null @@ -1 +0,0 @@ -#define DELTACHECK_VERSION "0.4.1" diff --git a/src/deltagit/Makefile b/src/deltagit/Makefile deleted file mode 100644 index 4c481facf..000000000 --- a/src/deltagit/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = deltagit_main.cpp deltagit_parse_options.cpp show_jobs.cpp \ - shell_escape.cpp git_log.cpp git_branch.cpp job_status.cpp do_job.cpp \ - deltagit_config.cpp revisions_report.cpp init.cpp reset.cpp \ - reanalyse.cpp - -OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - $(CBMC)/src/json/json$(LIBEXT) \ - $(CBMC)/src/big-int/big-int$(LIBEXT) \ - ../html/html_escape$(OBJEXT) \ - ../html/logo$(OBJEXT) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -LIBS = - -CLEANFILES = deltagit$(EXEEXT) - -all: deltagit$(EXEEXT) - -revisions_report$(OBJEXT): revisions_report_header.inc - -revisions_report_header.inc: revisions_report_header.html - ../html/to_c_string.perl < revisions_report_header.html > $@ - -############################################################################### - -deltagit$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/deltagit/deltagit_config.cpp b/src/deltagit/deltagit_config.cpp deleted file mode 100644 index 482c32351..000000000 --- a/src/deltagit/deltagit_config.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include - -#include "deltagit_config.h" - -/*******************************************************************\ - -Function: deltagit_configt::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltagit_configt::read() -{ - xmlt src; - - console_message_handlert message_handler; - - if(parse_xml("config.xml", message_handler, src)) - return; - - if(src.name!="deltagit_config") - throw std::string("unexpected XML for deltagit config"); - - description=src.get_element("description"); -} - diff --git a/src/deltagit/deltagit_config.h b/src/deltagit/deltagit_config.h deleted file mode 100644 index 8b6de7398..000000000 --- a/src/deltagit/deltagit_config.h +++ /dev/null @@ -1,29 +0,0 @@ -/*******************************************************************\ - -Module: Deltagit configuration - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_CONFIG_H -#define DELTAGIT_CONFIG_H - -#include - -class deltagit_configt -{ -public: - deltagit_configt() - { - read(); - } - - std::string description; - - void read(); - -protected: -}; - -#endif diff --git a/src/deltagit/deltagit_main.cpp b/src/deltagit/deltagit_main.cpp deleted file mode 100644 index 2115a268c..000000000 --- a/src/deltagit/deltagit_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "deltagit_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - deltagit_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - deltagit_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/deltagit/deltagit_parse_options.cpp b/src/deltagit/deltagit_parse_options.cpp deleted file mode 100644 index bbd8f4e1d..000000000 --- a/src/deltagit/deltagit_parse_options.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include - -#include "../deltacheck/version.h" - -#include "show_jobs.h" -#include "do_job.h" -#include "init.h" -#include "reset.h" -#include "reanalyse.h" -#include "deltagit_parse_options.h" -#include "revisions_report.h" - -/*******************************************************************\ - -Function: deltagit_parse_optionst::deltagit_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -deltagit_parse_optionst::deltagit_parse_optionst( - int argc, const char **argv): - parse_options_baset(DELTACHECK_OPTIONS, argc, argv) -{ -} - -/*******************************************************************\ - -Function: deltagit_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int deltagit_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - try - { - if(cmdline.args.size()==0) - { - usage_error(); - return 10; - } - - const std::string command=cmdline.args[0]; - - if(command=="jobs") - { - show_jobs(std::cout); - } - else if(command=="init") - { - if(cmdline.args.size()==1) - { - init(0); - } - else if(cmdline.args.size()==2) - { - init(atoi(cmdline.args[1].c_str())); - } - else - { - usage_error(); - return 10; - } - } - else if(command=="do") - { - if(cmdline.args.size()==2) - { - do_job(cmdline.args[1]); - } - else if(cmdline.args.size()==1) - { - do_job(); - } - else - { - usage_error(); - return 10; - } - } - else if(command=="reset") - { - if(cmdline.args.size()==2) - reset(cmdline.args[1]); - else if(cmdline.args.size()==1) - reset(); - else - { - usage_error(); - return 10; - } - } - else if(command=="reanalyse") - { - if(cmdline.args.size()==2) - reanalyse(cmdline.args[1]); - else if(cmdline.args.size()==1) - reanalyse(); - else - { - usage_error(); - return 10; - } - } - else if(command=="report") - { - bool partial_html=cmdline.isset("partial-html"); - unsigned max_revs=0; - std::string rel_path; - if(cmdline.isset("max-revs")) - max_revs=unsafe_string2unsigned(cmdline.get_value("max-revs")); - if(partial_html) - rel_path=cmdline.get_value("partial-html"); - revisions_report(partial_html, rel_path, max_revs); - } - else - { - usage_error(); - return 10; - } - } - - catch(const std::string &e) - { - std::cerr << e << std::endl; - return 13; - } - - catch(std::bad_alloc) - { - std::cerr << "Out of memory" << std::endl; - return 14; - } - - return 0; -} - -/*******************************************************************\ - -Function: deltagit_parse_optionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void deltagit_parse_optionst::help() -{ - std::cout << - "\n" - "* * DELTAGIT " DELTACHECK_VERSION " - Copyright (C) 2012-2013 * *\n" - "* * Daniel Kroening * *\n" - "* * Oxford University, Computer Science Department * *\n" - "* * kroening@kroening.com * *\n" - "\n" - "Usage: Purpose:\n" - "\n" - " deltagit [-?] [-h] [--help] show help\n" - " deltagit init set up the jobs\n" - " deltagit jobs list the jobs\n" - " deltagit do do given job\n" - " deltagit do do a job that needs work\n" - " deltagit reset clear failure bit on all jobs\n" - " deltagit reanalyse redo analysis\n" - " deltagit report generate top-level report\n" - "\n" - "Reporting options:\n" - " --partial-html generate a partial HTML file \"include.html\"\n" - " --max-revs report on the last revisions\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - "\n"; -} diff --git a/src/deltagit/deltagit_parse_options.h b/src/deltagit/deltagit_parse_options.h deleted file mode 100644 index 357f563ab..000000000 --- a/src/deltagit/deltagit_parse_options.h +++ /dev/null @@ -1,30 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H -#define CPROVER_DELTACHECK_PARSE_OPTIONS_H - -#include - -#define DELTACHECK_OPTIONS \ - "(verbosity):(version)(description):" \ - "(max-revs):(partial-html):" - -class deltagit_parse_optionst:public parse_options_baset -{ -public: - virtual int doit(); - virtual void help(); - - deltagit_parse_optionst( - int argc, const char **argv); - -protected: -}; - -#endif diff --git a/src/deltagit/do_job.cpp b/src/deltagit/do_job.cpp deleted file mode 100644 index 714cfc462..000000000 --- a/src/deltagit/do_job.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/*******************************************************************\ - -Module: Do a jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include - -#include "job_status.h" -#include "do_job.h" - -/*******************************************************************\ - -Function: check_out - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void check_out(job_statust &job_status) -{ - const std::string working_dir=job_status.get_wd(); - - // check if we already have it - if(access((working_dir+"/.git/HEAD").c_str(), R_OK)==0) - { - std::cout << "git repository for " << job_status.id - << " already present\n"; - job_status.next_stage(); - job_status.write(); - return; - } - - std::cout << "Checking out " << job_status.id << "\n"; - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command; - - // Do a shared clone -- this uses very little disc space. - // Will overwrite checkout log. - command="git clone --no-checkout --shared source-repo "+ - working_dir+ - " > jobs/"+job_status.id+".checkout.log 2>&1"; - - int result1=system(command.c_str()); - if(result1!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - return; - } - - // Now do checkout; this will eat disc space. - command="(cd "+working_dir+"; "+ - "git checkout --detach "+job_status.commit+ - ") >> jobs/"+job_status.id+".checkout.log 2>&1"; - - int result2=system(command.c_str()); - - if(result2!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - return; - } - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void build(job_statust &job_status) -{ - std::cout << "Building " << job_status.id << "\n"; - - const std::string working_dir=job_status.get_wd(); - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command; - - // Now run build script in working directory. - command="(cd "+working_dir+"; ../../build"+ - ") >> jobs/"+job_status.id+".build.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Build has failed\n"; - return; - } - - std::cout << "Build successful\n"; - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: analyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void analyse( - job_statust &job_status, - const std::list &jobs) -{ - // get the job before this one - - std::string previous=""; - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - if(j_it->id==job_status.id) break; - previous=j_it->id; - } - - if(previous!="") - { - std::cout << "Differential analysis between " - << previous << " and " << job_status.id - << "\n"; - - // is it built already? - job_statust old_version(previous); - - if(old_version.stage!=job_statust::ANALYSE && - old_version.stage!=job_statust::DONE) - { - std::cout << "Job " << previous << " is not built yet\n"; - return; - } - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command= - "./analyse \""+previous+"\" \""+job_status.id+"\"" - " > jobs/"+job_status.id+".analysis.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Analysis has failed\n"; - return; - } - - job_status.next_stage(); - job_status.write(); - std::cout << "Analysis completed\n"; - } - else - { - std::cout << "One-version analysis for " << job_status.id - << "\n"; - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command= - "./analyse-one \""+job_status.id+"\"" - " > jobs/"+job_status.id+".analysis.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Analysis has failed\n"; - return; - } - - job_status.next_stage(); - job_status.write(); - std::cout << "Analysis completed\n"; - } -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job(job_statust &job_status, - const std::list &jobs) -{ - if(job_status.status==job_statust::FAILURE) - { - std::cout << "Job " << job_status.id << " has failed, " - "consider resetting it.\n"; - } - else if(job_status.status==job_statust::RUNNING) - { - std::cout << "Job " << job_status.id - << " is already running.\n"; - } - else if(job_status.status==job_statust::COMPLETED) - { - std::cout << "Job " << job_status.id - << " is already completed.\n"; - } - else - { - switch(job_status.stage) - { - case job_statust::INIT: return; // done by deltagit init - case job_statust::CHECK_OUT: check_out(job_status); break; - case job_statust::BUILD: build(job_status); break; - case job_statust::ANALYSE: analyse(job_status, jobs); break; - case job_statust::DONE: break; - } - } - -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job(const std::string &id) -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // get current job status - job_statust job_status(id); - do_job(job_status, jobs); -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // Do a job that needs work, - // starting from the end of the log. - for(std::list::reverse_iterator - j_it=jobs.rbegin(); - j_it!=jobs.rend(); - j_it++) - { - if(j_it->stage!=job_statust::DONE && - j_it->stage!=job_statust::INIT && - j_it->status==job_statust::WAITING) - { - std::cout << "Doing job " << j_it->id << std::endl; - do_job(*j_it, jobs); - return; - } - } -} - diff --git a/src/deltagit/do_job.h b/src/deltagit/do_job.h deleted file mode 100644 index e0df19a50..000000000 --- a/src/deltagit/do_job.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Do a jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_DO_JOB_H -#define CPROVER_DELTAREPO_DO_JOB_H - -#include - -void do_job(const std::string &id); -void do_job(); - -#endif diff --git a/src/deltagit/git_branch.cpp b/src/deltagit/git_branch.cpp deleted file mode 100644 index 42e915ea5..000000000 --- a/src/deltagit/git_branch.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/*******************************************************************\ - -Module: Get git branch -a as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "git_branch.h" - -/*******************************************************************\ - -Function: git_brancht::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void git_brancht::read() -{ - temporary_filet tmpfile("deltagit", "log"); - - // get the git branches by running "git branch -a" - std::string command; - command="cd source-repo; git branch --list -a -v --no-abbrev > "+tmpfile(); - system(command.c_str()); - - // read it from temporary file - - std::ifstream in(tmpfile().c_str()); - if(!in) return; - - std::string line; - - - while(std::getline(in, line)) - { - if(has_prefix(line, "* ") || has_prefix(line, " ")) - { - const std::size_t branch_end=line.find(' ', 2); - if(branch_end==std::string::npos) continue; - - const std::size_t commit_pos=line.find_first_not_of(' ', branch_end); - if(commit_pos==std::string::npos) continue; - - std::size_t commit_end=line.find(' ', commit_pos); - if(commit_end==std::string::npos) commit_end=line.size(); - - entryt entry; - entry.name=line.substr(2, branch_end-1); - entry.commit=line.substr(commit_pos, commit_pos-commit_end); - entries.push_back(entry); - } - } -} diff --git a/src/deltagit/git_branch.h b/src/deltagit/git_branch.h deleted file mode 100644 index 23f1f513b..000000000 --- a/src/deltagit/git_branch.h +++ /dev/null @@ -1,37 +0,0 @@ -/*******************************************************************\ - -Module: Get git branch -a as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_GIT_BRANCH_H -#define DELTAGIT_GIT_BRANCH_H - -#include -#include - -class git_brancht -{ -public: - class entryt - { - public: - std::string name; - std::string commit; - }; - - typedef std::list entriest; - entriest entries; - - git_brancht() - { - read(); - } - -protected: - void read(); -}; - -#endif diff --git a/src/deltagit/git_log.cpp b/src/deltagit/git_log.cpp deleted file mode 100644 index 6f9816a65..000000000 --- a/src/deltagit/git_log.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*******************************************************************\ - -Module: Get git log as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include -#include - -#include "git_log.h" - -/*******************************************************************\ - -Function: git_logt::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void git_logt::read(unsigned max_commits) -{ - temporary_filet tmpfile("deltagit", "log"); - - // get the git log by running "git log" - std::string command; - command="cd source-repo; git log --name-only"; - if(max_commits!=0) command+=" --max-count="+i2string(max_commits); - command+=" > "+tmpfile(); - system(command.c_str()); - - // read it from temporary file - - std::ifstream in(tmpfile().c_str()); - if(!in) return; - - std::string line; - - entryt entry; - - while(std::getline(in, line)) - { - if(has_prefix(line, "commit ")) - { - if(entry.commit!="") - { - entries.push_back(entry); - entry=entryt(); // clear it - } - - entry.commit=line.substr(7, std::string::npos); - } - else if(has_prefix(line, "Author: ")) - { - entry.author=line.substr(8, std::string::npos); - } - else if(has_prefix(line, "Date: ")) - { - entry.date=line.substr(8, std::string::npos); - } - else if(has_prefix(line, " git-svn-id: ")) - { - std::size_t pos1=line.rfind('@'); - std::size_t pos2=line.rfind(' '); - if(pos1!=std::string::npos && pos2!=std::string::npos) - entry.git_svn_id=line.substr(pos1+1, pos2-pos1-1); - } - else if(!line.empty() && line[0]!=' ') - { - // shall be file name - entry.files.push_back(line); - } - } - - // last one - if(entry.commit!="") - entries.push_back(entry); -} diff --git a/src/deltagit/git_log.h b/src/deltagit/git_log.h deleted file mode 100644 index 27b307707..000000000 --- a/src/deltagit/git_log.h +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Get git log as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_GIT_LOG_H -#define DELTAGIT_GIT_LOG_H - -#include -#include - -class git_logt -{ -public: - class entryt - { - public: - std::string commit; - std::string author; - std::string date; - std::string git_svn_id; - std::list files; - }; - - typedef std::list entriest; - entriest entries; - - // Read at most max_commits many entries; - // 0 means no limit. - explicit git_logt(unsigned max_commits=0) - { - read(max_commits); - } - -protected: - void read(unsigned max_commits); -}; - -#endif diff --git a/src/deltagit/init.cpp b/src/deltagit/init.cpp deleted file mode 100644 index ff5c8b9aa..000000000 --- a/src/deltagit/init.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*******************************************************************\ - -Module: Initialize Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include - -#ifdef _WIN32 -#include -#endif - -#include -#include - -#include "job_status.h" -#include "init.h" -#include "git_log.h" - -/*******************************************************************\ - -Function: init - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void init(job_statust &job_status) -{ - std::string command; - temporary_filet tempfile("deltagit", "txt"); - - // do a git show to learn more about the job - command="cd source-repo; git show "+job_status.commit+ - " --numstat"+ - " > "+tempfile(); - - int result1=system(command.c_str()); - if(result1!=0) - { - job_status.status=job_statust::FAILURE; - // don't write, commit might be bogus - return; - } - - // parse the file - std::ifstream in(tempfile().c_str()); - if(!in) return; - - std::string line; - - job_status.added=0; - job_status.deleted=0; - job_status.message=""; - - while(std::getline(in, line)) - { - if(has_prefix(line, " git-svn-id: ")) - { - } - else if(has_prefix(line, " ")) - { - // commit message - job_status.message+=line.substr(4, std::string::npos)+"\n"; - } - else if(has_prefix(line, "Author: ")) - { - job_status.author=line.substr(8, std::string::npos); - } - else if(has_prefix(line, "Date: ")) - { - job_status.date=line.substr(8, std::string::npos); - } - else if(!line.empty() && isdigit(line[0])) - { - // \t\t - const std::size_t end_added=line.find('\t', 0); - if(end_added==std::string::npos) continue; - const std::size_t end_deleted=line.find('\t', end_added+1); - if(end_deleted==std::string::npos) continue; - - job_status.added+=atol(line.substr(0, end_added).c_str()); - job_status.deleted+=atol(line.substr(end_added+1, end_deleted-end_added-1).c_str()); - } - } - - // strip trailing \n from commit message - std::string &message=job_status.message; - while(!message.empty() && message[message.size()-1]=='\n') - message.resize(message.size()-1); - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: get_extension - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_extension(const std::string &s) -{ - std::size_t pos=s.rfind('.'); - if(pos==std::string::npos) return ""; - return s.substr(pos+1, std::string::npos); -} - -/*******************************************************************\ - -Function: get_file - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_file(const std::string &s) -{ - std::size_t pos=s.rfind('/'); - if(pos==std::string::npos) return s; - return s.substr(pos+1, std::string::npos); -} - -/*******************************************************************\ - -Function: init - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void init(unsigned max_commits) -{ - // get jobs from git log - std::list jobs; - - std::cout << "Getting git log\n"; - - // get the git log - git_logt git_log(max_commits); - - // rummage through it, looking for 'interesting' commits - // we reverse, to start with older commits - for(git_logt::entriest::const_reverse_iterator - l_it=git_log.entries.rbegin(); - l_it!=git_log.entries.rend(); - l_it++) - { - bool found=false; - - for(std::list::const_iterator - f_it=l_it->files.begin(); - f_it!=l_it->files.end(); - f_it++) - { - std::string file=get_file(*f_it); - std::string ext=get_extension(file); - - if(ext=="c" || ext=="C" || - ext=="cpp" || ext=="c++" || - ext=="h" || ext=="hpp") - { - found=true; - break; - } - } - - if(found) - { - std::string id; - - if(l_it->git_svn_id!="") - id="r"+l_it->git_svn_id; - else - id=l_it->commit; - - job_statust job_status(id); - job_status.commit=l_it->commit; - - jobs.push_back(job_status); - } - } - - // Make sure we have a 'jobs' directory - #ifdef _WIN32 - mkdir("jobs"); - #else - mkdir("jobs", 0777); - #endif - - unsigned total=0; - - // Do jobs that need to be initialized, - // starting from the end. - for(std::list::reverse_iterator - j_it=jobs.rbegin(); - j_it!=jobs.rend(); - j_it++) - { - if(j_it->stage==job_statust::INIT && - j_it->status!=job_statust::FAILURE) - { - std::cout << "Setting up job " << j_it->id << "\n"; - std::cout << std::flush; - init(*j_it); - total++; - } - } - - std::cout << "Added " << total << " jobs\n"; -} - diff --git a/src/deltagit/init.h b/src/deltagit/init.h deleted file mode 100644 index 5787e4af6..000000000 --- a/src/deltagit/init.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Initialize Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_INIT_H -#define CPROVER_DELTAGIT_INIT_H - -void init(unsigned max_commits); - -#endif diff --git a/src/deltagit/job_status.cpp b/src/deltagit/job_status.cpp deleted file mode 100644 index e7a06e18a..000000000 --- a/src/deltagit/job_status.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#ifdef _WIN32 -#else -#include -#include -#endif - -#include - -#include -#include -#include - -#include - -#include "git_log.h" -#include "job_status.h" - -/*******************************************************************\ - -Function: job_statust::set_hostname - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::set_hostname() -{ - #ifdef _WIN32 - #else - char s[1000]; - if(gethostname(s, 1000)==0) - { - hostname=std::string(s); - } - #endif -} - -/*******************************************************************\ - -Function: job_statust::next_stage - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::next_stage() -{ - assert(stage!=DONE); - stage=(staget)((int)stage+1); - status=stage==DONE?COMPLETED:WAITING; -} - -/*******************************************************************\ - -Function: job_statust::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::read() -{ - xmlt src; - - console_message_handlert message_handler; - - if(parse_xml("jobs/"+id+".status", message_handler, src)) - { - // assume it's new - clear(); - return; - } - - if(src.name!="deltagit_jobstatus") - throw std::string("unexpected XML for job status"); - - const std::string stage_string=src.get_attribute("stage"); - - if(stage_string=="init") - stage=INIT; - else if(stage_string=="check out") - stage=CHECK_OUT; - else if(stage_string=="build") - stage=BUILD; - else if(stage_string=="analyse") - stage=ANALYSE; - else if(stage_string=="done") - stage=DONE; - else - throw std::string("unexpected stage"); - - const std::string status_string=src.get_attribute("status"); - - if(status_string=="waiting") - status=WAITING; - else if(status_string=="running") - status=RUNNING; - else if(status_string=="failure") - status=FAILURE; - else if(status_string=="completed") - status=COMPLETED; - else - throw std::string("unexpected status"); - - added=atol(src.get_attribute("added").c_str()); - deleted=atol(src.get_attribute("deleted").c_str()); - message=src.get_element("message"); - author=src.get_attribute("author"); - date=src.get_attribute("date"); - commit=src.get_attribute("commit"); - - hostname=src.get_attribute("hostname"); -} - -/*******************************************************************\ - -Function: as_string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string as_string(job_statust::staget stage) -{ - switch(stage) - { - case job_statust::INIT: return "init"; - case job_statust::CHECK_OUT: return "check out"; - case job_statust::BUILD: return "build"; - case job_statust::ANALYSE: return "analyse"; - case job_statust::DONE: return "done"; - default: return ""; - } -} - -/*******************************************************************\ - -Function: as_string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string as_string(job_statust::statust status) -{ - switch(status) - { - case job_statust::WAITING: return "waiting"; - case job_statust::RUNNING: return "running"; - case job_statust::FAILURE: return "failure"; - case job_statust::COMPLETED: return "completed"; - default: return ""; - } -} - -/*******************************************************************\ - -Function: job_statust::write - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::write() -{ - xmlt xml; - xml.name="deltagit_jobstatus"; - - xml.set_attribute("id", id); - xml.set_attribute("status", as_string(status)); - xml.set_attribute("stage", as_string(stage)); - xml.set_attribute("commit", commit); - xml.set_attribute("added", added); - xml.set_attribute("deleted", deleted); - xml.set_attribute("author", author); - xml.set_attribute("date", date); - xml.set_attribute("hostname", hostname); - xml.new_element("message").data=message; - - std::ofstream out(("jobs/"+id+".status").c_str()); - out << xml; -} - -/*******************************************************************\ - -Function: get_jobs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -class job_ordering -{ -public: - bool operator()(const job_statust &j1, const job_statust &j2) - { - // SVN revisions rNNNN? - const std::string &s1=j1.id; - const std::string &s2=j2.id; - - if(s1.size()>=2 && s2.size()>=2 && - s1[0]=='r' && s2[0]=='r') - return atol(s1.substr(1, std::string::npos).c_str())< - atol(s2.substr(1, std::string::npos).c_str()); - else - { - #ifdef _WIN32 - throw "todo: win32 doesn't have strptime"; - #else - // use date - struct tm tm1, tm2; - // Tue Feb 4 12:29:13 2014 +0000 - strptime(j1.date.c_str(), "%a %b %d %T %Y %z", &tm1); - strptime(j2.date.c_str(), "%a %b %d %T %Y %z", &tm2); - - time_t t1=mktime(&tm1); - time_t t2=mktime(&tm2); - - // secondary criterion - if(t1==t2) return s1 &jobs) -{ - // sort into set - std::set job_set; - - DIR *dir=opendir("jobs"); - if(dir==NULL) return; - - std::string suffix=".status"; - - struct dirent *ent; - while((ent=readdir(dir))!=NULL) - { - std::string name=ent->d_name; - if(has_suffix(name, suffix)) - { - std::string id=name.substr(0, name.size()-suffix.size()); - job_statust job_status(id); - job_set.insert(job_status); - } - } - - closedir(dir); - - // dump the set into list - for(std::set::const_iterator - s_it=job_set.begin(); - s_it!=job_set.end(); - s_it++) - jobs.push_back(*s_it); -} diff --git a/src/deltagit/job_status.h b/src/deltagit/job_status.h deleted file mode 100644 index 38f0ac1a9..000000000 --- a/src/deltagit/job_status.h +++ /dev/null @@ -1,72 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_JOB_STATUS_H -#define DELTAGIT_JOB_STATUS_H - -#include -#include - -class job_statust -{ -public: - explicit job_statust(const std::string &_id):id(_id) - { - read(); - } - - // unique identifier - std::string id; - - // stuff about the commit - std::string commit; - std::string message; - std::string author; - std::string date; - - unsigned added, deleted; - - // analysis status - enum statust { WAITING, RUNNING, FAILURE, COMPLETED }; - enum staget { INIT, CHECK_OUT, BUILD, ANALYSE, DONE }; - statust status; - staget stage; - - std::string hostname; - - void read(); - void write(); - - void clear() - { - commit=""; - status=WAITING; - stage=INIT; - added=deleted=0; - } - - void next_stage(); - - std::string get_wd() const - { - return "jobs/"+id+".wd"; - } - - void set_hostname(); - -protected: -}; - -std::string as_string(job_statust::statust); -std::string as_string(job_statust::staget); - -typedef std::list jobst; - -void get_jobs(jobst &); - -#endif diff --git a/src/deltagit/log_scale.cpp b/src/deltagit/log_scale.cpp deleted file mode 100644 index 3777babfc..000000000 --- a/src/deltagit/log_scale.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "log_scale.h" - -const char log_scale[]= - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAsCAIAAACPL/3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAjlJREFUeNrslsGVozAMhuXpwCnBlGBKUEogJUAJ4ZxcTAlxCaEEKAFKgBLsEjQHzfJYm7AJE/awOzrk2X6S/wTp448gItg/PuabsiyPxyOvrbVCCCFEmqbee+99mqZ8Yq0FgL7vkyQRQiRJ0vc9ABRFwQlFUYQ6REREwzDwFhGJyDknpTTGOOeUUsYYY4xSyjlnjJFSOucQMcsyIsqyDBGbpgGAruu6rgOApmn45svlQkRfMhx5nrMM1/Dh+XxGREQ0xvA34Fumu+73OwAYY7iWiLTWnDzJfCw+ynEclVK8llLyCS+mLQBwznQSlDzszRRKKb4IALz3UkqllPeetwCgtZ7EpoR5yVMyWmspZVVV3vu6rrXWiGit9d5ba6WU0wkA1HWNiFrrtm37X4GICyMQ9IaIbrdbMBT8C6SU3JKu6/gpaa2HYeByLpk3huM3mT1ibQT2xTOOl4ANEh72Zh4bgA0SHuIZx6vAzhO29OYZYF/GcwOw3vsgYYvMH4HN8zxIeGoENgAbJPy3eMbzdjgc2rblbWCXsZ8+25ugT1zCnYjtMvDTF/BcfDWwTGyXgZ++pzeLPM79dHtvVoCdtL+F5yKwgV0GfrpxBILexHYZ++kPnotR17UQgtcreH5LJkmS0+k0d3StNWNUluX2EYiD38q83gXPmNZd8Ixp3QXPOFbwfKeMMabveyHEOI78Jr1er1+fP3iuRpqmVVXtKMP/oUN7frtMnudENHndvg/tL/Xm35WZ8PwcAHt/WQW5PfKNAAAAAElFTkSuQmCC"; diff --git a/src/deltagit/log_scale.h b/src/deltagit/log_scale.h deleted file mode 100644 index d9b389181..000000000 --- a/src/deltagit/log_scale.h +++ /dev/null @@ -1 +0,0 @@ -extern const char log_scale[]; diff --git a/src/deltagit/log_scale.png b/src/deltagit/log_scale.png deleted file mode 100644 index 08a271e10..000000000 Binary files a/src/deltagit/log_scale.png and /dev/null differ diff --git a/src/deltagit/reanalyse.cpp b/src/deltagit/reanalyse.cpp deleted file mode 100644 index a0cbe33ce..000000000 --- a/src/deltagit/reanalyse.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/*******************************************************************\ - -Module: Reanalyse Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "job_status.h" -#include "reanalyse.h" - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse(job_statust &job_status) -{ - if(job_status.stage==job_statust::DONE) - { - std::cout << "Reanalysing job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.stage=job_statust::ANALYSE; - job_status.write(); - } - else if(job_status.stage==job_statust::ANALYSE && - job_status.status==job_statust::FAILURE) - { - std::cout << "Resetting job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.stage=job_statust::ANALYSE; - job_status.write(); - } -} - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // reanalyse jobs that need to be - for(std::list::iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - reanalyse(*j_it); - } -} - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse(const std::string &job) -{ - job_statust job_status(job); - reanalyse(job_status); -} - diff --git a/src/deltagit/reanalyse.h b/src/deltagit/reanalyse.h deleted file mode 100644 index ddfb3f59f..000000000 --- a/src/deltagit/reanalyse.h +++ /dev/null @@ -1,15 +0,0 @@ -/*******************************************************************\ - -Module: Reanalyse Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_REANALYSE_H -#define CPROVER_DELTAGIT_REANALYSE_H - -void reanalyse(); -void reanalyse(const std::string &job); - -#endif diff --git a/src/deltagit/reset.cpp b/src/deltagit/reset.cpp deleted file mode 100644 index 76ef93b06..000000000 --- a/src/deltagit/reset.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Reset Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "job_status.h" -#include "reset.h" - -/*******************************************************************\ - -Function: reset - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reset() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // reset jobs that need to be - for(std::list::iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - if(j_it->status==job_statust::FAILURE) - { - std::cout << "Resetting job " << j_it->id << "\n"; - j_it->status=job_statust::WAITING; - j_it->write(); - } - } -} - -/*******************************************************************\ - -Function: reset - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reset(const std::string &job) -{ - job_statust job_status(job); - - if(job_status.status==job_statust::FAILURE) - { - std::cout << "Resetting job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.write(); - } -} - diff --git a/src/deltagit/reset.h b/src/deltagit/reset.h deleted file mode 100644 index 1e48308d9..000000000 --- a/src/deltagit/reset.h +++ /dev/null @@ -1,15 +0,0 @@ -/*******************************************************************\ - -Module: Reset Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_RESET_H -#define CPROVER_DELTAGIT_RESET_H - -void reset(); -void reset(const std::string &job); - -#endif diff --git a/src/deltagit/revisions_report.cpp b/src/deltagit/revisions_report.cpp deleted file mode 100644 index cb943234b..000000000 --- a/src/deltagit/revisions_report.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*******************************************************************\ - -Module: Show the overview for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "../html/html_escape.h" -#include "../html/logo.h" - -#include "revisions_report.h" -#include "job_status.h" -#include "deltagit_config.h" -#include "log_scale.h" - -const char revisions_report_header[]= -#include "revisions_report_header.inc" -; - -/*******************************************************************\ - -Function: shorten_message - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string shorten_message(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - unsigned line_count=1; - - for(unsigned i=0; i20) // arbitrary magic number - { - result+="...\n"; - break; - } - } - } - - return result; -} - -/*******************************************************************\ - -Function: htmlize_message - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string htmlize_message(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - for(unsigned i=0; i': result+=">"; break; - case '"': result+="""; break; - case '&': result+="&"; break; - case '\n': result+="
"; break; - - // ' does not seem to be universally supported, - // and Unicode seems to suggest to prefer ’ over ' - case '\'': result+="&8217;"; break; - - default: - if(src[i]>=' ') result+=src[i]; - } - - return result; -} - -/*******************************************************************\ - -Function: height - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned height(const job_statust &job_status) -{ - unsigned lines=job_status.added+job_status.deleted; - if(lines==0) return 0; - if(lines==1) return 1; - return log10(lines)*10; -} - -/*******************************************************************\ - -Function: revisions_report - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void revisions_report( - bool partial_html, - const std::string &rel_path, - unsigned max_revs) -{ - deltagit_configt deltagit_config; - - std::string title="DeltaCheck Summary of Revisions"; - if(deltagit_config.description!="") - title+=" "+deltagit_config.description; - - std::list jobs; - - get_jobs(jobs); - - unsigned max_height=44; // the hight of log_scale.png - - std::string outfile_name= - partial_html?"include.html":"index.html"; - - std::ofstream out(outfile_name.c_str()); - - if(!partial_html) - { - out << "\n"; - out << "\n" - "\n" - "\n"; - - out << "" << html_escape(title) << "\n"; - - out << revisions_report_header; - - out << "\n\n"; - - out << "\n\n"; - - out << "\"DeltaCheck\n\n"; - } - - out << "
" - << html_escape(deltagit_config.description) - << "
\n"; - - out << "
\n"; - - out << "\n" - << "\n
\n" - << "\n" - << "\n"; - - unsigned counter=0, number_of_jobs=jobs.size(); - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++, counter++) - { - if(max_revs!=0 && - counter+max_revsget_wd()+"/deltacheck-stat.json"; - jsont deltacheck_summary; - null_message_handlert null_message_handler; - parse_json(summary_file_name, null_message_handler, deltacheck_summary); - const jsont &properties=deltacheck_summary["properties"]; - passed=unsafe_string2unsigned(properties["passed"].value); - failed=unsafe_string2unsigned(properties["failed"].value); - } - - std::string tooltip= - "
"+j_it->id+"
"+ - ""; - if(j_it->author!="") tooltip+="Author: "+html_escape(j_it->author)+"
"; - if(j_it->date!="") tooltip+="Date: "+html_escape(j_it->date)+"
"; - tooltip+=htmlize_message(shorten_message(j_it->message)); - if(j_it->stage!=job_statust::DONE) - { - tooltip+="
"+html_escape(as_string(j_it->stage)); - tooltip+=" "+html_escape(as_string(j_it->status)); - if(j_it->hostname!="" && j_it->status!=job_statust::WAITING) - tooltip+=" on "+html_escape(as_string(j_it->hostname)); - tooltip+=""; - } - tooltip+= - "
"; - - unsigned h=std::min(height(*j_it), max_height); - - std::string link; - std::string bar_color="#7070e0"; - - if(j_it->stage==job_statust::ANALYSE) - { - link=rel_path+"/"+j_it->get_wd()+"/deltacheck-diff.html"; - } - else if(j_it->stage==job_statust::DONE) - { - link=rel_path+"/"+j_it->get_wd()+"/deltacheck-diff.html"; - - unsigned r, g; - if(passed+failed==0) - { - r=0; - g=255; - } - else - { - r=(unsigned long long)255*failed/(passed+failed); - g=(unsigned long long)255*passed/(passed+failed); - } - - char buffer[100]; - snprintf(buffer, 100, "#%02x%02x30", r, g); - bar_color=buffer; - } - else - { - if(j_it->status==job_statust::FAILURE) - bar_color="#e0e0e0"; - } - - if(link!="") - out << ""; - - out << "
id << "\"" - " onMouseOver=\"tooltip.show('" << tooltip << "');\"" - " onMouseOut=\"tooltip.hide();\"" - ">"; - - out << "
"; - - out << "
"; - out << "
"; - - if(link!="") - out << "
"; - - out << "\n"; - } - - out << "
\n"; - - // revisions - out << "
\n"; - - if(!partial_html) - { - out << "\n"; - out << "\n"; - } -} diff --git a/src/deltagit/revisions_report.h b/src/deltagit/revisions_report.h deleted file mode 100644 index fe5bfff78..000000000 --- a/src/deltagit/revisions_report.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Show the overview for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_REVISIONS_REPORT_H -#define CPROVER_DELTAGIT_REVISIONS_REPORT_H - -void revisions_report( - bool partial_html, - const std::string &rel_path, - unsigned max_revs); - -#endif diff --git a/src/deltagit/revisions_report_header.html b/src/deltagit/revisions_report_header.html deleted file mode 100644 index 6e5ba3e5b..000000000 --- a/src/deltagit/revisions_report_header.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - diff --git a/src/deltagit/shell_escape.cpp b/src/deltagit/shell_escape.cpp deleted file mode 100644 index 96991a113..000000000 --- a/src/deltagit/shell_escape.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "shell_escape.h" - -/*******************************************************************\ - -Function: shell_escape - - Inputs: - - Outputs: - - Purpose: escape characters for bash - -\*******************************************************************/ - -std::string shell_escape(const std::string &src) -{ - bool clean=true; - - for(std::string::const_iterator - it=src.begin(); it!=src.end(); it++) - { - // positive list of safe characters - if(isalnum(*it) || *it=='_' || *it=='/' || - *it=='.' || *it==':') - { - } - else - clean=false; - } - - if(clean) - return src; - - std::string result; - result.reserve(src.size()+2); - - result+='\''; - - for(std::string::const_iterator - it=src.begin(); it!=src.end(); it++) - { - if(*it=='\'') - result+="'\\''"; // quote, backslash, quote - else - result+=*it; - } - - result+='\''; - - return result; -} diff --git a/src/deltagit/shell_escape.h b/src/deltagit/shell_escape.h deleted file mode 100644 index d27c6a5ad..000000000 --- a/src/deltagit/shell_escape.h +++ /dev/null @@ -1,13 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -std::string shell_escape(const std::string &); - - diff --git a/src/deltagit/show_jobs.cpp b/src/deltagit/show_jobs.cpp deleted file mode 100644 index 4956442c7..000000000 --- a/src/deltagit/show_jobs.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*******************************************************************\ - -Module: Show the jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include "show_jobs.h" -#include "job_status.h" - -/*******************************************************************\ - -Function: show_jobs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_jobs(std::ostream &out) -{ - std::list jobs; - - get_jobs(jobs); - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - out << j_it->id; - - out << " " << as_string(j_it->stage) - << " " << as_string(j_it->status); - - if(j_it->hostname!="") - out << " on " << j_it->hostname; - - out << "\n"; - } -} diff --git a/src/deltagit/show_jobs.h b/src/deltagit/show_jobs.h deleted file mode 100644 index 749acc2b3..000000000 --- a/src/deltagit/show_jobs.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Show the jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_SHOW_JOBS_H -#define CPROVER_DELTAREPO_SHOW_JOBS_H - -#include - -void show_jobs(std::ostream &); - -#endif diff --git a/src/deltagit/update.cpp b/src/deltagit/update.cpp deleted file mode 100644 index 28385e698..000000000 --- a/src/deltagit/update.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/*******************************************************************\ - -Module: Update a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "update.h" -#include "deltarepo_config.h" - -/*******************************************************************\ - -Function: update - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void update() -{ - deltarepo_configt config; - - switch(config.kind) - { - case NONE: assert(false); break; - - case GIT: - { - } - break; - - case SVN: - { - - } - break; - } -} diff --git a/src/deltagit/update.h b/src/deltagit/update.h deleted file mode 100644 index 4dee5e1a6..000000000 --- a/src/deltagit/update.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Update a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_UPDATE_H -#define CPROVER_DELTAREPO_UPDATE_H - -void update(); - -#endif diff --git a/src/dist-bin b/src/dist-bin deleted file mode 100755 index 76c005560..000000000 --- a/src/dist-bin +++ /dev/null @@ -1,9 +0,0 @@ -file=`date "+%Y-%m-%d"` - -strip deltacheck/deltacheck - -rm -f binaries/*.gz - -gzip -9 < deltacheck/deltacheck > binaries/deltacheck-$file-linux32.gz - -scp binaries/*.gz kroening@dkr-srv.cs.ox.ac.uk:/srv/www/cprover.org/tmp/binaries/ diff --git a/src/domains/Makefile b/src/domains/Makefile index 8ba93df10..86018b186 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -1,29 +1,30 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ - tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ +SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ ssa_analyzer.cpp util.cpp incremental_solver.cpp \ - strategy_solver_base.cpp \ + strategy_solver_base.cpp strategy_solver_equality.cpp \ linrank_domain.cpp lexlinrank_domain.cpp\ ranking_solver_enumeration.cpp lexlinrank_solver_enumeration.cpp \ strategy_solver_enumeration.cpp strategy_solver_binsearch.cpp \ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp + strategy_solver_predabs.cpp disjunctive_analyzer.cpp \ + simplify_transformer.cpp simplify_bounds.cpp #solver_enumeration.cpp +include ../config.inc include $(CBMC)/src/config.inc include $(CBMC)/src/common +CBMC ?= ../.. -CP_CXXFLAGS += $(SUMMARIZERFLAGS) +CP_CXXFLAGS += $(TWOLSFLAGS) -INCLUDES= -I $(CBMC)/src +INCLUDES= -I $(CBMC)/src -I .. -CLEANFILES = +CLEANFILES = domains$(LIBEXT) -all: $(OBJ) +all: domains$(LIBEXT) ############################################################################### +domains$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/domains/disjunctive_analyzer.cpp b/src/domains/disjunctive_analyzer.cpp new file mode 100644 index 000000000..28d506624 --- /dev/null +++ b/src/domains/disjunctive_analyzer.cpp @@ -0,0 +1,315 @@ +/*******************************************************************\ + +Module: Data Flow Analysis + +Author: Peter Schrammel, Kumar Madhukar + +\*******************************************************************/ + +#include +#include + +#include "ssa_analyzer.h" +#include "disjunctive_analyzer.h" + +/*******************************************************************\ + +Function: disjunctive_analyzert::eliminate_implication() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::eliminate_implication(exprt &formula) +{ + if(formula.id()==ID_implies) + formula=or_exprt(not_exprt(formula.op0()), formula.op1()); + + Forall_operands(it, formula) + eliminate_implication(*it); +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::push_negation_to_atoms() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::push_negation_to_atoms(exprt &formula) +{ + if(formula.id()==ID_not) + { + if((formula.op0()).id()==ID_not) + { + formula=(formula.op0()).op0(); + } + else + { + exprt::operandst operands; + Forall_operands(it, formula.op0()) + operands.push_back(not_exprt(*it)); + + if((formula.op0()).id()==ID_and) + { + formula=disjunction(operands); + } + else + { + if((formula.op0()).id()==ID_or) + { + formula=conjunction(operands); + } + } + } + } + + Forall_operands(it, formula) + push_negation_to_atoms(*it); +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::convert_to_dnf() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::convert_to_dnf(exprt &formula) +{ + eliminate_implication(formula); + push_negation_to_atoms(formula); + convert_to_dnf_rec(formula); +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::convert_to_dnf_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::convert_to_dnf_rec(exprt &formula) +{ + if(formula.id()==ID_or) + { + Forall_operands(it, formula) + convert_to_dnf_rec(*it); + } + else + { + if(formula.id()==ID_and) + { + Forall_operands(it, formula) + convert_to_dnf_rec(*it); + + while((formula.operands()).size()>1) + { + exprt::operandst first_operands, second_operands, combination; + + if(((formula.operands()).back()).id()==ID_or) + first_operands=((formula.operands()).back()).operands(); + else + first_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + if(((formula.operands()).back()).id()==ID_or) + second_operands=((formula.operands()).back()).operands(); + else + second_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + for(const auto &fo : first_operands) + { + for(const auto &so : second_operands) + { + combination.push_back(and_exprt(fo, so)); + } + } + + formula.operands().push_back(disjunction(combination)); + } + formula=formula.op0(); + } + } +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool disjunctive_analyzert::operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars) +{ + bool response=true; + exprt::operandst result; + + exprt simple_disjunctive_conditions= + simplify_expr(disjunctive_conditions, SSA.ns); // disjunctive_conditions + + // converting simple_disjunctive_conditions into DNF + convert_to_dnf(simple_disjunctive_conditions); + + if(simple_disjunctive_conditions.id()==ID_or) + { + exprt::operandst processed_disjuncts; + + exprt::operandst disjuncts=simple_disjunctive_conditions.operands(); + for(auto &d : disjuncts) + { + if(d.id()==ID_not) + { + exprt::operandst ops=d.operands(); + for(auto &op : ops) + { + if(op.id()==ID_equal) + { + exprt::operandst ops_equality=op.operands(); + equal_exprt equal_expr_in_not=to_equal_expr(op); + + bool constant_comparison=false; + for(auto &oe : ops_equality) + { + if(oe.id()==ID_constant) + constant_comparison=true; + } + if(constant_comparison) + { + processed_disjuncts.push_back( + binary_relation_exprt( + equal_expr_in_not.rhs(), ID_gt, equal_expr_in_not.lhs())); + processed_disjuncts.push_back( + binary_relation_exprt( + equal_expr_in_not.rhs(), ID_lt, equal_expr_in_not.lhs())); + } + else + { + processed_disjuncts.push_back(d); + } + } + else + { + processed_disjuncts.push_back(d); + } + } + } + else + { + processed_disjuncts.push_back(d); + } + } + + for(auto &d : processed_disjuncts) + { + std::set disjunct_symbols; + find_symbols(d, disjunct_symbols); + + // TODO: decompose into convex regions for all variables + // assert(disjunct_symbols.size()==1); + + // TODO: unclear what this loop should be doing + symbol_exprt var; + for(const auto &ds : disjunct_symbols) + { + var=ds; + } + + exprt::operandst split_disjuncts; + + if((var.type().id()==ID_signedbv) || (var.type().id()==ID_unsignedbv)) + { + exprt smallest; + if(var.type().id()==ID_signedbv) + smallest=to_signedbv_type(var.type()).smallest_expr(); + if(var.type().id()==ID_unsignedbv) + smallest=to_unsignedbv_type(var.type()).smallest_expr(); + + split_disjuncts.push_back( + and_exprt( + d, + binary_relation_exprt( + var, + ID_ge, + plus_exprt(smallest, from_integer(mp_integer(1), var.type()))))); + + split_disjuncts.push_back( + and_exprt(d, binary_relation_exprt(var, ID_equal, smallest))); + } + else + { + split_disjuncts.push_back(d); + } + + for(auto &sd : split_disjuncts) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + exprt cc=simplify_expr((and_exprt(side_conditions, sd)), SSA.ns); + response=response && (analyzer(solver, SSA, cc, template_generator)); + + exprt res; + analyzer.get_result(res, vars); + result.push_back(res); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } + } + } + else + { + // for the complete disjunctive_conditions at once + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + exprt cc=simplify_expr( + and_exprt(side_conditions, simple_disjunctive_conditions), SSA.ns); + + response=analyzer(solver, SSA, cc, template_generator); + + exprt res; + analyzer.get_result(res, vars); + result.push_back(res); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } + + result_expr=disjunction(result); + return response; +} + diff --git a/src/domains/disjunctive_analyzer.h b/src/domains/disjunctive_analyzer.h new file mode 100644 index 000000000..1b021bbb3 --- /dev/null +++ b/src/domains/disjunctive_analyzer.h @@ -0,0 +1,50 @@ +/*******************************************************************\ + +Module: Data Flow Analysis + +Author: Peter Schrammel, Kumar Madhukar + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_DISJUNCTIVE_ANALYZER_H +#define CPROVER_2LS_DOMAINS_DISJUNCTIVE_ANALYZER_H + +class disjunctive_analyzert:public messaget +{ + public: + disjunctive_analyzert(): + solver_instances(0), + solver_calls(0) + { + } + + ~disjunctive_analyzert() + { + } + + void eliminate_implication(exprt &formula); + void push_negation_to_atoms(exprt &formula); + void convert_to_dnf(exprt &formula); + void convert_to_dnf_rec(exprt &formula); + + bool operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars); + + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + +protected: + // statistics + unsigned solver_instances; + unsigned solver_calls; +}; + + +#endif + diff --git a/src/domains/domain.cpp b/src/domains/domain.cpp index bda121290..a693a8180 100644 --- a/src/domains/domain.cpp +++ b/src/domains/domain.cpp @@ -1,36 +1,67 @@ +/*******************************************************************\ + +Module: Abstract domain base class + +Author: Peter Schrammel + +\*******************************************************************/ + #include "domain.h" +/*******************************************************************\ + +Function: domaint::merge_kinds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ domaint::kindt domaint::merge_kinds(kindt k1, kindt k2) { - return (k1==OUT || - k2==OUT ? (k1==LOOP || - k2==LOOP ? OUTL : OUT) : - (k1==LOOP || k2==LOOP ? LOOP : IN)); + return + (k1==OUT || k2==OUT ? (k1==LOOP || k2==LOOP ? OUTL : OUT) : + (k1==LOOP || k2==LOOP ? LOOP : IN)); } -void domaint::output_var_specs(std::ostream &out, const var_specst &var_specs, - const namespacet &ns) -{ - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) +/*******************************************************************\ + +Function: domaint::output_var_specs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void domaint::output_var_specs( + std::ostream &out, + const var_specst &var_specs, + const namespacet &ns) +{ + for(const auto &v : var_specs) + { + switch(v.kind) { - switch(v->kind) - { - case LOOP: - out << "(LOOP) [ " << from_expr(ns,"",v->pre_guard) << " | "; - out << from_expr(ns,"",v->post_guard) << " ]: "; - break; - case IN: - out << "(IN) "; - out << from_expr(ns,"",v->pre_guard) << ": "; - break; - case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",v->post_guard) << ": "; - break; - default: assert(false); - } - out << from_expr(ns,"",v->var) << std::endl; + case LOOP: + out << "(LOOP) [ " << from_expr(ns, "", v.pre_guard) << " | "; + out << from_expr(ns, "", v.post_guard) << " ]: "; + break; + case IN: + out << "(IN) "; + out << from_expr(ns, "", v.pre_guard) << ": "; + break; + case OUT: case OUTL: + out << "(OUT) "; + out << from_expr(ns, "", v.post_guard) << ": "; + break; + default: assert(false); } + out << from_expr(ns, "", v.var) << std::endl; + } } diff --git a/src/domains/domain.h b/src/domains/domain.h index abb8b1931..59487f28a 100644 --- a/src/domains/domain.h +++ b/src/domains/domain.h @@ -1,5 +1,13 @@ -#ifndef CPROVER_DOMAIN_H -#define CPROVER_DOMAIN_H +/*******************************************************************\ + +Module: Abstract domain base class + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_DOMAIN_H +#define CPROVER_2LS_DOMAINS_DOMAIN_H #include #include @@ -13,14 +21,19 @@ class domaint { public: - domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domain_number(_domain_number), + domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domain_number(_domain_number), renaming_map(_renaming_map), ns(_ns) - {} - virtual ~domaint() {} + { + } + + virtual ~domaint() + { + } typedef exprt vart; typedef std::vector var_listt; @@ -28,75 +41,98 @@ class domaint typedef enum {LOOP, IN, OUT, OUTL} kindt; - typedef exprt guardt; + typedef exprt guardt; - typedef struct { + typedef struct + { guardt pre_guard; guardt post_guard; vart var; - exprt aux_expr; //some auxiliary per-variable constraint + exprt aux_expr; // some auxiliary per-variable constraint kindt kind; } var_spect; - typedef std::vector var_specst; + typedef std::vector var_specst; - class valuet { - public: - typedef enum{TOP,BOTTOM,OTHER} basic_valuet; - valuet() : basic_value(OTHER) {} + class valuet + { + public: + typedef enum {TOP, BOTTOM, OTHER} basic_valuet; + valuet():basic_value(OTHER) {} virtual ~valuet() {} - basic_valuet basic_value; + basic_valuet basic_value; }; - virtual void initialize(valuet &value) { value.basic_value = valuet::BOTTOM; } + virtual void initialize(valuet &value) { value.basic_value=valuet::BOTTOM; } - //returns true as long as further refinements are possible + // returns true as long as further refinements are possible virtual void reset_refinements() { } virtual bool refine() { return false; } - virtual void join(valuet &value1, const valuet &value2) - { + virtual void join(valuet &value1, const valuet &value2) + { + bool other_bottom= + value1.basic_value==valuet::OTHER && + value2.basic_value==valuet::BOTTOM; if(value1.basic_value==value2.basic_value || value1.basic_value==valuet::TOP || - (value1.basic_value==valuet::OTHER && - value2.basic_value==valuet::BOTTOM)) return; - value1.basic_value = value2.basic_value; + other_bottom) + return; + value1.basic_value=value2.basic_value; } - virtual void output_value(std::ostream &out, const valuet &value, - const namespacet &ns) const { assert(false); } - virtual void output_domain(std::ostream &out, - const namespacet &ns) const { assert(false); } + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const + { + assert(false); + } + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const + { + assert(false); + } + + // (not useful to make value const (e.g. union-find)) + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) + { + if(value.basic_value==valuet::BOTTOM) + result=false_exprt(); + else + result=true_exprt(); + } - virtual void project_on_vars(valuet &value, const var_sett &vars, - exprt &result) - //(not useful to make value const (e.g. union-find)) - { - if(value.basic_value==valuet::BOTTOM) result = false_exprt(); - else result = true_exprt(); - } + virtual bool is_spec_empty() const { assert(false); } static kindt merge_kinds(kindt k1, kindt k2); - static void output_var_specs(std::ostream &out, const var_specst &var_specs, - const namespacet &ns); + static void output_var_specs( + std::ostream &out, + const var_specst &var_specs, + const namespacet &ns); - protected: - unsigned domain_number; //serves as id for variables names +protected: + unsigned domain_number; // serves as id for variables names replace_mapt &renaming_map; const namespacet &ns; - - inline void rename(exprt &expr) - { - replace_expr(renaming_map, expr); + + inline void rename(exprt &expr) + { + replace_expr(renaming_map, expr); } + inline void rename(exprt::operandst &operands) { for(unsigned i=0; i + +class domain_refinementt +{ +public: + explicit domain_refinementt( + const local_SSAt &_SSA, + incremental_solvert &_solver) + : + SSA(_SSA), + solver(_solver) + {} + + // refine, returns true if there are no more refinements + virtual bool operator()() { return true; } + + // template generators associated with this SSA and solver + typedef std::map template_generatorst; + template_generatorst template_generators; + +protected: + const local_SSAt &SSA; + incremental_solvert &solver; +}; + +#endif // CPROVER_2LS_DOMAINS_DOMAIN_REFINEMENT_H diff --git a/src/domains/domain_refinement_variables.cpp b/src/domains/domain_refinement_variables.cpp new file mode 100644 index 000000000..cb906d8d1 --- /dev/null +++ b/src/domains/domain_refinement_variables.cpp @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Domain Refinement: Choice of Variables + +Author: Peter Schrammel + +\*******************************************************************/ + +bool domain_refinementt::operator()() +{ + // TODO: analyze counterexample + for(template_generatorst::iterator it=template_generators.begin(); + it!=template_generators.end(); ++it) + { + // TODO: select refinement to be performed + + // TODO: update template and domain + // it->add_template(); + } +} diff --git a/src/domains/equality_domain.cpp b/src/domains/equality_domain.cpp index d127d0c69..8d77115b3 100644 --- a/src/domains/equality_domain.cpp +++ b/src/domains/equality_domain.cpp @@ -1,13 +1,23 @@ -#include "equality_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Equalities/Disequalities domain + +Author: Peter Schrammel +\*******************************************************************/ + +#ifdef DEBUG #include +#include +#endif #include #include #include #include -#include + +#include "equality_domain.h" +#include "util.h" /*******************************************************************\ @@ -24,10 +34,11 @@ Function: equality_domaint::initialize void equality_domaint::initialize(valuet &value) { #if 0 - if(templ.size()==0) return domaint::initialize(value); + if(templ.size()==0) + return domaint::initialize(value); #endif - equ_valuet &v = static_cast(value); + equ_valuet &v=static_cast(value); v.equs.clear(); v.disequs.clear(); } @@ -47,51 +58,103 @@ Function: equality_domaint::get_pre_equ_constraint exprt equality_domaint::get_pre_equ_constraint(unsigned index) { assert(index(value); + equ_valuet &v=static_cast(value); exprt::operandst c; - for(unsigned index = 0; index(value); - equ_valuet v = _v; + const equ_valuet &_v=static_cast(value); + equ_valuet v=_v; - for(unsigned index = 0; index " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " - << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) << "===> " + << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " - << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << "===> " + << std::endl << " "; break; default: assert(false); } - const var_pairt &vv = templ_row.var_pair; - out << from_expr(ns,"",vv.first) << " =!= " - << from_expr(ns,"",vv.second) << std::endl; + const var_pairt &vv=templ_row.var_pair; + out << from_expr(ns, "", vv.first) << "=!= " + << from_expr(ns, "", vv.second) << std::endl; } } /*******************************************************************\ -Function: equality_domaint::make_template +Function: equality_domaint::adapt_types Inputs: @@ -305,119 +378,140 @@ Function: equality_domaint::make_template \*******************************************************************/ -bool adapt_types(exprt &v1, exprt &v2) +bool equality_domaint::adapt_types(exprt &v1, exprt &v2) { - //signed, unsigned integers + // signed, unsigned integers if((v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv) && - (v2.type().id()==ID_signedbv || v2.type().id()==ID_unsignedbv)) + (v2.type().id()==ID_signedbv || v2.type().id()==ID_unsignedbv)) { - unsigned size1 = 0, size2 = 0; - if(v1.type().id()==ID_signedbv) - size1 = to_signedbv_type(v1.type()).get_width(); - if(v1.type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(v1.type()).get_width(); - if(v2.type().id()==ID_signedbv) - size2 = to_signedbv_type(v2.type()).get_width(); - if(v2.type().id()==ID_unsignedbv) - size2 = to_unsignedbv_type(v2.type()).get_width(); + unsigned size1=0, size2=0; + if(v1.type().id()==ID_signedbv) + size1=to_signedbv_type(v1.type()).get_width(); + if(v1.type().id()==ID_unsignedbv) + size1=to_unsignedbv_type(v1.type()).get_width(); + if(v2.type().id()==ID_signedbv) + size2=to_signedbv_type(v2.type()).get_width(); + if(v2.type().id()==ID_unsignedbv) + size2=to_unsignedbv_type(v2.type()).get_width(); if(v1.type().id()==v2.type().id()) - { - if(size1==size2) return true; - - typet new_type = v1.type(); - if(new_type.id()==ID_signedbv) - to_signedbv_type(new_type).set_width(std::max(size1,size2)); - else //if(new_type.id()==ID_unsignedbv) - to_unsignedbv_type(new_type).set_width(std::max(size1,size2)); - - if(size1>size2) v2 = typecast_exprt(v2,new_type); - else v1 = typecast_exprt(v1,new_type); - return true; - } - - //types are different - typet new_type = signedbv_typet(std::max(size1,size2)+1); - v1 = typecast_exprt(v1,new_type); - v2 = typecast_exprt(v2,new_type); + { + if(size1==size2) + return true; + + typet new_type=v1.type(); + if(new_type.id()==ID_signedbv) + to_signedbv_type(new_type).set_width(std::max(size1, size2)); + else // if(new_type.id()==ID_unsignedbv) + to_unsignedbv_type(new_type).set_width(std::max(size1, size2)); + + if(size1>size2) + v2=typecast_exprt(v2, new_type); + else + v1=typecast_exprt(v1, new_type); + return true; + } + + // types are different + typet new_type=signedbv_typet(std::max(size1, size2)+1); + v1=typecast_exprt(v1, new_type); + v2=typecast_exprt(v2, new_type); return true; } - //pointer equality - if(v1.type().id()==ID_pointer && v2.type().id()==ID_pointer) + // pointer equality + if(v1.type().id()==ID_pointer && v2.type().id()==ID_pointer) { - if(to_pointer_type(v1.type()).subtype() == + if(to_pointer_type(v1.type()).subtype()== to_pointer_type(v2.type()).subtype()) return true; return false; } - if(v1.id()==ID_index || v2.id()==ID_index) + if(v1.id()==ID_index || v2.id()==ID_index) { #if 0 std::cout << "v1: " << v1 << std::endl; std::cout << "v2: " << v2 << std::endl; #endif - //TODO: implement - return false; - } - - return false; //types incompatible + // TODO: implement + return false; + } + + return false; // types incompatible } +/*******************************************************************\ + +Function: equality_domaint::make_template + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void equality_domaint::make_template( const var_specst &var_specs, const namespacet &ns) -{ - unsigned size = var_specs.size(); //just an estimate +{ + unsigned size=var_specs.size(); // just an estimate templ.clear(); - templ.reserve(size); + templ.reserve(size); - for(var_specst::const_iterator v1 = var_specs.begin(); + for(var_specst::const_iterator v1=var_specs.begin(); v1!=var_specs.end(); v1++) { - // NULL pointer checks + // NULL pointer checks if(v1->var.type().id()==ID_pointer) { templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.var_pair = var_pairt(v1->var, - null_pointer_exprt(to_pointer_type(v1->var.type()))); - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.aux_expr = v1->aux_expr; - templ_row.kind = v1->kind; + template_rowt &templ_row=templ.back(); + templ_row.var_pair= + var_pairt(v1->var, null_pointer_exprt(to_pointer_type(v1->var.type()))); + templ_row.pre_guard=v1->pre_guard; + templ_row.post_guard=v1->post_guard; + templ_row.aux_expr=v1->aux_expr; + templ_row.kind=v1->kind; } - var_specst::const_iterator v2 = v1; v2++; + var_specst::const_iterator v2=v1; v2++; for(; v2!=var_specs.end(); v2++) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - //if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + +#if 0 + // TODO: must be done in caller (for preconditions, e.g.) + if(k==IN) + continue; +#endif exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - exprt vv1 = v1->var; - exprt vv2 = v2->var; - if(!adapt_types(vv1,vv2)) continue; + exprt vv1=v1->var; + exprt vv2=v2->var; + if(!adapt_types(vv1, vv2)) + continue; templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.var_pair = var_pairt(vv1,vv2); - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.aux_expr = aux_expr; - templ_row.kind = k; + template_rowt &templ_row=templ.back(); + templ_row.var_pair=var_pairt(vv1, vv2); + templ_row.pre_guard=pre_g; + templ_row.post_guard=post_g; + templ_row.aux_expr=aux_expr; + templ_row.kind=k; } } } /*******************************************************************\ -Function: equality_domaint::get_var_pairs +Function: equality_domaint::get_index_set Inputs: @@ -427,7 +521,8 @@ Function: equality_domaint::get_var_pairs \*******************************************************************/ -void equality_domaint::get_index_set(std::set &indices) +void equality_domaint::get_index_set(std::set &indices) { - for(unsigned i=0;i #include #include -#include - #include "domain.h" -class equality_domaint : public domaint +class equality_domaint:public domaint { - public: - typedef std::pair var_pairt; +public: + typedef std::pair var_pairt; typedef std::set var_pairst; typedef std::set index_sett; - equality_domaint(unsigned _domain_number, replace_mapt &_renaming_map, - const var_specst &var_specs, - const namespacet &ns) - : domaint(_domain_number,_renaming_map, ns) +equality_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &ns): + domaint(_domain_number, _renaming_map, ns) { - make_template(var_specs,ns); + make_template(var_specs, ns); } - class equ_valuet : public valuet + class equ_valuet:public valuet { - public: - + public: union_find equs; index_sett disequs; }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -52,21 +61,29 @@ class equality_domaint : public domaint void set_equal(unsigned index, equ_valuet &value); void set_disequal(unsigned index, equ_valuet &value); - virtual void output_value(std::ostream &out, const valuet &value, + virtual void output_value( + std::ostream &out, + const valuet &value, const namespacet &ns) const; + virtual void output_domain(std::ostream &out, const namespacet &ns) const; - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); - void get_index_set(index_sett &indices); + void get_index_set(index_sett &indices); const var_pairt &get_var_pair(unsigned index); - protected: +protected: templatet templ; void make_template( const var_specst &var_specs, const namespacet &ns); + + bool adapt_types(exprt &v1, exprt &v2); }; -#endif +#endif // CPROVER_2LS_DOMAINS_EQUALITY_DOMAIN_H diff --git a/src/domains/fixed_point.cpp b/src/domains/fixed_point.cpp deleted file mode 100644 index 6ddb75f60..000000000 --- a/src/domains/fixed_point.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/*******************************************************************\ - -Module: Forward Least Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: fixed_pointt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::operator()() -{ - iteration_number=0; - - // Set up the state predicate, starting with 'false' - // (the empty set). - - state_predicate.state_vars=pre_state_vars; - state_predicate.make_false(); - - bool change; - - do - { - iteration_number++; - - #ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; - #endif - - change=iteration(); - } - while(change); - - #ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - output(std::cout); - #endif -} - -/*******************************************************************\ - -Function: fixed_pointt::iteration - - Inputs: - - Outputs: 'true' if there is a change in the state predicate - - Purpose: - -\*******************************************************************/ - -bool fixed_pointt::iteration() -{ - #if 0 - solvert solver(ns); - - // Feed transition relation into solver. - for(constraintst::const_iterator - it=transition_relation.begin(); - it!=transition_relation.end(); - it++) - solver << *it; - - // Feed current state predicate into solver. - state_predicate.set_to_true(solver); - - #ifdef DEBUG - std::cout << "Entry state:\n"; - output(std::cout); - #endif - - // solve - solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - // now get new post-state - predicatet post_state; - post_state.state_vars=post_state_vars; - - post_state.get(solver); - - #ifdef DEBUG - std::cout << "Post state:\n"; - post_state.output(std::cout); - #endif - - // Now 'OR' with previous state predicate. - // First rename post-state to pre-state. - post_state.rename(pre_state_vars); - - // Form disjunction of previous state predicate and the new one. - return state_predicate.disjunction(post_state); - #endif - - return false; -} - -/*******************************************************************\ - -Function: fixed_pointt::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::output(std::ostream &out) const -{ - state_predicate.output(out); -} diff --git a/src/domains/fixed_point.h b/src/domains/fixed_point.h deleted file mode 100644 index bdc4ec029..000000000 --- a/src/domains/fixed_point.h +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************************************\ - -Module: Forward Greatest Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_FIXED_POINT_H -#define DELTACHECK_FIXED_POINT_H - -#include "predicate.h" - -class fixed_pointt -{ -public: - explicit fixed_pointt(const namespacet &_ns):ns(_ns) - { - } - - typedef std::list constraintst; - constraintst transition_relation; - - predicatet::state_var_listt pre_state_vars, post_state_vars; - - // this is over pre_state_vars - predicatet state_predicate; - - void output(std::ostream &) const; - - unsigned iteration_number; - - void operator()(); - -protected: - const namespacet &ns; - - // fixed-point iteration - void initialize(); - bool iteration(); -}; - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, - const std::list &src) -{ - for(std::list::const_iterator - c_it=src.begin(); c_it!=src.end(); c_it++) - dest << *c_it; - return dest; -} - -#endif diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index 9001b2e41..750073850 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -1,13 +1,35 @@ +/*******************************************************************\ + +Module: Incremental Solver Interface + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include -#include #include #include #include "incremental_solver.h" -void incremental_solvert::new_context() +/*******************************************************************\ + +Function: incremental_solvert::new_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::new_context() { #ifdef NON_INCREMENTAL contexts.push_back(constraintst()); @@ -18,9 +40,11 @@ void incremental_solvert::new_context() #else - literalt activation_literal = solver->convert( - symbol_exprt("goto_symex::\\act$"+ - i2string(activation_literal_counter++), bool_typet())); + literalt activation_literal= + solver->convert( + symbol_exprt( + "goto_symex::\\act$"+ + i2string(activation_literal_counter++), bool_typet())); #ifdef DEBUG_OUTPUT debug() << "new context: " << activation_literal<< eom; @@ -30,12 +54,24 @@ void incremental_solvert::new_context() solver->set_assumptions(activation_literals); #if 0 - return !activation_literals.back(); //not to be used anymore + return !activation_literals.back(); // not to be used anymore #endif #endif } -void incremental_solvert::pop_context() +/*******************************************************************\ + +Function: incremental_solvert::pop_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::pop_context() { #ifdef NON_INCREMENTAL assert(!contexts.empty()); @@ -49,7 +85,7 @@ void incremental_solvert::pop_context() #else assert(!activation_literals.empty()); - literalt activation_literal = activation_literals.back(); + literalt activation_literal=activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA solver->set_to_false(literal_exprt(activation_literal)); @@ -65,16 +101,28 @@ void incremental_solvert::pop_context() #endif } -void incremental_solvert::make_context_permanent() +/*******************************************************************\ + +Function: incremental_solvert::make_context_permanent + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::make_context_permanent() { #ifdef NON_INCREMENTAL assert(contexts.size()>=2); - contextst::iterator c_it = contexts.end(); c_it--; c_it--; - c_it->insert(c_it->end(),contexts.back().begin(),contexts.back().end()); + contextst::iterator c_it=contexts.end(); c_it--; c_it--; + c_it->insert(c_it->end(), contexts.back().begin(), contexts.back().end()); contexts.pop_back(); #else assert(!activation_literals.empty()); - literalt activation_literal = activation_literals.back(); + literalt activation_literal=activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA solver->set_to_true(literal_exprt(activation_literal)); @@ -90,32 +138,60 @@ void incremental_solvert::make_context_permanent() #endif } -void incremental_solvert::debug_add_to_formula(const exprt &expr) +/*******************************************************************\ + +Function: incremental_solvert::debug_add_to_formula + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::debug_add_to_formula( + const exprt &_expr, exprt activation) { + if(_expr.id()!=ID_and) + { #ifdef NON_INCREMENTAL - // no debug mode for non-incremental yet + // no debug mode for non-incremental yet + assert(0); #else - literalt l = solver->convert(expr); - if(l.is_false()) - { + exprt expr; + if(activation.is_nil()) + expr=_expr; + else + expr=or_exprt(_expr, activation); + literalt l=solver->convert(expr); + if(l.is_false()) + { #ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": false = " << from_expr(ns,"",expr) <convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); + literalt dummy= + solver->convert(symbol_exprt("goto_symex::\\dummy", bool_typet())); formula.push_back(dummy); formula.push_back(!dummy); #ifdef DEBUG_OUTPUT - debug() << "literal " << dummy << ", " << !dummy << ": " - << from_expr(ns,"",expr) << eom; + debug() << "literal " << dummy << ", " << !dummy << ": " + << from_expr(ns, "", expr) << eom; #endif - } - else if(!l.is_true()) - { + } + else if(!l.is_true()) + { #ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": " << from_expr(ns,"",expr) << eom; + debug() << "literal " << l << ": " << from_expr(ns, "", expr) << eom; #endif - formula.push_back(l); - } + formula.push_back(l); + formula_expr.push_back(expr); + } #endif + } + else + { + forall_operands(it, _expr) + debug_add_to_formula(*it, activation); + } } diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index a4c668d11..4c7acc3b1 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -6,8 +6,8 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_INCREMENTAL_SOLVER_H -#define CPROVER_INCREMENTAL_SOLVER_H +#ifndef CPROVER_2LS_DOMAINS_INCREMENTAL_SOLVER_H +#define CPROVER_2LS_DOMAINS_INCREMENTAL_SOLVER_H #include #include @@ -15,41 +15,41 @@ Author: Peter Schrammel #include #include #include +#include #include "domain.h" #include "util.h" -//#define DISPLAY_FORMULA -//#define NO_ARITH_REFINEMENT -//#define NON_INCREMENTAL // (experimental) +// #define NO_ARITH_REFINEMENT +// #define NON_INCREMENTAL // (experimental) -//#define DISPLAY_FORMULA -//#define DEBUG_FORMULA -//#define DEBUG_OUTPUT +// #define DISPLAY_FORMULA +// #define DEBUG_FORMULA +// #define DEBUG_OUTPUT -class incremental_solvert : public messaget +class incremental_solvert:public messaget { - public: typedef std::list constraintst; typedef std::list contextst; explicit incremental_solvert( - const namespacet &_ns, bool _arith_refinement=false) : + const namespacet &_ns, + bool _arith_refinement=false): sat_check(NULL), - solver(NULL), + solver(NULL), ns(_ns), activation_literal_counter(0), domain_number(0), arith_refinement(_arith_refinement), solver_calls(0) - { + { allocate_solvers(_arith_refinement); contexts.push_back(constraintst()); } virtual ~incremental_solvert() - { + { deallocate_solvers(); } @@ -69,32 +69,63 @@ class incremental_solvert : public messaget #ifdef NON_INCREMENTAL deallocate_solvers(); allocate_solvers(arith_refinement); - unsigned context_no = 0; - for(contextst::const_iterator c_it = contexts.begin(); - c_it != contexts.end(); c_it++, context_no++) + unsigned context_no=0; + for(const auto &context : contexts) { #ifdef DISPLAY_FORMULA std::cerr << "context: " << context_no << std::endl; #endif - for(incremental_solvert::constraintst::const_iterator it = c_it->begin(); - it != c_it->end(); it++) + for(const auto &constraint : context) { #ifdef DISPLAY_FORMULA - std::cerr << "actual add_to_solver: " << from_expr(ns,"",*it) << std::endl; + std::cerr << "actual add_to_solver: " + << from_expr(ns, "", constraint) << std::endl; #endif - *solver << *it; + *solver << constraint; } } #else #ifdef DEBUG_FORMULA - bvt whole_formula = formula; - whole_formula.insert(whole_formula.end(),activation_literals.begin(), - activation_literals.end()); + bvt whole_formula=formula; + whole_formula.insert( + whole_formula.end(), + activation_literals.begin(), + activation_literals.end()); solver->set_assumptions(whole_formula); #endif #endif - - return (*solver)(); +#if defined(DEBUG_FORMULA) || defined(DISPLAY_FORMULA) + decision_proceduret::resultt result=(*solver)(); +#endif +#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) + if(result==decision_proceduret::D_UNSATISFIABLE) + { + for(unsigned i=0; iis_in_conflict(formula[i])) + std::cout << "is_in_conflict: " + << from_expr(ns, "", formula_expr[i]) << std::endl; + } + } +#endif +#if (defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT)) || defined(DISPLAY_FORMULA) // NOLINT(whitespace/line_length) + if(result==decision_proceduret::D_SATISFIABLE) + { + std::set vars; + for(unsigned i=0; i::const_iterator it=vars.begin(); + it!=vars.end(); ++it) + { + std::cout << "assignment: " << from_expr(ns, "", *it) << "=" + << from_expr(ns, "", solver->get(*it)) << std::endl; + } + } + return result; +#endif +#if !defined(DEBUG_FORMULA) && !defined(DISPLAY_FORMULA) + return (*solver)(); +#endif } exprt get(const exprt& expr) { return solver->get(expr); } @@ -105,72 +136,92 @@ class incremental_solvert : public messaget unsigned next_domain_number() { return domain_number++; } - static incremental_solvert *allocate(const namespacet &_ns, - bool arith_refinement=false) - { - return new incremental_solvert(_ns,arith_refinement); + static incremental_solvert *allocate( + const namespacet &_ns, + bool arith_refinement=false) + { + return new incremental_solvert(_ns, arith_refinement); } inline prop_convt & get_solver() { return *solver; } - propt* sat_check; - prop_convt* solver; + propt *sat_check; + prop_convt *solver; const namespacet &ns; void new_context(); void pop_context(); void make_context_permanent(); - //for debugging + // for debugging bvt formula; - void debug_add_to_formula(const exprt &expr); + exprt::operandst formula_expr; + void debug_add_to_formula(const exprt &expr, exprt activation=nil_exprt()); - //context assumption literals + // context assumption literals bvt activation_literals; - //non-incremental solving + // non-incremental solving contextst contexts; protected: unsigned activation_literal_counter; - unsigned domain_number; //ids for each domain instance to make symbols unique + unsigned domain_number; // ids for each domain instance to make symbols unique bool arith_refinement; - //statistics + // statistics unsigned solver_calls; void allocate_solvers(bool arith_refinement) { - sat_check = new satcheckt(); + sat_check=new satcheckt(); #if 0 - sat_check = new satcheck_minisat_no_simplifiert(); + sat_check=new satcheck_minisat_no_simplifiert(); #endif #ifdef NON_INCREMENTAL - solver = new bv_pointerst(ns,*sat_check); + solver=new bv_pointerst(ns, *sat_check); #else - solver = new bv_refinementt(ns,*sat_check); + solver=new bv_refinementt(ns, *sat_check); solver->set_all_frozen(); - ((bv_refinementt *)solver)->do_array_refinement = false; - ((bv_refinementt *)solver)->do_arithmetic_refinement = arith_refinement; + static_cast(solver)->do_array_refinement=false; + static_cast(solver)->do_arithmetic_refinement= + arith_refinement; #endif } void deallocate_solvers() { - if(solver!=NULL) delete solver; - if(sat_check!=NULL) delete sat_check; + if(solver!=NULL) + delete solver; + if(sat_check!=NULL) + delete sat_check; } }; -static inline incremental_solvert & operator << ( +/*******************************************************************\ + +Function: incremental_solvert::operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static inline incremental_solvert &operator<<( incremental_solvert &dest, const exprt &src) { #ifdef DISPLAY_FORMULA if(!dest.activation_literals.empty()) - std::cerr << "add_to_solver(" << !dest.activation_literals.back() << "): " - << from_expr(dest.ns,"",src) << std::endl; else - std::cerr << "add_to_solver: " << from_expr(dest.ns,"",src) << std::endl; + std::cerr << "add_to_solver(" << !dest.activation_literals.back() << "): " + << from_expr(dest.ns, "", src) << std::endl; + else + std::cerr << "add_to_solver: " + << from_expr(dest.ns, "", src) << std::endl; + dest.formula_expr.push_back(src); #endif #ifdef NON_INCREMENTAL @@ -178,33 +229,46 @@ static inline incremental_solvert & operator << ( #else #ifndef DEBUG_FORMULA if(!dest.activation_literals.empty()) - *dest.solver << or_exprt(src, - literal_exprt(!dest.activation_literals.back())); - else + *dest.solver << + or_exprt(src, literal_exprt(!dest.activation_literals.back())); + else *dest.solver << src; #else if(!dest.activation_literals.empty()) dest.debug_add_to_formula( - or_exprt(src,literal_exprt(!dest.activation_literals.back()))); - else + or_exprt( + src, + literal_exprt(!dest.activation_literals.back()))); + else dest.debug_add_to_formula(src); #endif #endif return dest; } -static inline incremental_solvert& operator << ( +/*******************************************************************\ + +Function: incremental_solvert::operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static inline incremental_solvert &operator<<( incremental_solvert &dest, const incremental_solvert::constraintst &src) { #ifdef NON_INCREMENTAL - dest.contexts.back().insert(dest.contexts.back().begin() - ,src.begin(),src.end()); + dest.contexts.back().insert( + dest.contexts.back().begin(), src.begin(), src.end()); #else - for(incremental_solvert::constraintst::const_iterator it = src.begin(); - it != src.end(); it++) + for(const auto &constraint : src) { - dest << *it; + dest << constraint; } #endif return dest; diff --git a/src/domains/lexlinrank_domain.cpp b/src/domains/lexlinrank_domain.cpp index 89bd7a8b8..0fc5aa5ea 100644 --- a/src/domains/lexlinrank_domain.cpp +++ b/src/domains/lexlinrank_domain.cpp @@ -1,7 +1,14 @@ -#include "lexlinrank_domain.h" -#include "util.h" +/*******************************************************************\ +Module: Lexicographic linear ranking function domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "lexlinrank_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define EXTEND_TYPES @@ -17,123 +27,192 @@ #define COEFF_C_SIZE 10 #define MAX_REFINEMENT 2 +/*******************************************************************\ + +Function: lexlinrank_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void lexlinrank_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row=MAX_REFINEMENT) return false; + if(refinement_level>=MAX_REFINEMENT) + return false; refinement_level++; return true; } +/*******************************************************************\ + +Function: lexlinrank_domaint::reset_refinements + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void lexlinrank_domaint::reset_refinements() { - refinement_level = 0; + refinement_level=0; } -exprt lexlinrank_domaint::get_not_constraints(const lexlinrank_domaint::templ_valuet &value, - exprt::operandst &cond_exprs, - std::vector &value_exprs) +/*******************************************************************\ + +Function: lexlinrank_domaint::get_not_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt lexlinrank_domaint::get_not_constraints( + const lexlinrank_domaint::templ_valuet &value, + exprt::operandst &cond_exprs, + std::vector &value_exprs) { cond_exprs.resize(value.size()); value_exprs.resize(value.size()); - for(unsigned row = 0; row true) - cond_exprs[row] = false_exprt(); + // !(g=> true) + cond_exprs[row]=false_exprt(); } else if(is_row_value_false(value[row])) { - // !(g => false) - cond_exprs[row] = - and_exprt(templ[row].pre_guard, templ[row].post_guard); + // !(g=> false) + cond_exprs[row]= + and_exprt(templ[row].pre_guard, templ[row].post_guard); } else { exprt::operandst elmts; elmts.reserve(value[row].size()); - for(unsigned elm=0; elm=1); exprt::operandst c; - c.reserve(1 + value[row].size() - (elm+1)); + c.reserve(1+value[row].size()-(elm+1)); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(value[row][elm].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum= + mult_exprt( + value[row][elm].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(value[row][elm].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(value[row][elm].c[0],templ[row].expr[0].second); + exprt sum_pre= + mult_exprt(value[row][elm].c[0], templ[row].expr[0].first); + exprt sum_post= + mult_exprt(value[row][elm].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < value[row][elm].c.size(); ++i) + for(std::size_t i=1; i=1); exprt::operandst c; - c.reserve(1 + symb_values.size() - (elm+1)); + c.reserve(1+symb_values.size()-(elm+1)); - symb_values[elm].c[0] = symbol_exprt(SYMB_COEFF_VAR+std::string("c!")+ - i2string(row)+"$"+i2string(elm)+"$0", - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + symb_values[elm].c[0]=symbol_exprt( + SYMB_COEFF_VAR+std::string("c!")+ + i2string(row)+"$"+i2string(elm)+"$0", + signedbv_typet(COEFF_C_SIZE)); // coefficients are signed integers #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(symb_values[elm].c[0], - minus_exprt(values[0].first, values[0].second)); + exprt sum=mult_exprt( + symb_values[elm].c[0], + minus_exprt(values[0].first, values[0].second)); #else - exprt sum_pre = mult_exprt(symb_values[elm].c[0],values[0].first); - exprt sum_post = mult_exprt(symb_values[elm].c[0],values[0].second); + exprt sum_pre=mult_exprt(symb_values[elm].c[0], values[0].first); + exprt sum_post=mult_exprt(symb_values[elm].c[0], values[0].second); #endif -// symb_values[elm].d = -// symbol_exprt(SYMB_BOUND_VAR+std::string("d!")+i2string(row)+std::string("$")+i2string(elm), -// signedbv_typet(COEFF_D_SIZE)); - - for(unsigned i = 1; i < values.size(); ++i) + for(std::size_t i=1; i(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl; + out << "(RANK) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " ]===> " << std::endl; break; default: assert(false); } - for(unsigned elm=0; elm0) out << " + "; - out << from_expr(ns,"",v[row][elm].c[i]) << " * " - << from_expr(ns,"",templ_row.expr[i].first); + if(i>0) + out << "+"; + out << from_expr(ns, "", v[row][elm].c[i]) << " * " + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } } -void lexlinrank_domaint::output_domain(std::ostream &out, const namespacet &ns) const +/*******************************************************************\ + +Function: lexlinrank_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void lexlinrank_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " + out << "(RANK) (" << from_expr(ns, "", templ_row.pre_guard) + << ") && (" << from_expr(ns, "", templ_row.post_guard) << ")===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; + if(i>0) + out << "+"; out << "c!" << row << "$" << i << " * " - << from_expr(ns,"",templ_row.expr[i].first); + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void lexlinrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) +/*******************************************************************\ + +Function: lexlinrank_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void lexlinrank_domaint::project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) { - //don't do any projection - const templ_valuet &v = static_cast(value); + // don't do any projection + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; // c is the conjunction of all rows c.reserve(templ.size()); - for(unsigned row = 0; row false) + // (g=> false) c.push_back(implies_exprt( and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); } else if(is_row_value_true(v[row])) { - //(g => true) + // (g=> true) c.push_back(implies_exprt( and_exprt(templ[row].pre_guard, templ[row].post_guard), true_exprt())); @@ -434,50 +626,63 @@ void lexlinrank_domaint::project_on_vars(valuet &value, const var_sett &vars, ex { exprt::operandst d; // d is the disjunction of lexicographic elements d.reserve(v[row].size()); - for(unsigned elm=0; elm=1); - exprt::operandst con; // con is the constraints for a single element of the lexicography - con.reserve(1 + v[row].size() - (elm+1)); + // con is the constraints for a single element of the lexicography + exprt::operandst con; + con.reserve(1+v[row].size()-(elm+1)); - exprt sum = mult_exprt(v[row][elm].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + v[row][elm].c[0], + minus_exprt( + templ[row].expr[0].first, + templ[row].expr[0].second)); - for(unsigned i = 1; i < v[row][elm].c.size(); ++i) + for(std::size_t i=1; ikind==LOOP) + if(v.kind==LOOP) { - has_loop = true; + has_loop=true; break; } } - if(!has_loop) return; + if(!has_loop) + return; templ.reserve(templ.size()+1); templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.kind = LOOP; + template_rowt &templ_row=templ.back(); + templ_row.kind=LOOP; exprt::operandst preg; exprt::operandst postg; - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - if(v->kind!=LOOP) continue; - preg.push_back(v->pre_guard); - postg.push_back(v->post_guard); - exprt vpost = v->var; //copy + if(v.kind!=LOOP) + continue; + preg.push_back(v.pre_guard); + postg.push_back(v.post_guard); + exprt vpost=v.var; // copy rename(vpost); - templ_row.expr.push_back(std::pair(v->var,vpost)); + templ_row.expr.push_back(std::pair(v.var, vpost)); } - templ_row.pre_guard = conjunction(preg); - templ_row.post_guard = conjunction(postg); + templ_row.pre_guard=conjunction(preg); + templ_row.post_guard=conjunction(postg); } /*******************************************************************\ @@ -556,14 +762,28 @@ Function: lexlinrank_domaint::is_row_value_false \*******************************************************************/ -bool lexlinrank_domaint::is_row_value_false(const row_valuet & row_value) const +bool lexlinrank_domaint::is_row_value_false( + const row_valuet & row_value) const { assert(row_value.size()>=1); assert(row_value[0].c.size()>=1); return row_value[0].c[0].get(ID_value)==ID_false; } -bool lexlinrank_domaint::is_row_element_value_false(const row_value_elementt & row_value_element) const +/*******************************************************************\ + +Function: lexlinrank_domaint::is_row_element_value_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool lexlinrank_domaint::is_row_element_value_false( + const row_value_elementt & row_value_element) const { assert(false); assert(row_value_element.c.size()>=1); @@ -589,7 +809,20 @@ bool lexlinrank_domaint::is_row_value_true(const row_valuet & row_value) const return row_value[0].c[0].get(ID_value)==ID_true; } -bool lexlinrank_domaint::is_row_element_value_true(const row_value_elementt & row_value_element) const +/*******************************************************************\ + +Function: lexlinrank_domaint::is_row_element_value_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool lexlinrank_domaint::is_row_element_value_true( + const row_value_elementt & row_value_element) const { assert(false); assert(row_value_element.c.size()>=1); @@ -608,12 +841,14 @@ Function: lexlinrank_domaint::add_element \*******************************************************************/ -void lexlinrank_domaint::add_element(const rowt &row, templ_valuet &value) -{ +void lexlinrank_domaint::add_element( + const rowt &row, + templ_valuet &value) +{ value[row].push_back(row_value_elementt()); for(unsigned i=0; i +#endif #include #include #include #include #include -#include -class lexlinrank_domaint : public domaint +#include "domain.h" + +class lexlinrank_domaint:public domaint { public: typedef unsigned rowt; - typedef std::vector > pre_post_valuest; + typedef std::vector > pre_post_valuest; typedef pre_post_valuest row_exprt; typedef struct { @@ -23,11 +34,11 @@ class lexlinrank_domaint : public domaint typedef std::vector row_valuet; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -37,14 +48,14 @@ class lexlinrank_domaint : public domaint typedef std::vector templatet; - - - lexlinrank_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number,_renaming_map, _ns), + lexlinrank_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns), refinement_level(0) - {} + { + } // initialize value virtual void initialize(valuet &value); @@ -53,34 +64,47 @@ class lexlinrank_domaint : public domaint virtual void reset_refinements(); // value -> constraints - exprt get_not_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs,// identical to before - std::vector &value_exprs); // (x, x') - exprt get_row_symb_constraint(row_valuet &symb_values, // contains vars c and d - const rowt &row, - const pre_post_valuest &values, - exprt &refinement_constraint - ); + exprt get_not_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, // identical to before + std::vector &value_exprs); // (x, x') + exprt get_row_symb_constraint( + row_valuet &symb_values, // contains vars c and d + const rowt &row, + const pre_post_valuest &values, + exprt &refinement_constraint); void add_element(const rowt &row, templ_valuet &value); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); void set_row_value_to_true(const rowt &row, templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; - - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const; + + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size() {return templ.size();} // generating templates - void add_template(const var_specst &var_specs, - const namespacet &ns); + void add_template( + const var_specst &var_specs, + const namespacet &ns); protected: templatet templ; @@ -88,11 +112,10 @@ class lexlinrank_domaint : public domaint bool is_row_value_false(const row_valuet & row_value) const; bool is_row_value_true(const row_valuet & row_value) const; - bool is_row_element_value_false(const row_value_elementt & row_value_element) const; - bool is_row_element_value_true(const row_value_elementt & row_value_element) const; - - + bool is_row_element_value_false( + const row_value_elementt & row_value_element) const; + bool is_row_element_value_true( + const row_value_elementt & row_value_element) const; }; - -#endif +#endif // CPROVER_2LS_DOMAINS_LEXLINRANK_DOMAIN_H diff --git a/src/domains/lexlinrank_solver_enumeration.cpp b/src/domains/lexlinrank_solver_enumeration.cpp index b01ec2340..68633968f 100644 --- a/src/domains/lexlinrank_solver_enumeration.cpp +++ b/src/domains/lexlinrank_solver_enumeration.cpp @@ -1,46 +1,76 @@ +/*******************************************************************\ + +Module: Enumeration-based solver for lexicographic linear ranking + functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include + #include "lexlinrank_solver_enumeration.h" #include "util.h" -//#define DEBUG_OUTER_FORMULA -//#define DEBUG_INNER_FORMULA +// #define DEBUG_OUTER_FORMULA +// #define DEBUG_INNER_FORMULA + +/*******************************************************************\ -bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) +Function: lexlinrank_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +lexlinrank_solver_enumerationt::progresst +lexlinrank_solver_enumerationt::iterate(invariantt &_rank) { - lexlinrank_domaint::templ_valuet &rank = + lexlinrank_domaint::templ_valuet &rank= static_cast(_rank); - bool improved = false; + progresst progress=CONVERGED; static std::vector number_elements_per_row; number_elements_per_row.resize(rank.size()); - debug() << "(RANK) no rows = " << rank.size() << eom; + debug() << "(RANK) no rows=" << rank.size() << eom; solver.new_context(); + // choose round to even rounding mode for template computations + // not clear what its implications on soundness and + // termination of the synthesis are + exprt rounding_mode= + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); - //choose round to even rounding mode for template computations - // not clear what its implications on soundness and termination of the synthesis are - exprt rounding_mode = symbol_exprt(CPROVER_PREFIX "rounding_mode",signedbv_typet(32)); - solver << equal_exprt(rounding_mode,from_integer(mp_integer(0),signedbv_typet(32))); - - //handles on values to retrieve from model + // handles on values to retrieve from model std::vector rank_value_exprs; exprt::operandst rank_cond_exprs; bvt rank_cond_literals; - exprt rank_expr = lexlinrank_domain.get_not_constraints(rank, - rank_cond_exprs, rank_value_exprs); + exprt rank_expr= + lexlinrank_domain.get_not_constraints( + rank, + rank_cond_exprs, + rank_value_exprs); solver << rank_expr; rank_cond_literals.resize(rank_cond_exprs.size()); - for(unsigned i = 0; i < rank_cond_exprs.size(); i++) - { - rank_cond_literals[i] = solver.solver->convert(rank_cond_exprs[i]); + for(std::size_t i=0; iconvert(rank_cond_exprs[i]); } debug() << "Outer solve(): "; @@ -48,193 +78,205 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) #if 0 // check whether the literal is UNSAT to start with satcheck_minisat_no_simplifiert test_satcheck; - bv_pointerst test_solver = bv_pointerst(ns, test_satcheck); + bv_pointerst test_solver=bv_pointerst(ns, test_satcheck); test_solver << rank_expr; - if(test_solver() == decision_proceduret::D_SATISFIABLE) + if(test_solver()==decision_proceduret::D_SATISFIABLE) debug() << "test solver: SAT" << eom; else debug() << "test solver: UNSAT" << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { debug() << "Outer solver: SAT" << eom; - for(unsigned row = 0; row < rank_cond_literals.size(); row++) + for(std::size_t row=0; rowl_get(rank_cond_literals[row]).is_true()) + + if(solver.solver->l_get(rank_cond_literals[row]).is_true()) { - for(lexlinrank_domaint::pre_post_valuest::iterator it = - rank_value_exprs[row].begin(); - it != rank_value_exprs[row].end(); ++it) - { - // model for x_i - exprt value = solver.solver->get(it->first); - debug() << "Row " << row << " Value for " - << from_expr(ns,"",it->first) - << ": " << from_expr(ns,"",value) << eom; - // model for x'_i - exprt post_value = solver.solver->get(it->second); - debug() << "Row " << row << " Value for " - << from_expr(ns,"",it->second) - << ": " << from_expr(ns,"",post_value) << eom; - // record all the values - values.push_back(std::make_pair(value, post_value)); - } - - lexlinrank_domaint::row_valuet symb_values; + for(auto &row_expr : rank_value_exprs[row]) + { + // model for x_i + exprt value=solver.solver->get(row_expr.first); + debug() << "Row " << row << " Value for " + << from_expr(ns, "", row_expr.first) + << ": " << from_expr(ns, "", value) << eom; + // model for x'_i + exprt post_value=solver.solver->get(row_expr.second); + debug() << "Row " << row << " Value for " + << from_expr(ns, "", row_expr.second) + << ": " << from_expr(ns, "", post_value) << eom; + // record all the values + values.push_back(std::make_pair(value, post_value)); + } + + lexlinrank_domaint::row_valuet symb_values; symb_values.resize(rank[row].size()); - //debug() << "elements: " << rank[row].size() << eom; + // debug() << "elements: " << rank[row].size() << eom; - exprt constraint; - exprt refinement_constraint; + exprt constraint; + exprt refinement_constraint; - // generate the new constraint - constraint = lexlinrank_domain.get_row_symb_constraint(symb_values, - row, values, refinement_constraint); + // generate the new constraint + constraint=lexlinrank_domain.get_row_symb_constraint( + symb_values, + row, values, + refinement_constraint); - simplify_expr(constraint, ns); - debug() << "Constraint sent to the inner solver: " << row - << " constraint "; - pretty_print_termination_argument(debug(), ns, constraint); - debug() << eom; + simplify_expr(constraint, ns); + debug() << "Constraint sent to the inner solver: " << row + << " constraint "; + pretty_print_termination_argument(debug(), ns, constraint); + debug() << eom; - *inner_solver << constraint; + *inner_solver << constraint; - //set rounding mode - *inner_solver << equal_exprt(rounding_mode, - from_integer(mp_integer(0),signedbv_typet(32))); + // set rounding mode + *inner_solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); - //refinement - if(!refinement_constraint.is_true()) - { - inner_solver->new_context(); - *inner_solver << refinement_constraint; - } + // refinement + if(!refinement_constraint.is_true()) + { + inner_solver->new_context(); + *inner_solver << refinement_constraint; + } - debug() << "Inner solve()" << eom; - // solve + debug() << "Inner solve()" << eom; + // solve solver_calls++; - bool inner_solver_result = (*inner_solver)(); - if(inner_solver_result == decision_proceduret::D_SATISFIABLE && - number_inner_iterations < max_inner_iterations) - { - number_inner_iterations++; - - debug() << "Inner solver: SAT and the max number of iterations was not reached " << eom; - debug() << "Inner solver: Current number of iterations = " << number_inner_iterations << eom; - debug() << "Inner solver: Current number of components for row " << row << " is " << number_elements_per_row[row]+1 << eom; - - // new_row_values will contain the new values for c and d - lexlinrank_domaint::row_valuet new_row_values; + bool inner_solver_result=(*inner_solver)(); + if(inner_solver_result==decision_proceduret::D_SATISFIABLE && + number_inner_iterations c = symb_values[constraint_no].c; - - // get the model for all c - for(std::vector::iterator it = c.begin(); - it != c.end(); ++it) - { - exprt v = inner_solver->solver->get(*it); - new_row_values[constraint_no].c.push_back(v); - debug() << "Inner Solver: row " << row - << " ==> c value for "; - pretty_print_termination_argument(debug(), ns, *it); - debug() << ": "; - pretty_print_termination_argument(debug(), ns, v); - debug() << eom; - } - } - - improved = true; - - // update the current template - lexlinrank_domain.set_row_value(row, new_row_values, rank); - - if(!refinement_constraint.is_true()) inner_solver->pop_context(); - } - else - { - if(inner_solver_result == decision_proceduret::D_UNSATISFIABLE) - debug() << "Inner solver: UNSAT" << eom; - else - debug() << "Inner solver: reached max number of iterations" << eom; - - debug() << "Inner solver: number of iterations = " << number_inner_iterations << eom; + for(std::size_t constraint_no=0; + constraint_no c=symb_values[constraint_no].c; + + // get the model for all c + for(auto &e : c) + { + exprt v=inner_solver->solver->get(e); + new_row_values[constraint_no].c.push_back(v); + debug() << "Inner Solver: row " << row + << "==> c value for "; + pretty_print_termination_argument(debug(), ns, e); + debug() << ": "; + pretty_print_termination_argument(debug(), ns, v); + debug() << eom; + } + } + + progress=CHANGED; + + // update the current template + lexlinrank_domain.set_row_value(row, new_row_values, rank); + + if(!refinement_constraint.is_true()) + inner_solver->pop_context(); + } + else + { + if(inner_solver_result==decision_proceduret::D_UNSATISFIABLE) + debug() << "Inner solver: UNSAT" << eom; + else + debug() << "Inner solver: reached max number of iterations" << eom; + + debug() << "Inner solver: number of iterations=" + << number_inner_iterations << eom; #ifdef DEBUG_INNER_FORMULA - for(unsigned i=0; iformula.size(); i++) - { - if(inner_solver->solver->is_in_conflict(inner_solver->formula[i])) - debug() << "is_in_conflict: " << inner_solver->formula[i] << eom; - else - debug() << "not_in_conflict: " << inner_solver->formula[i] << eom; - } -#endif - - if(lexlinrank_domain.refine()) - { - debug() << "refining..." << eom; - improved = true; //refinement possible - - if(!refinement_constraint.is_true()) inner_solver->pop_context(); - } - else - { - if(number_elements_per_row[row] == max_elements-1) - { - debug() << "Reached the max no of lexicographic components and no ranking function was found" << eom; - // no ranking function for the current template - lexlinrank_domain.set_row_value_to_true(row, rank); - lexlinrank_domain.reset_refinements(); - } - else - { - number_elements_per_row[row]++; - debug() << "Inner solver: increasing the number of lexicographic components for row " << row << " to " << number_elements_per_row[row] + 1 << eom; - // reset the inner solver - debug() << "Reset the inner solver " << eom; - delete inner_solver; - inner_solver = incremental_solvert::allocate(ns); + for(std::size_t i=0; iformula.size(); i++) + { + if(inner_solver->solver->is_in_conflict(inner_solver->formula[i])) + debug() << "is_in_conflict: " << inner_solver->formula[i] << eom; + else + debug() << "not_in_conflict: " << inner_solver->formula[i] << eom; + } +#endif + + if(lexlinrank_domain.refine()) + { + debug() << "refining..." << eom; + progress=CHANGED; // refinement possible + + if(!refinement_constraint.is_true()) + inner_solver->pop_context(); + } + else + { + if(number_elements_per_row[row]==max_elements-1) + { + debug() << "Reached the max no of lexicographic components " + << "and no ranking function was found" << eom; + // no ranking function for the current template + lexlinrank_domain.set_row_value_to_true(row, rank); + lexlinrank_domain.reset_refinements(); + } + else + { + number_elements_per_row[row]++; + debug() << "Inner solver: increasing the number " + << "of lexicographic components for row " << row + << " to " << number_elements_per_row[row]+1 << eom; + // reset the inner solver + debug() << "Reset the inner solver " << eom; + delete inner_solver; + inner_solver=incremental_solvert::allocate(ns); solver_instances++; - lexlinrank_domain.reset_refinements(); - - lexlinrank_domain.add_element(row, rank); - number_inner_iterations = 0; - debug() << "Inner solver: the number of inner iterations for row " << row << " was reset to " << number_inner_iterations << eom; - improved = true; - } - } - } + lexlinrank_domain.reset_refinements(); + + lexlinrank_domain.add_element(row, rank); + number_inner_iterations=0; + debug() << "Inner solver: " + << "the number of inner iterations for row " << row + << " was reset to " << number_inner_iterations << eom; + progress=CHANGED; + } + } + } } } - } - else + else { debug() << "Outer solver: UNSAT!!" << eom; lexlinrank_domain.reset_refinements(); + #ifdef DEBUG_OUTER_FORMULA - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; + debug() << "is_in_conflict: " << solver.formula[i] << eom; else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + debug() << "not_in_conflict: " << solver.formula[i] << eom; } -#endif - - +#endif } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/lexlinrank_solver_enumeration.h b/src/domains/lexlinrank_solver_enumeration.h index 6bd7c10a9..257be310f 100644 --- a/src/domains/lexlinrank_solver_enumeration.h +++ b/src/domains/lexlinrank_solver_enumeration.h @@ -1,5 +1,14 @@ -#ifndef CPROVER_LEXLINRANK_SOLVER_ENUMERATION_H -#define CPROVER_LEXLINRANK_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Enumeration-based solver for lexicographic linear ranking + functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_LEXLINRANK_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_LEXLINRANK_SOLVER_ENUMERATION_H #include "strategy_solver_base.h" #include "../domains/incremental_solver.h" @@ -8,27 +17,26 @@ #include -class lexlinrank_solver_enumerationt : public strategy_solver_baset +class lexlinrank_solver_enumerationt:public strategy_solver_baset { public: explicit lexlinrank_solver_enumerationt( lexlinrank_domaint &_lexlinrank_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, const namespacet &_ns, unsigned _max_elements, // lexicographic components - unsigned _max_inner_iterations - ) : - strategy_solver_baset(_solver, _ns), - lexlinrank_domain(_lexlinrank_domain), + unsigned _max_inner_iterations): + strategy_solver_baset(_solver, const_literal(true), _ns), + lexlinrank_domain(_lexlinrank_domain), max_elements(_max_elements), max_inner_iterations(_max_inner_iterations), number_inner_iterations(0) { - inner_solver = incremental_solvert::allocate(_ns); + inner_solver=incremental_solvert::allocate(_ns); solver_instances++; } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: lexlinrank_domaint &lexlinrank_domain; @@ -37,8 +45,7 @@ class lexlinrank_solver_enumerationt : public strategy_solver_baset // the "inner" solver const unsigned max_inner_iterations; incremental_solvert *inner_solver; - unsigned number_inner_iterations; - + unsigned number_inner_iterations; }; #endif diff --git a/src/domains/linrank_domain.cpp b/src/domains/linrank_domain.cpp index 043ca50ef..064ce3859 100644 --- a/src/domains/linrank_domain.cpp +++ b/src/domains/linrank_domain.cpp @@ -1,7 +1,14 @@ -#include "linrank_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Linear ranking function domain + +Author: Peter Schrammel +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "linrank_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define EXTEND_TYPES @@ -17,24 +27,61 @@ #define COEFF_C_SIZE 10 #define MAX_REFINEMENT 2 +/*******************************************************************\ + +Function: linrank_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void linrank_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row=MAX_REFINEMENT) return false; - refinement_level++; +{ + if(refinement_level>=MAX_REFINEMENT) + return false; + refinement_level++; return true; } +/*******************************************************************\ + +Function: linrank_domaint::get_not_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + exprt linrank_domaint::get_not_constraints( const linrank_domaint::templ_valuet &value, exprt::operandst &cond_exprs, @@ -44,21 +91,23 @@ exprt linrank_domaint::get_not_constraints( cond_exprs.resize(value.size()); value_exprs.resize(value.size()); - for(unsigned row = 0; row true) - cond_exprs[row] = false_exprt(); + // !(g=> true) + cond_exprs[row]=false_exprt(); } else if(is_row_value_false(value[row])) { - // !(g => false) - cond_exprs[row] = - and_exprt(templ[row].pre_guard, templ[row].post_guard); + // !(g=> false) + cond_exprs[row]= + and_exprt(templ[row].pre_guard, templ[row].post_guard); } else { @@ -66,26 +115,28 @@ exprt linrank_domaint::get_not_constraints( assert(value[row].c.size()>=1); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(value[row].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + value[row].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(value[row].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(value[row].c[0],templ[row].expr[0].second); + exprt sum_pre=mult_exprt(value[row].c[0], templ[row].expr[0].first); + exprt sum_post=mult_exprt(value[row].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < value[row].c.size(); ++i) + for(unsigned i=1; i=1); - symb_values.c[0] = symbol_exprt(SYMB_COEFF_VAR+std::string("c!")+ - i2string(row)+"$0", - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + symb_values.c[0]=symbol_exprt( + SYMB_COEFF_VAR+std::string("c!")+i2string(row)+"$0", + signedbv_typet(COEFF_C_SIZE)); // coefficients are signed integers #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(symb_values.c[0], - minus_exprt(values[0].first, - values[0].second)); + exprt sum=mult_exprt( + symb_values.c[0], + minus_exprt(values[0].first, values[0].second)); #else - exprt sum_pre = mult_exprt(symb_values.c[0],values[0].first); - exprt sum_post = mult_exprt(symb_values.c[0],values[0].second); + exprt sum_pre=mult_exprt(symb_values.c[0], values[0].first); + exprt sum_post=mult_exprt(symb_values.c[0], values[0].second); #endif - for(unsigned i = 1; i < symb_values.c.size(); ++i) - { - symb_values.c[i] = symbol_exprt( - SYMB_COEFF_VAR+std::string("c!")+i2string(row)+"$"+i2string(i), - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + for(unsigned i=1; i(value); - for(unsigned row = 0; row(value); + for(unsigned row=0; row " << std::endl << " "; + out << "(RANK) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) + << " ]===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; - out << from_expr(ns,"",v[row].c[i]) << " * " - << from_expr(ns,"",templ_row.expr[i].first); + if(i>0) + out << "+"; + out << from_expr(ns, "", v[row].c[i]) << " * " + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void linrank_domaint::output_domain(std::ostream &out, const namespacet &ns) const +/*******************************************************************\ + +Function: linrank_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void linrank_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " + out << "(RANK) (" << from_expr(ns, "", templ_row.pre_guard) + << ") && (" << from_expr(ns, "", templ_row.post_guard) << ")===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; + if(i>0) + out << "+"; out << "c!" << row << "$" << i << " * " - << from_expr(ns,"",templ_row.expr[i].first); + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void linrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) +/*******************************************************************\ + +Function: linrank_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void linrank_domaint::project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) { - //don't do any projection - const templ_valuet &v = static_cast(value); + // don't do any projection + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; c.reserve(templ.size()); - for(unsigned row = 0; row true) + // (g=> true) c.push_back(true_exprt()); } else if(is_row_value_false(v[row])) { - // (g => false) - c.push_back(implies_exprt(and_exprt(templ[row].pre_guard, templ[row].post_guard),false_exprt())); + // (g=> false) + c.push_back(implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); } else { @@ -309,26 +466,28 @@ void linrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt assert(v[row].c.size()>=1); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(v[row].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + v[row].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(v[row].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(v[row].c[0],templ[row].expr[0].second); + exprt sum_pre=mult_exprt(v[row].c[0], templ[row].expr[0].first); + exprt sum_post=mult_exprt(v[row].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < v[row].c.size(); ++i) + for(unsigned i=1; ikind==LOOP) { - has_loop = true; + has_loop=true; break; } } - if(!has_loop) return; + if(!has_loop) + return; templ.reserve(templ.size()+1); templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.kind = LOOP; + template_rowt &templ_row=templ.back(); + templ_row.kind=LOOP; exprt::operandst preg; exprt::operandst postg; - for(var_specst::const_iterator v = var_specs.begin(); + for(var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind!=LOOP) continue; + if(v->kind!=LOOP) + continue; preg.push_back(v->pre_guard); postg.push_back(v->post_guard); - exprt vpost = v->var; //copy + exprt vpost=v->var; // copy rename(vpost); - templ_row.expr.push_back(std::pair(v->var,vpost)); + templ_row.expr.push_back(std::pair(v->var, vpost)); } - templ_row.pre_guard = conjunction(preg); - templ_row.post_guard = conjunction(postg); + templ_row.pre_guard=conjunction(preg); + templ_row.post_guard=conjunction(postg); } /*******************************************************************\ diff --git a/src/domains/linrank_domain.h b/src/domains/linrank_domain.h index 6be96436f..4d1383098 100644 --- a/src/domains/linrank_domain.h +++ b/src/domains/linrank_domain.h @@ -1,31 +1,42 @@ -#ifndef CPROVER_LINRANK_DOMAIN_H -#define CPROVER_LINRANK_DOMAIN_H +/*******************************************************************\ -#include "domain.h" +Module: Linear ranking function domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H +#define CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H + +#ifdef DEBUG +#include +#endif #include #include #include #include #include -#include -class linrank_domaint : public domaint +#include "domain.h" + +class linrank_domaint:public domaint { public: typedef unsigned rowt; - typedef std::vector > pre_post_valuest; + typedef std::vector > pre_post_valuest; typedef pre_post_valuest row_exprt; typedef struct { std::vector c; } row_valuet; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -35,12 +46,14 @@ class linrank_domaint : public domaint typedef std::vector templatet; - linrank_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number, _renaming_map, _ns), - refinement_level(0) - {} + linrank_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns), + refinement_level(0) + { + } // initialize value virtual void initialize(valuet &value); @@ -48,33 +61,45 @@ class linrank_domaint : public domaint virtual bool refine(); // value -> constraints - exprt get_not_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs,// identical to before - std::vector &value_exprs); // (x, x') - exprt get_row_symb_constraint(row_valuet &symb_values, // contains vars c - const rowt &row, - const pre_post_valuest &values, - exprt &refinement_constraint - ); - + exprt get_not_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, // identical to before + std::vector &value_exprs); // (x, x') + exprt get_row_symb_constraint( + row_valuet &symb_values, // contains vars c + const rowt &row, + const pre_post_valuest &values, + exprt &refinement_constraint); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); void set_row_value_to_true(const rowt &row, templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; - - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); - - unsigned template_size() {return templ.size();} + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const; + + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); + + unsigned template_size() { return templ.size(); } // generating templates - void add_template(const var_specst &var_specs, - const namespacet &ns); + void add_template( + const var_specst &var_specs, + const namespacet &ns); protected: templatet templ; @@ -82,9 +107,6 @@ class linrank_domaint : public domaint bool is_row_value_false(const row_valuet & row_value) const; bool is_row_value_true(const row_valuet & row_value) const; - - }; - -#endif +#endif // CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H diff --git a/src/domains/predabs_domain.cpp b/src/domains/predabs_domain.cpp index ea898d5c0..ec05e41ac 100644 --- a/src/domains/predabs_domain.cpp +++ b/src/domains/predabs_domain.cpp @@ -1,7 +1,14 @@ -#include "predabs_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Predicate abstraction domain + +Author: Peter Schrammel + +\*******************************************************************/ +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "predabs_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define COMPLEXITY_COUNTER_PREFIX "__CPROVER_CPLX_CNT_" @@ -26,51 +36,80 @@ Function: predabs_domaint::initialize void predabs_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row (row_value => row_expr) + Purpose: pre_guard==> (row_value=> row_expr) \*******************************************************************/ -exprt predabs_domaint::get_row_constraint(const rowt &row, +exprt predabs_domaint::get_row_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_pre_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_pre_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_pre_constraint(row,value[row]); + return get_row_pre_constraint(row, value[row]); } /*******************************************************************\ @@ -81,27 +120,44 @@ Function: predabs_domaint::get_row_post_constraint Outputs: - Purpose: post_guard => (row_value => row_expr) + Purpose: post_guard=> (row_value=> row_expr) \*******************************************************************/ -exprt predabs_domaint::get_row_post_constraint(const rowt &row, +exprt predabs_domaint::get_row_post_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_post_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_post_constraint(row,value[row]); + return get_row_post_constraint(row, value[row]); } /*******************************************************************\ @@ -112,19 +168,19 @@ Function: predabs_domaint::to_pre_constraints Outputs: - Purpose: /\_all_rows ( pre_guard ==> (row_value => row_expr) ) + Purpose: /\_all_rows ( pre_guard==> (row_value=> row_expr) ) \*******************************************************************/ exprt predabs_domaint::to_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_value => row_expr) ) + Purpose: for all rows !(post_guard==> (row_value=> row_expr) ) to be connected disjunctively \*******************************************************************/ -void predabs_domaint::make_not_post_constraints(const templ_valuet &value, +void predabs_domaint::make_not_post_constraints( + const templ_valuet &value, exprt::operandst &cond_exprs) { assert(value.size()==templ.size()); cond_exprs.resize(templ.size()); - exprt::operandst c; - for(unsigned row = 0; row(value); + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); + find_symbols(templ_row.expr, symbols); - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) + bool pure=true; + for(const auto &symbol : symbols) { - if(vars.find(*it)==vars.end()) + if(vars.find(symbol)==vars.end()) { - pure = false; + pure=false; break; } } - if(!pure) continue; + if(!pure) + continue; - const row_valuet &row_v = v[row]; + const row_valuet &row_v=v[row]; if(templ_row.kind==LOOP) { - c.push_back(implies_exprt(templ_row.pre_guard, - implies_exprt(row_v,templ_row.expr))); + c.push_back(implies_exprt( + templ_row.pre_guard, + implies_exprt(row_v, templ_row.expr))); } else { - c.push_back(implies_exprt(row_v,templ_row.expr)); + c.push_back(implies_exprt(row_v, templ_row.expr)); } } - result = conjunction(c); + result=conjunction(c); } /*******************************************************************\ @@ -239,11 +301,13 @@ Function: predabs_domaint::set_row_value \*******************************************************************/ void predabs_domaint::set_row_value( - const rowt &row, const predabs_domaint::row_valuet &row_value, templ_valuet &value) + const rowt &row, + const predabs_domaint::row_valuet &row_value, + templ_valuet &value) { assert(row(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; case IN: out << "(IN) "; break; case OUT: case OUTL: out << "(OUT) "; break; default: assert(false); } - out << "( " << from_expr(ns,"",v[row]) << " ==> " << - from_expr(ns,"",templ_row.expr) << " )" << std::endl; + out << "( " << from_expr(ns, "", v[row]) << "==> " << + from_expr(ns, "", templ_row.expr) << " )" << std::endl; } } @@ -293,30 +360,34 @@ Function: predabs_domaint::output_domain \*******************************************************************/ -void predabs_domaint::output_domain(std::ostream &out, const namespacet &ns) const +void predabs_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) + << "===> " << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) + << "===> " << std::endl << " "; break; default: assert(false); } - out << "( " << - from_expr(ns,"",templ_row.expr) << ")" << std::endl; + out << "( " << from_expr(ns, "", templ_row.expr) << ")" << std::endl; } } @@ -339,7 +410,7 @@ unsigned predabs_domaint::template_size() /*******************************************************************\ -Function: add_template_row +Function: predabs_domaint::add_template_row Inputs: @@ -358,19 +429,19 @@ predabs_domaint::template_rowt &predabs_domaint::add_template_row( ) { templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = expr; - //extend_expr_types(templ_row.expr); - templ_row.pre_guard = pre_guard; - templ_row.post_guard = post_guard; - templ_row.aux_expr = aux_expr; - templ_row.kind = kind; + template_rowt &templ_row=templ.back(); + templ_row.expr=expr; + // extend_expr_types(templ_row.expr); + templ_row.pre_guard=pre_guard; + templ_row.post_guard=post_guard; + templ_row.aux_expr=aux_expr; + templ_row.kind=kind; return templ_row; } /*******************************************************************\ -Function: equality_domaint::get_var_pairs +Function: predabs_domaint::get_row_set Inputs: @@ -380,7 +451,8 @@ Function: equality_domaint::get_var_pairs \*******************************************************************/ -void predabs_domaint::get_row_set(std::set &rows) +void predabs_domaint::get_row_set(std::set &rows) { - for(unsigned i=0;i #include #include #include -#include -class predabs_domaint : public domaint +#include "domain.h" + +class predabs_domaint:public domaint { public: typedef unsigned rowt; - typedef exprt row_exprt; //predicate - typedef constant_exprt row_valuet; //true/false + typedef exprt row_exprt; // predicate + typedef constant_exprt row_valuet; // true/false typedef std::set row_sett; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -31,11 +40,13 @@ class predabs_domaint : public domaint typedef std::vector templatet; - predabs_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet _ns) : - domaint(_domain_number, _renaming_map, _ns) - {} + predabs_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet _ns): + domaint(_domain_number, _renaming_map, _ns) + { + } // initialize value virtual void initialize(valuet &value); @@ -48,19 +59,29 @@ class predabs_domaint : public domaint exprt get_row_post_constraint(const rowt &row, const templ_valuet &value); exprt to_pre_constraints(const templ_valuet &value); - void make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs); + void make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const; virtual void output_domain(std::ostream &out, const namespacet &ns) const; - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size(); @@ -70,14 +91,12 @@ class predabs_domaint : public domaint const exprt& pre_guard, const exprt& post_guard, const exprt& aux_expr, - kindt kind - ); + kindt kind); - void get_row_set(row_sett &rows); + void get_row_set(row_sett &rows); protected: templatet templ; - }; #endif diff --git a/src/domains/predicate.cpp b/src/domains/predicate.cpp deleted file mode 100644 index 3c772b4c5..000000000 --- a/src/domains/predicate.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Abstract State - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicate.h" - -/*******************************************************************\ - -Function: predicatet::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatet::make_false() -{ - uuf.resize(state_vars.size()); - - for(unsigned v1=0; v1 &dest) const -{ - for(unsigned v=0; v eq_count; - - for(unsigned v=0; v::const_iterator - e_it=eq_count.begin(); e_it!=eq_count.end(); e_it++) - { - if(e_it->second>=2) - { - for(unsigned v=0; vfirst==uuf.find(v)) - out << "Equal: " << from_expr(state_vars[v]) << "\n"; - out << "\n"; - } - } - - // print intervals - #if 0 - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); i_it!=integer_intervals.end(); i_it++) - { - if(i_it->lower_is_set || i_it->upper_is_set) - { - if(i_it->lower_is_set) - out << from_expr(i_it->lower) << " <= "; - - out << from_expr(vars[i_it-intervals.begin()]); - - if(i_it->upper_is_set) - out << " <= " << from_expr(i_it->lower); - - out << "\n"; - } - } - #endif -} - -/*******************************************************************\ - -Function: predicatet::disjunction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool predicatet::disjunction(const predicatet &other) -{ - bool change=false; - - // - // do equalities - // - - assert(other.state_vars.size()==state_vars.size()); - - unsigned_union_find new_uuf; - new_uuf.resize(uuf.size()); - - // can be done better than quadratic - for(unsigned v1=0; v1 -#include -#include -#include -#include - -#include - -struct predicatet -{ -public: - predicatet() - { - } - - // support set of the predicate - typedef std::vector state_var_listt; - state_var_listt state_vars; - - void output(std::ostream &) const; - void make_false(); - - // returns 'true' iff predicate is weakened - bool disjunction(const predicatet &); - - // rename supporting set of variables - void rename(const state_var_listt &new_state_vars); - - // read the predicate from a solver state - //void get(const solvert &); - - // push the predicate to a solver as constraint - void set_to_true(decision_proceduret &) const; - - bool is_bottom() const - { - return false; - } - - bool is_top() const - { - for(unsigned i=0; i &) const; - -protected: - // for now, we can track: - // * equalities between variables - // * intervals - - unsigned_union_find uuf; - - typedef expanding_vector integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - integer_intervalst integer_intervals; - ieee_float_intervalst ieee_float_intervals; -}; - -static inline std::ostream & operator << ( - std::ostream &out, const predicatet &predicate) -{ - predicate.output(out); - return out; -} - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, const predicatet &predicate) -{ - predicate.set_to_true(dest); - return dest; -} - -#endif diff --git a/src/domains/ranking_solver_enumeration.cpp b/src/domains/ranking_solver_enumeration.cpp index 8b21786d8..bd04196d9 100644 --- a/src/domains/ranking_solver_enumeration.cpp +++ b/src/domains/ranking_solver_enumeration.cpp @@ -1,148 +1,173 @@ +/*******************************************************************\ + +Module: Enumeration-based solver for linear ranking functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include + #include "ranking_solver_enumeration.h" #include "util.h" -#include -bool ranking_solver_enumerationt::iterate(invariantt &_rank) +/*******************************************************************\ + +Function: ranking_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +ranking_solver_enumerationt::progresst +ranking_solver_enumerationt::iterate(invariantt &_rank) { - linrank_domaint::templ_valuet &rank = + linrank_domaint::templ_valuet &rank= static_cast(_rank); - bool improved = false; + progresst progress=CONVERGED; - //context for "outer" solver + // context for "outer" solver solver.new_context(); - //choose round to even rounding mode for template computations - // not clear what its implications on soundness and termination of the synthesis are - exprt rounding_mode = symbol_exprt(CPROVER_PREFIX "rounding_mode",signedbv_typet(32)); - solver << equal_exprt(rounding_mode,from_integer(mp_integer(0),signedbv_typet(32))); - - //handles on values to retrieve from model + // choose round to even rounding mode for template computations + // not clear what its implications on soundness and + // termination of the synthesis are + exprt rounding_mode= + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + solver << + equal_exprt( + rounding_mode, + from_integer(mp_integer(0), signedbv_typet(32))); + + // handles on values to retrieve from model std::vector rank_value_exprs; exprt::operandst rank_cond_exprs; bvt rank_cond_literals; - exprt rank_expr = + exprt rank_expr= linrank_domain.get_not_constraints(rank, rank_cond_exprs, rank_value_exprs); solver << rank_expr; rank_cond_literals.resize(rank_cond_exprs.size()); - for(unsigned i = 0; i < rank_cond_literals.size(); i++) - { - rank_cond_literals[i] = solver.solver->convert(rank_cond_exprs[i]); + for(std::size_t i=0; iconvert(rank_cond_exprs[i]); } debug() << "solve(): "; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { debug() << "SAT" << eom; - - for(unsigned row = 0; row < rank_cond_literals.size(); row++) + + for(std::size_t row=0; rowl_get(rank_cond_literals[row]).is_true()) + + if(solver.solver->l_get(rank_cond_literals[row]).is_true()) { - for(linrank_domaint::pre_post_valuest::iterator it = - rank_value_exprs[row].begin(); - it != rank_value_exprs[row].end(); ++it) - { - // model for x_i - exprt value = solver.solver->get(it->first); - debug() << "(RANK) Row " << row << " Value for " - << from_expr(ns,"",it->first) - << ": " << from_expr(ns,"",value) << eom; - // model for x'_i - exprt post_value = solver.solver->get(it->second); - debug() << "(RANK) Row " << row << " Value for " - << from_expr(ns,"",it->second) - << ": " << from_expr(ns,"",post_value) << eom; - // record all the values - values.push_back(std::make_pair(value, post_value)); - } - - linrank_domaint::row_valuet symb_values; - exprt constraint; - exprt refinement_constraint; - - // generate the new constraint - constraint = - linrank_domain.get_row_symb_constraint(symb_values, row, - values,refinement_constraint); - simplify_expr(constraint, ns); - debug() << "Inner Solver: " << row << " constraint " - << from_expr(ns,"", constraint) << eom; - - inner_solver << equal_exprt(rounding_mode, - from_integer(mp_integer(0),signedbv_typet(32))); - inner_solver << constraint; - - //refinement - if(!refinement_constraint.is_true()) - { - inner_solver.new_context(); - inner_solver << refinement_constraint; - } - - //solve - debug() << "inner solve()" << eom; - solver_calls++; - if(inner_solver() == decision_proceduret::D_SATISFIABLE && - number_inner_iterations < max_inner_iterations) - { - - debug() << "inner solver: SAT" << eom; - - std::vector c = symb_values.c; - - // new_row_values will contain the new values for c - linrank_domaint::row_valuet new_row_values; - - // get the model for all c - for(std::vector::iterator it = c.begin(); it != c.end(); ++it) - { - exprt v = inner_solver.solver->get(*it); - new_row_values.c.push_back(v); - debug() << "Inner Solver: " << row << " c value for " - << from_expr(ns,"", *it) << ": " - << from_expr(ns,"", v) << eom; - } - exprt rmv = inner_solver.solver->get(rounding_mode); + for(const auto &rve : rank_value_exprs[row]) + { + // model for x_i + exprt value=solver.solver->get(rve.first); + debug() << "(RANK) Row " << row << " Value for " + << from_expr(ns, "", rve.first) + << ": " << from_expr(ns, "", value) << eom; + // model for x'_i + exprt post_value=solver.solver->get(rve.second); + debug() << "(RANK) Row " << row << " Value for " + << from_expr(ns, "", rve.second) + << ": " << from_expr(ns, "", post_value) << eom; + // record all the values + values.push_back(std::make_pair(value, post_value)); + } + + linrank_domaint::row_valuet symb_values; + exprt constraint; + exprt refinement_constraint; + + // generate the new constraint + constraint=linrank_domain.get_row_symb_constraint( + symb_values, row, values, refinement_constraint); + simplify_expr(constraint, ns); + debug() << "Inner Solver: " << row << " constraint " + << from_expr(ns, "", constraint) << eom; + + inner_solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); + inner_solver << constraint; + + // refinement + if(!refinement_constraint.is_true()) + { + inner_solver.new_context(); + inner_solver << refinement_constraint; + } + + // solve + debug() << "inner solve()" << eom; + solver_calls++; + if(inner_solver()==decision_proceduret::D_SATISFIABLE && + number_inner_iterations c=symb_values.c; + + // new_row_values will contain the new values for c + linrank_domaint::row_valuet new_row_values; + + // get the model for all c + for(const auto &e : c) + { + exprt v=inner_solver.solver->get(e); + new_row_values.c.push_back(v); + debug() << "Inner Solver: " << row << " c value for " + << from_expr(ns, "", e) << ": " + << from_expr(ns, "", v) << eom; + } + exprt rmv=inner_solver.solver->get(rounding_mode); debug() << "Rounding mode: " << from_expr(ns, "", rmv) << eom; - // update the current template - linrank_domain.set_row_value(row, new_row_values, rank); - - improved = true; - } - else - { - debug() << "inner solver: UNSAT" << eom; - - if(linrank_domain.refine()) - { - debug() << "refining..." << eom; - improved = true; //refinement possible - } - else - { + // update the current template + linrank_domain.set_row_value(row, new_row_values, rank); + + progress=CHANGED; + } + else + { + debug() << "inner solver: UNSAT" << eom; + + if(linrank_domain.refine()) + { + debug() << "refining..." << eom; + progress=CHANGED; // refinement possible + } + else + { // no ranking function for the current template - linrank_domain.set_row_value_to_true(row, rank); - linrank_domain.reset_refinements(); - } - } + linrank_domain.set_row_value_to_true(row, rank); + linrank_domain.reset_refinements(); + } + } - if(!refinement_constraint.is_true()) inner_solver.pop_context(); + if(!refinement_constraint.is_true()) + inner_solver.pop_context(); } } - } - else + else { debug() << "UNSAT" << eom; linrank_domain.reset_refinements(); @@ -150,5 +175,5 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/ranking_solver_enumeration.h b/src/domains/ranking_solver_enumeration.h index d52ab0bb1..b6e810b35 100644 --- a/src/domains/ranking_solver_enumeration.h +++ b/src/domains/ranking_solver_enumeration.h @@ -1,40 +1,47 @@ -#ifndef CPROVER_RANKING_SOLVER_ENUMERATION_H -#define CPROVER_RANKING_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Enumeration-based solver for linear ranking functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_RANKING_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_RANKING_SOLVER_ENUMERATION_H #include #include #include "strategy_solver_base.h" -#include "../domains/incremental_solver.h" +#include "incremental_solver.h" #include "linrank_domain.h" -class ranking_solver_enumerationt : public strategy_solver_baset +class ranking_solver_enumerationt:public strategy_solver_baset { - public: - explicit ranking_solver_enumerationt( +public: + ranking_solver_enumerationt( linrank_domaint &_linrank_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, const namespacet &_ns, - unsigned _max_inner_iterations - ) : - strategy_solver_baset(_solver, _ns), + unsigned _max_inner_iterations): + strategy_solver_baset(_solver, literalt(), _ns), linrank_domain(_linrank_domain), max_inner_iterations(_max_inner_iterations), inner_solver(_ns), number_inner_iterations(0) - { - solver_instances++; - } + { + solver_instances++; + } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); - protected: +protected: linrank_domaint &linrank_domain; // the "inner" solver const unsigned max_inner_iterations; incremental_solvert inner_solver; - int number_inner_iterations; + unsigned number_inner_iterations; }; #endif diff --git a/src/domains/simplify_bounds.cpp b/src/domains/simplify_bounds.cpp new file mode 100644 index 000000000..8c63fa646 --- /dev/null +++ b/src/domains/simplify_bounds.cpp @@ -0,0 +1,454 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_bounds.h" +#include "simplify_bounds_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_boundst::get_min_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_min_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_min_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_smallest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_min_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_max_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::get_max_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_max_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_max_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_largest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_max_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_min_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::simplify_rec(exprt &expr) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_le) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=true_exprt(); + result=false; + } + else + result=clean_up_typecast(expr, rhs_value); + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_ge) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_lt) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_gt) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else + { + Forall_operands(it, expr) + if(!simplify_rec(*it)) + result=false; + } +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_typecast(exprt &expr, + const mp_integer &rhs_value) +{ + if(expr.id()==ID_le && expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast) + { + const typet &inner_type=expr.op0().op0().op0().type(); + if(-rhs_value>get_smallest(inner_type)) + { + expr=binary_relation_exprt(expr.op0().op0().op0(), ID_ge, + from_integer(-rhs_value, inner_type)); + return true; + } + } + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_implications(exprt &expr) +{ + bool result=true; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(!clean_up_implications(*it)) + result=false; + exprt::operandst::iterator it=expr.operands().begin(); + while(it!=expr.operands().end()) + { + if(it->is_true()) + it=expr.operands().erase(it); + else + ++it; + } + if(expr.operands().empty()) + expr=true_exprt(); + else if(expr.operands().size()==1) + expr=expr.op0(); + } + else if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + { + expr=true_exprt(); + result=false; + } + } + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::regroup_implications(exprt &expr) +{ + std::map implication_map; + exprt::operandst r; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(it->id()==ID_implies) + implication_map[it->op0()].push_back(it->op1()); + else + r.push_back(*it); + } + else + return true; + for(auto &i : implication_map) + r.push_back(implies_exprt(i.first, conjunction(i.second))); + expr=conjunction(r); + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::operator()(exprt &expr) +{ + bool result=simplify_rec(expr); + if(!clean_up_implications(expr)) + result=false; + if(!regroup_implications(expr)) + result=false; + return result; +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_bounds(exprt &expr, + const namespacet &ns) +{ + return simplify_boundst(ns)(expr); +} + +/*******************************************************************\ + +Function: simplify_bounds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_bounds(const exprt &src, + const namespacet &ns) +{ + exprt tmp=src; + simplify_boundst simplify_bounds(ns); + simplify_bounds(tmp); + return tmp; +} + diff --git a/src/domains/simplify_bounds.h b/src/domains/simplify_bounds.h new file mode 100644 index 000000000..df8ce143c --- /dev/null +++ b/src/domains/simplify_bounds.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H + +#include + +class exprt; +class namespacet; + +// +// simplify bounds +// +// true: did nothing +// false: simplified something +// + +bool simplify_bounds( + exprt &expr, + const namespacet &ns); + +exprt simplify_bounds( + const exprt &src, + const namespacet &ns); + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H diff --git a/src/domains/simplify_bounds_class.h b/src/domains/simplify_bounds_class.h new file mode 100644 index 000000000..c4622c100 --- /dev/null +++ b/src/domains/simplify_bounds_class.h @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: Bounds Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_boundst +{ +public: + explicit simplify_boundst(const namespacet &_ns): + ns(_ns) + { + } + + virtual ~simplify_boundst() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv; + } + inline static mp_integer get_largest(const typet &type) + { + if(type.id()==ID_signedbv) + return to_signedbv_type(type).largest(); + else if(type.id()==ID_unsignedbv) + return to_unsignedbv_type(type).largest(); + assert(false); + } + inline static mp_integer get_smallest(const typet &type) + { + if(type.id()==ID_signedbv) + return to_signedbv_type(type).smallest(); + else if(type.id()==ID_unsignedbv) + return to_unsignedbv_type(type).smallest(); + assert(false); + } + +protected: + const namespacet &ns; + + bool simplify_rec(exprt &expr); + bool get_min_bound(const exprt &expr, mp_integer &value); + bool get_max_bound(const exprt &expr, mp_integer &value); + + bool clean_up_implications(exprt &expr); + bool regroup_implications(exprt &expr); + bool clean_up_typecast(exprt &expr, const mp_integer &value); +}; + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H diff --git a/src/domains/simplify_transformer.cpp b/src/domains/simplify_transformer.cpp new file mode 100644 index 000000000..ebddc12ea --- /dev/null +++ b/src/domains/simplify_transformer.cpp @@ -0,0 +1,259 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_transformer.h" +#include "simplify_transformer_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_transformert::collect_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void simplify_transformert::collect_node( + const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy) +{ + if(expr.id()==ID_equal) + { + const equal_exprt &e=to_equal_expr(expr); + + bool rhs_is_constant=e.rhs().id()==ID_constant; + bool rhs_is_symbol=e.rhs().id()==ID_symbol || + e.rhs().id()==ID_nondet_symbol; + bool rhs_is_frozen=rhs_is_symbol && + frozen_symbols.find(e.rhs().get(ID_identifier))!=frozen_symbols.end(); + bool lhs_is_constant=e.lhs().id()==ID_constant; + bool lhs_is_symbol=e.lhs().id()==ID_symbol || + e.lhs().id()==ID_nondet_symbol; + bool lhs_is_frozen=lhs_is_symbol && + frozen_symbols.find(e.lhs().get(ID_identifier))!=frozen_symbols.end(); + + exprt lhs, rhs; + lhs.make_nil(); + rhs.make_nil(); + // stupid matching + if((rhs_is_frozen || rhs_is_constant || !frozen_only) && + lhs_is_symbol && !lhs_is_frozen) + { + lhs=e.lhs(); + rhs=e.rhs(); + } + if((lhs_is_frozen || lhs_is_constant || !frozen_only) && + rhs_is_symbol && !rhs_is_frozen) + { + rhs=e.lhs(); + lhs=e.rhs(); + } + if(rhs.is_not_nil() && lhs.is_not_nil()) + { + if(make_copy) // make lazy copy + { + replace_mapt _subst=substitutions; + substitutions=_subst; + } + substitutions[lhs]=rhs; + } + } + +#ifdef DEBUGX + std::cout << "COLLECT: " << from_expr(ns, "", expr) << std::endl; + for(const auto &it : substitutions) + std::cout << from_expr(ns, "", it.first) + << "---> " << from_expr(ns, "", it.second) + << "\n"; +#endif +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_node( + exprt &expr, + const replace_mapt &substitutions) +{ + return replace_expr(substitutions, expr); +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_rec( + exprt &expr, + replace_mapt &substitutions) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_and) + { + // first propagate from frozen symbols + bool res=false; + do + { + Forall_operands(it, expr) + collect_node(*it, substitutions, true, false); + + Forall_operands(it, expr) + if(!simplify_rec(*it, substitutions)) + result=false; + + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + + // simplify remaining equalities + Forall_operands(it, expr) + collect_node(*it, substitutions, false, false); + + res=false; + do + { + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + } + +#if 0 // for later extension to disjunctions + // TODO: handle negation, too + else if(expr.id()==ID_or || expr.id()==ID_implies) + { + Forall_operands(it, expr) + { + collect_node(*it, substitutions, true); + if(!simplify_rec(*it, substitutions)) + result=false; + + bool res=false; + do + { + res=simplify_node(*it, substitutions); + if(!res) result=false; + } + while(!res); + } + } +#endif + +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_transformert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::operator()(exprt &expr) +{ + replace_mapt substitutions; + return simplify_rec(expr, substitutions); +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify( + exprt &expr, + const std::set &frozen_vars, + const namespacet &ns) +{ + return simplify_transformert(ns, frozen_vars)(expr); +} + +/*******************************************************************\ + +Function: simplify_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, + const namespacet &ns) +{ +#ifdef DEBUGX + std::cout << "FROZEN:"; + for(const auto &it : frozen_vars) + std::cout << " " << it; + std::cout << "\n"; +#endif + + exprt tmp=src; + simplify_transformert(ns, frozen_vars)(tmp); + return tmp; +} + diff --git a/src/domains/simplify_transformer.h b/src/domains/simplify_transformer.h new file mode 100644 index 000000000..31779d6f0 --- /dev/null +++ b/src/domains/simplify_transformer.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H + +#include +#include + +class exprt; +class namespacet; + +// +// simplify transformers by best-effort intermediate variable elimination +// +// true: did nothing +// false: simplified something +// + +bool simplify( + exprt &expr, + const std::set &frozen_vars, // do not eliminate these + const namespacet &ns); + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, // do not eliminate these + const namespacet &ns); + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H diff --git a/src/domains/simplify_transformer_class.h b/src/domains/simplify_transformer_class.h new file mode 100644 index 000000000..9a9218516 --- /dev/null +++ b/src/domains/simplify_transformer_class.h @@ -0,0 +1,61 @@ +/*******************************************************************\ + +Module: Transformer Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_transformert +{ +public: + simplify_transformert( + const namespacet &_ns, + const std::set &_frozen_symbols): + ns(_ns), + frozen_symbols(_frozen_symbols) + { + } + + virtual ~simplify_transformert() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + // main recursion + bool simplify_rec(exprt &expr, replace_mapt &substitutions); + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv; + } + +protected: + const namespacet &ns; + const std::set &frozen_symbols; + + void collect_node( + const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy); + + bool simplify_node(exprt &expr, const replace_mapt &substitutions); +}; + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H diff --git a/src/domains/solver_enumeration.cpp b/src/domains/solver_enumeration.cpp deleted file mode 100644 index 5f5ac9431..000000000 --- a/src/domains/solver_enumeration.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include - -#include -#include "solver_enumeration.h" - -bool solver_enumerationt::iterate(invariantt &_inv) -{ - tpolyhedra_domaint::templ_valuet &inv = - static_cast(_inv); - - bool improved = false; - - literalt activation_literal = new_context(); - - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; - -#ifndef DEBUG_FORMULA - solver << or_exprt(inv_expr, literal_exprt(activation_literal)); -#else - debug() << "literal " << activation_literal << eom; - literalt l = solver.convert(or_exprt(inv_expr, literal_exprt(activation_literal))); - if(!l.is_constant()) - { - debug() << "literal " << l << ": " << from_expr(ns,"",or_exprt(inv_expr, literal_exprt(activation_literal))) <first) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " << - from_expr(ns, "", solver.get(it->second)) << eom; - } - - #endif - - tpolyhedra_domaint::templ_valuet new_value; - tpolyhedra_domain.initialize(new_value); - for(unsigned row=0;row +#endif +#include +#include +#include +#include +#include +#include #include - #include "strategy_solver_base.h" #include "strategy_solver_enumeration.h" -//#include "solver_enumeration.h" #include "strategy_solver_binsearch.h" #include "strategy_solver_binsearch2.h" #include "strategy_solver_binsearch3.h" @@ -26,21 +32,16 @@ Author: Daniel Kroening, kroening@kroening.com #include "strategy_solver_predabs.h" #include "ssa_analyzer.h" - -#include -#include - -#include -#include -#include -#include - -#define BINSEARCH_SOLVER strategy_solver_binsearcht(*static_cast(domain), solver, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch2t(*static_cast(domain), solver, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch3t(*static_cast(domain), solver, SSA, SSA.ns) - -#ifdef DEBUG -#include +#define BINSEARCH_SOLVER strategy_solver_binsearcht( \ + *static_cast(domain), \ + solver, assertions_check, SSA.ns) +#if 0 +#define BINSEARCH_SOLVER strategy_solver_binsearch2t( \ + *static_cast(domain), solver, \ + assertions_check, SSA.ns) +#define BINSEARCH_SOLVER strategy_solver_binsearch3t( \ + *static_cast(domain), solver, \ + assertions_check, SSA, SSA.ns) #endif /*******************************************************************\ @@ -49,19 +50,21 @@ Function: ssa_analyzert::operator() Inputs: - Outputs: - + Outputs: true if the computation was not aborted due to + assertion_checks that did not pass Purpose: \*******************************************************************/ -void ssa_analyzert::operator()(incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator) +bool ssa_analyzert::operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions) { if(SSA.goto_function.body.instructions.empty()) - return; + return true; solver << SSA; SSA.mark_nodes(); @@ -71,108 +74,127 @@ void ssa_analyzert::operator()(incremental_solvert &solver, // add precondition (or conjunction of asssertion in backward analysis) solver << precondition; - - domain = template_generator.domain(); + + domain=template_generator.domain(); + + // get assertions if check_assertions is requested + literalt assertions_check=const_literal(false); + bvt assertion_literals; + if(check_assertions) + { + exprt::operandst ll; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + assert(n_it->assertions.size()<=1); + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + { + literalt l=solver.solver->convert(*a_it); + assertion_literals.push_back(!l); + ll.push_back(literal_exprt(!l)); + nonpassed_assertions.push_back(n_it); + } + } + assertions_check=solver.solver->convert(disjunction(ll)); + } // get strategy solver from options strategy_solver_baset *strategy_solver; if(template_generator.options.get_bool_option("compute-ranking-functions")) { if(template_generator.options.get_bool_option( - "monolithic-ranking-function")) + "monolithic-ranking-function")) { - strategy_solver = new ranking_solver_enumerationt( + strategy_solver=new ranking_solver_enumerationt( *static_cast(domain), solver, SSA.ns, - template_generator.options.get_unsigned_int_option( - "max-inner-ranking-iterations")); - result = new linrank_domaint::templ_valuet(); + template_generator.options.get_unsigned_int_option( + "max-inner-ranking-iterations")); + result=new linrank_domaint::templ_valuet(); } else { - strategy_solver = new lexlinrank_solver_enumerationt( + strategy_solver=new lexlinrank_solver_enumerationt( *static_cast(domain), solver, SSA.ns, template_generator.options.get_unsigned_int_option( - "lexicographic-ranking-function"), - template_generator.options.get_unsigned_int_option( + "lexicographic-ranking-function"), + template_generator.options.get_unsigned_int_option( "max-inner-ranking-iterations")); - result = new lexlinrank_domaint::templ_valuet(); + result=new lexlinrank_domaint::templ_valuet(); } - } + } else if(template_generator.options.get_bool_option("equalities")) { - strategy_solver = new strategy_solver_equalityt( - *static_cast(domain), solver, SSA.ns); - result = new equality_domaint::equ_valuet(); + strategy_solver=new strategy_solver_equalityt( + *static_cast(domain), + solver, assertions_check, SSA.ns); + result=new equality_domaint::equ_valuet(); } else { if(template_generator.options.get_bool_option("enum-solver")) { - result = new tpolyhedra_domaint::templ_valuet(); - strategy_solver = new strategy_solver_enumerationt( - *static_cast(domain), solver, SSA.ns); + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new strategy_solver_enumerationt( + *static_cast(domain), + solver, assertions_check, SSA.ns); } else if(template_generator.options.get_bool_option("predabs-solver")) { - result = new predabs_domaint::templ_valuet(); - strategy_solver = new strategy_solver_predabst( - *static_cast(domain), solver, SSA.ns); + result=new predabs_domaint::templ_valuet(); + strategy_solver=new strategy_solver_predabst( + *static_cast(domain), + solver, assertions_check, SSA.ns); } else if(template_generator.options.get_bool_option("binsearch-solver")) { - result = new tpolyhedra_domaint::templ_valuet(); - strategy_solver = new BINSEARCH_SOLVER; + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new BINSEARCH_SOLVER; } - else assert(false); + else + assert(false); } strategy_solver->set_message_handler(get_message_handler()); - unsigned iteration_number=0; - // initialize inv domain->initialize(*result); - bool change; + strategy_solver_baset::progresst status; do { - iteration_number++; - - #ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; - #endif - - change = strategy_solver->iterate(*result); - - if(change) - { + status=strategy_solver->iterate(*result); + } + while(status==strategy_solver_baset::CHANGED); - #ifdef DEBUG - std::cout << "Value after " << iteration_number - << " iteration(s):\n"; - domain->output_value(std::cout,*result,SSA.ns); - #endif + // get status of assertions + if(!assertions_check.is_false() && + status==strategy_solver_baset::FAILED) + { + nonpassed_assertionst::iterator it=nonpassed_assertions.begin(); + for(unsigned i=0; il_get(assertion_literals[i]).is_true()) + nonpassed_assertions.erase(it++); + else + ++it; } } - while(change); - - #ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - domain->output_value(std::cout,*result,SSA.ns); - #endif + else + nonpassed_assertions.clear(); solver.pop_context(); - //statistics - solver_instances += strategy_solver->get_number_of_solver_instances(); - solver_calls += strategy_solver->get_number_of_solver_calls(); - solver_instances += strategy_solver->get_number_of_solver_instances(); + // statistics + solver_calls+=strategy_solver->get_number_of_solver_calls(); + solver_instances+=strategy_solver->get_number_of_solver_instances(); delete strategy_solver; + + return (status==strategy_solver_baset::CONVERGED); } /*******************************************************************\ @@ -189,5 +211,5 @@ Function: ssa_analyzert::get_result void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) { - domain->project_on_vars(*result,vars,_result); + domain->project_on_vars(*result, vars, _result); } diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index ae848b03a..35cc63626 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -1,58 +1,67 @@ /*******************************************************************\ -Module: Data Flow Analysis +Module: SSA Analyzer -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_SSA_ANALYZER_H -#define DELTACHECK_SSA_ANALYZER_H +#ifndef CPROVER_2LS_DOMAINS_SSA_ANALYZER_H +#define CPROVER_2LS_DOMAINS_SSA_ANALYZER_H #include -#include "../ssa/local_ssa.h" +#include + #include "strategy_solver_base.h" #include "template_generator_base.h" -class ssa_analyzert : public messaget +class ssa_analyzert:public messaget { public: typedef strategy_solver_baset::constraintst constraintst; typedef strategy_solver_baset::var_listt var_listt; - explicit ssa_analyzert() - : - result(NULL), + ssa_analyzert(): + result(nullptr), solver_instances(0), solver_calls(0) - { - } - - ~ssa_analyzert() - { - if(result!=NULL) delete result; - } - - void operator()(incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator); - + { + } + + ~ssa_analyzert() + { + if(result!=nullptr) + delete result; + } + + bool operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions=false); + + // retrieve the result if operator() returned true void get_result(exprt &result, const domaint::var_sett &vars); - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } + // retrieve the non-passed assertions if operator() returned false + typedef std::list + nonpassed_assertionst; + nonpassed_assertionst get_nonpassed_assertions() + { return nonpassed_assertions; } + + inline unsigned get_number_of_solver_instances() { return solver_instances; } + inline unsigned get_number_of_solver_calls() { return solver_calls; } protected: - domaint *domain; //template generator is responsable for the domain object + domaint *domain; // template generator is responsable for the domain object domaint::valuet *result; + nonpassed_assertionst nonpassed_assertions; - //statistics + // statistics unsigned solver_instances; unsigned solver_calls; }; - #endif - diff --git a/src/domains/ssa_fixed_point.cpp b/src/domains/ssa_fixed_point.cpp deleted file mode 100644 index 8e592b7d5..000000000 --- a/src/domains/ssa_fixed_point.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" -#include "ssa_fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: ssa_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_point(local_SSAt &SSA) -{ - if(SSA.goto_function.body.instructions.empty()) - return; - - fixed_pointt fixed_point(SSA.ns); - - // get all backwards edges - - forall_goto_program_instructions(i_it, SSA.goto_function.body) - { - if(i_it->is_backwards_goto()) - { - // Record the objects modified by the loop to get - // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, i_it); - symbol_exprt out=SSA.read_rhs(*o_it, i_it); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - - { - ssa_objectt guard=SSA.guard_symbol(); - symbol_exprt in=SSA.name(guard, local_SSAt::LOOP_BACK, i_it); - symbol_exprt out=SSA.name(guard, local_SSAt::OUT, i_it); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - } - } - - // transition relation - fixed_point.transition_relation << SSA; - - // kick off fixed-point computation - fixed_point(); - - // Add fixed-point as constraints back into SSA. - // We simply use the last CFG node. It would be prettier to put - // these close to the loops. - assert(SSA.nodes.begin()!=SSA.nodes.end()); - fixed_point.state_predicate.get_constraints(SSA.nodes.back().constraints); -} diff --git a/src/domains/ssa_fixed_point.h b/src/domains/ssa_fixed_point.h deleted file mode 100644 index ac197e75a..000000000 --- a/src/domains/ssa_fixed_point.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SSA_FIXED_POINT_H -#define DELTACHECK_SSA_FIXED_POINT_H - -#include "../ssa/local_ssa.h" - -void ssa_fixed_point(local_SSAt &); - -#endif diff --git a/src/domains/strategy_solver_base.cpp b/src/domains/strategy_solver_base.cpp index 4b6ce48ea..edf2329ba 100644 --- a/src/domains/strategy_solver_base.cpp +++ b/src/domains/strategy_solver_base.cpp @@ -1,3 +1,10 @@ +/*******************************************************************\ + +Module: Strategy iteration solver base class + +Author: Peter Schrammel + +\*******************************************************************/ #include "strategy_solver_base.h" diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index 9c35a1d01..1244a7bdc 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -1,47 +1,52 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BASE_H -#define CPROVER_STRATEGY_SOLVER_BASE_H +/*******************************************************************\ -#include -#include +Module: Strategy iteration solver base class -#include +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BASE_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BASE_H #include "domain.h" +#include "incremental_solver.h" #include "util.h" -#include "../domains/incremental_solver.h" -class strategy_solver_baset : public messaget +class strategy_solver_baset:public messaget { - public: typedef std::list constraintst; typedef std::vector var_listt; typedef domaint::valuet invariantt; + typedef enum {CHANGED, CONVERGED, FAILED} progresst; explicit strategy_solver_baset( - incremental_solvert &_solver, - const namespacet &_ns) : - solver(_solver), + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + solver(_solver), + assertion_check(_assertion_check), ns(_ns), solver_instances(0), solver_calls(0) {} - virtual bool iterate(invariantt &inv) { assert(false); } + virtual progresst iterate(invariantt &inv) { assert(false); } unsigned get_number_of_solver_calls() { return solver_calls; } unsigned get_number_of_solver_instances() { return solver_instances; } - protected: + protected: incremental_solvert &solver; - + literalt assertion_check; const namespacet &ns; - //handles on values to retrieve from model + // handles on values to retrieve from model bvt strategy_cond_literals; exprt::operandst strategy_value_exprs; - //statistics for additional solvers + // statistics for additional solvers unsigned solver_instances; unsigned solver_calls; }; diff --git a/src/domains/strategy_solver_binsearch.cpp b/src/domains/strategy_solver_binsearch.cpp index 690b49ef5..687eed4da 100644 --- a/src/domains/strategy_solver_binsearch.cpp +++ b/src/domains/strategy_solver_binsearch.cpp @@ -1,133 +1,172 @@ +/*******************************************************************\ + +Module: Simplified strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include "strategy_solver_binsearch.h" #include "util.h" -bool strategy_solver_binsearcht::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_binsearcht::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +strategy_solver_baset::progresst strategy_solver_binsearcht::iterate( + invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved = false; + progresst progress=CONVERGED; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + // solver.set_frozen(strategy_cond_literals[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } #if 0 debug() << eom; #endif - solver << disjunction(strategy_cond_exprs); + solver << or_exprt(disjunction(strategy_cond_exprs), + literal_exprt(assertion_check)); #if 0 debug() << "solve(): "; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - + #if 0 - for(unsigned i=0; i improve_rows; improve_rows.insert(row); - tpolyhedra_domaint::row_valuet upper = + tpolyhedra_domaint::row_valuet upper= tpolyhedra_domain.get_max_row_value(row); - tpolyhedra_domaint::row_valuet lower = + tpolyhedra_domaint::row_valuet lower= simplify_const(solver.get(strategy_value_exprs[row])); - solver.pop_context(); //improvement check - - solver.new_context(); //symbolic value system + solver.pop_context(); // improvement check + + solver.new_context(); // symbolic value system - exprt pre_inv_expr = - tpolyhedra_domain.to_symb_pre_constraints(inv,improve_rows); + exprt pre_inv_expr= + tpolyhedra_domain.to_symb_pre_constraints(inv, improve_rows); solver << pre_inv_expr; - exprt post_inv_expr = tpolyhedra_domain.get_row_symb_post_constraint(row); + exprt post_inv_expr=tpolyhedra_domain.get_row_symb_post_constraint(row); solver << post_inv_expr; #if 0 debug() << "symbolic value system: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",pre_inv_expr) << eom; - debug() << "post-inv: " << from_expr(ns,"",post_inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns, "", post_inv_expr) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; // row_symb_value >= middle - exprt c = tpolyhedra_domain.get_row_symb_value_constraint(row,middle,true); + exprt c= + tpolyhedra_domain.get_row_symb_value_constraint(row, middle, true); #if 0 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif solver.new_context(); // binary search iteration @@ -138,87 +177,83 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - + #if 0 - for(unsigned i=0; ifirst) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " - << from_expr(ns, "", solver.get(it->second)) << eom; - } +#if 0 + for(const auto &rm : renaming_map) + { + debug() << "replace_map (1st): " + << from_expr(ns, "", rm.first) << " " + << from_expr(ns, "", solver.get(rm.first)) << eom; + debug() << "replace_map (2nd): " + << from_expr(ns, "", rm.second) << " " + << from_expr(ns, "", solver.get(rm.second)) << eom; + } #endif - - lower = simplify_const( - solver.get(tpolyhedra_domain.get_row_symb_value(row))); + + lower=simplify_const( + solver.get(tpolyhedra_domain.get_row_symb_value(row))); } - else + else { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif #if 0 - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; - else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + if(solver.solver->is_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; } #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; - upper = middle; + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; + upper=middle; } solver.pop_context(); // binary search iteration } - - debug() << "update value: " << from_expr(ns,"",lower) << eom; - solver.pop_context(); //symbolic value system + debug() << "update value: " << from_expr(ns, "", lower) << eom; - tpolyhedra_domain.set_row_value(row,lower,inv); - improved = true; + tpolyhedra_domain.set_row_value(row, lower, inv); + progress=CHANGED; } - else + else { #if 0 debug() << "UNSAT" << eom; #endif #ifdef DEBUG_FORMULA - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; + debug() << "is_in_conflict: " << solver.formula[i] << eom; else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + debug() << "not_in_conflict: " << solver.formula[i] << eom; } #endif - solver.pop_context(); //improvement check + solver.pop_context(); // improvement check } - - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch.h b/src/domains/strategy_solver_binsearch.h index 89d4b1041..e47c6cfef 100644 --- a/src/domains/strategy_solver_binsearch.h +++ b/src/domains/strategy_solver_binsearch.h @@ -1,24 +1,34 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH_H +/*******************************************************************\ + +Module: Simplified strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearcht : public strategy_solver_baset +class strategy_solver_binsearcht:public strategy_solver_baset { - public: - explicit strategy_solver_binsearcht( +public: + strategy_solver_binsearcht( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, - const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), - tpolyhedra_domain(_tpolyhedra_domain) {} + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), + tpolyhedra_domain(_tpolyhedra_domain) + { + } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); - protected: +protected: tpolyhedra_domaint &tpolyhedra_domain; - }; #endif diff --git a/src/domains/strategy_solver_binsearch2.cpp b/src/domains/strategy_solver_binsearch2.cpp index ad611345f..d97bb40a8 100644 --- a/src/domains/strategy_solver_binsearch2.cpp +++ b/src/domains/strategy_solver_binsearch2.cpp @@ -1,4 +1,16 @@ +/*******************************************************************\ + +Module: Strategy iteration solver by binary search + with optimisation of the parameter sum + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include @@ -6,44 +18,56 @@ #include "strategy_solver_binsearch2.h" #include "util.h" - #define SUM_BOUND_VAR "sum_bound#" -bool strategy_solver_binsearch2t::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_binsearch2t::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +strategy_solver_binsearch2t::progresst +strategy_solver_binsearch2t::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved = false; + progresst progress=CONVERGED; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + // solver.set_frozen(strategy_cond_literals[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } debug() << eom; @@ -53,165 +77,169 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) debug() << "solve(): "; #endif - std::map symb_values; - std::map lower_values; + std::map symb_values; + std::map lower_values; exprt::operandst blocking_constraint; std::set improve_rows; - bool improved_from_neginf = false; - while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + bool improved_from_neginf=false; + while(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - improved = true; + progress=CHANGED; - unsigned row=0; - for(;row=1); - std::map::iterator - it = symb_values.begin(); - exprt _lower = lower_values[it->first]; + std::map::iterator + it=symb_values.begin(); + exprt _lower=lower_values[it->first]; #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); - exprt _upper = + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); + exprt _upper= tpolyhedra_domain.get_max_row_value(it->first); - exprt sum = it->second; - for(it++; it != symb_values.end(); it++) + exprt sum=it->second; + for(++it; it!=symb_values.end(); ++it) { - sum = plus_exprt(sum,it->second); - _upper = plus_exprt(_upper,tpolyhedra_domain.get_max_row_value(it->first)); - _lower = plus_exprt(_lower,lower_values[it->first]); + sum=plus_exprt(sum, it->second); + _upper=plus_exprt(_upper, tpolyhedra_domain.get_max_row_value(it->first)); + _lower=plus_exprt(_lower, lower_values[it->first]); #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); } - - //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) return improved; - solver.new_context(); //symbolic value system + // do not solve system if we have just reached a new loop + // (the system will be very large!) + if(improved_from_neginf) + return progress; + + solver.new_context(); // symbolic value system solver << pre_inv_expr; solver << post_inv_expr; extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); - tpolyhedra_domaint::row_valuet upper = simplify_const(_upper); - //from_integer(mp_integer(512),_upper.type()); - tpolyhedra_domaint::row_valuet lower = simplify_const(_lower); + tpolyhedra_domaint::row_valuet upper=simplify_const(_upper); + tpolyhedra_domaint::row_valuet lower=simplify_const(_lower); assert(sum.type()==upper.type()); assert(sum.type()==lower.type()); - symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << equal_exprt(sum_bound,sum); + symbol_exprt sum_bound( + SUM_BOUND_VAR+i2string(sum_bound_counter++), + sum.type()); + solver << equal_exprt(sum_bound, sum); #if 0 - debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; + debug() << from_expr(ns, "", equal_exprt(sum_bound, sum)) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; - // row_symb_value >= middle - assert(sum_bound.type()==middle.type()); - exprt c = binary_relation_exprt(sum_bound,ID_ge,middle); + // row_symb_value >= middle + assert(sum_bound.type()==middle.type()); + exprt c=binary_relation_exprt(sum_bound, ID_ge, middle); #if 0 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif - solver.new_context(); // binary search iteration + solver.new_context(); // binary search iteration #if 0 - debug() << "constraint: " << from_expr(ns, "", c) << eom; + debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << c; + solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - - lower = middle; - for(std::map::iterator - it = symb_values.begin(); it != symb_values.end(); it++) - { + lower=middle; + + for(const auto &sv : symb_values) + { #if 1 - debug() << "update row " << it->first << " " - << from_expr(ns,"",it->second) << ": "; + debug() << "update row " << sv.first << " " + << from_expr(ns, "", sv.second) << ": "; #endif - constant_exprt lower_row = - simplify_const(solver.get(it->second)); + constant_exprt lower_row= + simplify_const(solver.get(sv.second)); #if 1 - debug() << from_expr(ns,"",lower_row) << eom; + debug() << from_expr(ns, "", lower_row) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_row,inv); - } - } - else - { + tpolyhedra_domain.set_row_value(sv.first, lower_row, inv); + } + } + else + { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; - upper = middle; - } - solver.pop_context(); // binary search iteration - } + upper=middle; + } + solver.pop_context(); // binary search iteration + } - solver.pop_context(); //symbolic value system + solver.pop_context(); // symbolic value system - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch2.h b/src/domains/strategy_solver_binsearch2.h index e15cb1971..0692418a6 100644 --- a/src/domains/strategy_solver_binsearch2.h +++ b/src/domains/strategy_solver_binsearch2.h @@ -1,27 +1,37 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH2_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH2_H +/*******************************************************************\ + +Module: Strategy iteration solver by binary search + with optimisation of the parameter sum + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH2_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH2_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearch2t : public strategy_solver_baset +class strategy_solver_binsearch2t:public strategy_solver_baset { public: - explicit strategy_solver_binsearch2t( + strategy_solver_binsearch2t( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, - const namespacet &_ns) : - strategy_solver_baset( _solver, _ns), + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), tpolyhedra_domain(_tpolyhedra_domain), - sum_bound_counter(0) {} + sum_bound_counter(0) + { + } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; unsigned sum_bound_counter; - }; - -#endif +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH2_H diff --git a/src/domains/strategy_solver_binsearch3.cpp b/src/domains/strategy_solver_binsearch3.cpp index 91e294d97..8be4c7136 100644 --- a/src/domains/strategy_solver_binsearch3.cpp +++ b/src/domains/strategy_solver_binsearch3.cpp @@ -1,4 +1,15 @@ +/*******************************************************************\ + +Module: Full strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include @@ -6,44 +17,55 @@ #include "strategy_solver_binsearch3.h" #include "util.h" - #define SUM_BOUND_VAR "sum_bound#" -bool strategy_solver_binsearch3t::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_binsearch3t::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +strategy_solver_baset::progresst +strategy_solver_binsearch3t::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved = false; + progresst progress=CONVERGED; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } debug() << eom; @@ -54,201 +76,173 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) #endif std::set improve_rows; - std::map symb_values; - std::map lower_values; + std::map symb_values; + std::map lower_values; exprt::operandst blocking_constraint; - bool improved_from_neginf = false; - while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + bool improved_from_neginf=false; + while(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - improved = true; + progress=CHANGED; - unsigned row=0; - for(;row=1); - std::map::iterator - it = symb_values.begin(); - exprt _lower = lower_values[it->first]; + std::map::iterator + it=symb_values.begin(); + exprt _lower=lower_values[it->first]; #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); - exprt _upper = + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); + exprt _upper= tpolyhedra_domain.get_max_row_value(it->first); - exprt sum = it->second; - for(it++; it != symb_values.end(); it++) + exprt sum=it->second; + for(++it; it!=symb_values.end(); ++it) { - sum = plus_exprt(sum,it->second); - _upper = plus_exprt(_upper,tpolyhedra_domain.get_max_row_value(it->first)); - _lower = plus_exprt(_lower,lower_values[it->first]); + sum=plus_exprt(sum, it->second); + _upper=plus_exprt(_upper, tpolyhedra_domain.get_max_row_value(it->first)); + _lower=plus_exprt(_lower, lower_values[it->first]); #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); } - - //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) return improved; - solver.new_context(); //symbolic value system + // do not solve system if we have just reached a new loop + // (the system will be very large!) + if(improved_from_neginf) + return progress; + + solver.new_context(); // symbolic value system solver << pre_inv_expr; solver << post_inv_expr; #if 1 debug() << "symbolic value system: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",pre_inv_expr) << eom; - debug() << "post-inv: " << from_expr(ns,"",post_inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns, "", post_inv_expr) << eom; #endif -/* - //add renamed SSA for rows 1..n-1 - SSA.unmark_nodes(); - for(unsigned i=1; i program; - program << SSA; - for(std::list::iterator it = program.begin(); - it != program.end(); it++) - { -// tpolyhedra_domain.rename_for_row(*it,i); -#if 1 - debug() << "ssa " << i << ": " << from_expr(ns,"",*it) << eom; -#endif - } - solver << SSA; - } - SSA.mark_nodes(); -*/ extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); - tpolyhedra_domaint::row_valuet upper = simplify_const(_upper); - tpolyhedra_domaint::row_valuet lower = simplify_const(_lower); + tpolyhedra_domaint::row_valuet upper=simplify_const(_upper); + tpolyhedra_domaint::row_valuet lower=simplify_const(_lower); assert(sum.type()==upper.type()); assert(sum.type()==lower.type()); - symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << equal_exprt(sum_bound,sum); + symbol_exprt sum_bound( + SUM_BOUND_VAR+i2string(sum_bound_counter++), + sum.type()); + solver << equal_exprt(sum_bound, sum); #if 1 - debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; + debug() << from_expr(ns, "", equal_exprt(sum_bound, sum)) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; - // row_symb_value >= middle - assert(sum_bound.type()==middle.type()); - exprt c = binary_relation_exprt(sum_bound,ID_ge,middle); + // row_symb_value >= middle + assert(sum_bound.type()==middle.type()); + exprt c=binary_relation_exprt(sum_bound, ID_ge, middle); #if 1 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif - solver.new_context(); // binary search iteration + solver.new_context(); // binary search iteration #if 1 - debug() << "constraint: " << from_expr(ns, "", c) << eom; + debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << c; + solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - - lower = middle; - for(std::map::iterator - it = symb_values.begin(); it != symb_values.end(); it++) - { + lower=middle; + + for(const auto &sv : symb_values) + { #if 1 - debug() << "update row " << it->first << " " - << from_expr(ns,"",it->second) << ": "; + debug() << "update row " << sv.first << " " + << from_expr(ns, "", sv.second) << ": "; #endif - constant_exprt lower_row = - simplify_const(solver.get(it->second)); + constant_exprt lower_row= + simplify_const(solver.get(sv.second)); #if 1 - debug() << from_expr(ns,"",lower_row) << eom; + debug() << from_expr(ns, "", lower_row) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_row,inv); - } - } - else - { + tpolyhedra_domain.set_row_value(sv.first, lower_row, inv); + } + } + else + { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; - - upper = middle; - } - solver.pop_context(); // binary search iteration - } + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; - solver.pop_context(); //symbolic value system + upper=middle; + } + solver.pop_context(); // binary search iteration + } + solver.pop_context(); // symbolic value system - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch3.h b/src/domains/strategy_solver_binsearch3.h index 3a2578850..a7af92f94 100644 --- a/src/domains/strategy_solver_binsearch3.h +++ b/src/domains/strategy_solver_binsearch3.h @@ -1,33 +1,39 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH3_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH3_H +/*******************************************************************\ + +Module: Full strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H + +#include -#include "../ssa/local_ssa.h" #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearch3t : public strategy_solver_baset +class strategy_solver_binsearch3t:public strategy_solver_baset { public: explicit strategy_solver_binsearch3t( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, local_SSAt& _SSA, - const namespacet &_ns) : - strategy_solver_baset( _solver, _ns), + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), SSA(_SSA), tpolyhedra_domain(_tpolyhedra_domain), sum_bound_counter(0) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: local_SSAt &SSA; tpolyhedra_domaint &tpolyhedra_domain; unsigned sum_bound_counter; -// std::set improve_rows; -// std::map symb_values; -// std::map lower_values; - }; -#endif +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H diff --git a/src/domains/strategy_solver_enumeration.cpp b/src/domains/strategy_solver_enumeration.cpp index 11d72a834..7d6f3e976 100644 --- a/src/domains/strategy_solver_enumeration.cpp +++ b/src/domains/strategy_solver_enumeration.cpp @@ -1,4 +1,14 @@ +/*******************************************************************\ + +Module: Synthesis by enumeration of counterexamples + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -6,144 +16,162 @@ #include "strategy_solver_enumeration.h" #include "util.h" -bool strategy_solver_enumerationt::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +strategy_solver_baset::progresst strategy_solver_enumerationt::iterate( + invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved = false; + progresst progress=CONVERGED; solver.new_context(); - exprt preinv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt preinv_expr=tpolyhedra_domain.to_pre_constraints(inv); #ifdef DEBUG_OUTPUT - debug() << "pre-inv: " << from_expr(ns,"",preinv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", preinv_expr) << eom; #endif solver << preinv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - - exprt postinv_expr = disjunction(strategy_cond_exprs); + + exprt postinv_expr=disjunction(strategy_cond_exprs); #ifdef DEBUG_OUTPUT debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]) ; + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } #ifdef DEBUG_OUTPUT debug() << eom; #endif - solver << disjunction(strategy_cond_exprs); + solver << or_exprt(disjunction(strategy_cond_exprs), + literal_exprt(assertion_check)); #ifdef DEBUG_OUTPUT debug() << "solve(): "; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #ifdef DEBUG_OUTPUT debug() << "SAT" << eom; #endif - + #ifdef DEBUG_OUTPUT - for(unsigned i=0; i vars; - find_symbols(preinv_expr,vars); + find_symbols(preinv_expr, vars); - for(std::set::const_iterator - it=vars.begin(); - it!=vars.end(); - ++it) + for(const auto &var : vars) { - debug() << "var: " << from_expr(ns, "", *it) << " = " << - from_expr(ns, "", solver.get(*it)) << eom; + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; } } - for(unsigned i=0; i vars; - find_symbols(strategy_value_exprs[i],vars); + find_symbols(strategy_value_exprs[i], vars); - for(std::set::const_iterator - it=vars.begin(); - it!=vars.end(); - ++it) + for(const auto &var : vars) { - debug() << "var: " << from_expr(ns, "", *it) << " = " << - from_expr(ns, "", solver.get(*it)) << eom; + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; } } #endif - - for(unsigned row=0;rowis_in_conflict(solver.formula[i])) debug() << "is_in_conflict: " << solver.formula[i] << eom; else debug() << "not_in_conflict: " << solver.formula[i] << eom; - } -#endif + } +#endif } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/strategy_solver_enumeration.h b/src/domains/strategy_solver_enumeration.h index 4bf6ebcc6..4383e4d0e 100644 --- a/src/domains/strategy_solver_enumeration.h +++ b/src/domains/strategy_solver_enumeration.h @@ -1,24 +1,34 @@ -#ifndef CPROVER_STRATEGY_SOLVER_ENUMERATION_H -#define CPROVER_STRATEGY_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Synthesis by enumeration of counterexamples + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_ENUMERATION_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_enumerationt : public strategy_solver_baset +class strategy_solver_enumerationt:public strategy_solver_baset { - public: - explicit strategy_solver_enumerationt( +public: + strategy_solver_enumerationt( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, - const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), - tpolyhedra_domain(_tpolyhedra_domain) {} + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), + tpolyhedra_domain(_tpolyhedra_domain) + { + } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); - protected: +protected: tpolyhedra_domaint &tpolyhedra_domain; - }; #endif diff --git a/src/domains/strategy_solver_equality.cpp b/src/domains/strategy_solver_equality.cpp index 417e1525a..43602e881 100644 --- a/src/domains/strategy_solver_equality.cpp +++ b/src/domains/strategy_solver_equality.cpp @@ -1,24 +1,47 @@ +/*******************************************************************\ + +Module: Solver for equalities/disequalities domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include "strategy_solver_equality.h" -bool strategy_solver_equalityt::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_equalityt::iterate + + Inputs: + + Outputs: + + Purpose: Comment: assertion check is not possible + because this is a gfp solver + +\*******************************************************************/ + +strategy_solver_baset::progresst strategy_solver_equalityt::iterate(invariantt &_inv) { - equality_domaint::equ_valuet &inv = + equality_domaint::equ_valuet &inv= static_cast(_inv); - worklistt::iterator e_it = todo_equs.begin(); - if(e_it!=todo_equs.end()) //check equalities + worklistt::iterator e_it=todo_equs.begin(); + if(e_it!=todo_equs.end()) // check equalities { solver.new_context(); - exprt pre_expr = equality_domain.get_pre_equ_constraint(*e_it); + exprt pre_expr=equality_domain.get_pre_equ_constraint(*e_it); solver << pre_expr; - - exprt post_expr = equality_domain.get_post_not_equ_constraint(*e_it); - literalt cond_literal = solver.convert(post_expr); + + exprt post_expr=equality_domain.get_post_not_equ_constraint(*e_it); + literalt cond_literal=solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -28,8 +51,8 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) debug() << "Post: " << from_expr(ns, "", post_expr) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 debug() << "SAT" << eom; #endif @@ -37,53 +60,40 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) solver.pop_context(); } - else //equality holds + else // equality holds { #if 0 debug() << "UNSAT" << eom; #endif - - equality_domain.set_equal(*e_it,inv); + + equality_domain.set_equal(*e_it, inv); solver.pop_context(); - solver << pre_expr; //make permanent + solver << pre_expr; // make permanent - //due to transitivity, we would like to recheck equalities that did not hold - todo_equs.insert(todo_disequs.begin(),todo_disequs.end()); + // due to transitivity, we have to recheck equalities + // that did not hold + todo_equs.insert(todo_disequs.begin(), todo_disequs.end()); todo_disequs.clear(); } - todo_equs.erase(e_it); - - //check status of remaining equalities - /* worklistt rm_equs; - for(e_it = todo_equs.begin(); e_it!=todo_equs.end(); e_it++) - { - equality_domaint::var_pairt vv = equality_domain.get_var_pair(*e_it); - if(solver.get(vv.first)!=solver.get(vv.second)) - rm_equs.insert(*e_it); - } - for(e_it = rm_equs.begin(); e_it!=rm_equs.end(); e_it++) - { - todo_disequs.insert(*e_it); - todo_equs.erase(*e_it); - } */ } - else //check disequalities + else // check disequalities { - e_it = todo_disequs.begin(); - if(e_it==todo_disequs.end()) return false; //done + e_it=todo_disequs.begin(); + if(e_it==todo_disequs.end()) + return CONVERGED; // done solver.new_context(); - exprt pre_expr = equality_domain.get_pre_disequ_constraint(*e_it); + exprt pre_expr=equality_domain.get_pre_disequ_constraint(*e_it); solver << pre_expr; - - exprt post_expr = equality_domain.get_post_not_disequ_constraint(*e_it); - literalt cond_literal = solver.convert(post_expr); + + exprt post_expr=equality_domain.get_post_not_disequ_constraint(*e_it); + literalt cond_literal=solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -93,19 +103,19 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) debug() << "Post: " << from_expr(ns, "", post_expr) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 debug() << "SAT" << eom; -#endif +#endif } - else //equality holds + else // equality holds { #if 0 debug() << "UNSAT" << eom; -#endif - equality_domain.set_disequal(*e_it,inv); - solver << pre_expr; //make permanent +#endif + equality_domain.set_disequal(*e_it, inv); + solver << pre_expr; // make permanent } solver.pop_context(); @@ -113,5 +123,5 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) todo_disequs.erase(e_it); } - return true; + return CHANGED; } diff --git a/src/domains/strategy_solver_equality.h b/src/domains/strategy_solver_equality.h index 115ef7037..ae011cdf9 100644 --- a/src/domains/strategy_solver_equality.h +++ b/src/domains/strategy_solver_equality.h @@ -1,23 +1,32 @@ -#ifndef CPROVER_STRATEGY_SOLVER_EQUALITY_H -#define CPROVER_STRATEGY_SOLVER_EQUALITY_H +/*******************************************************************\ + +Module: Solver for equalities/disequalities domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_EQUALITY_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_EQUALITY_H #include "strategy_solver_base.h" #include "equality_domain.h" -class strategy_solver_equalityt : public strategy_solver_baset +class strategy_solver_equalityt:public strategy_solver_baset { - public: - explicit strategy_solver_equalityt( +public: + strategy_solver_equalityt( equality_domaint &_equality_domain, - incremental_solvert &_solver, - const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), equality_domain(_equality_domain) { equality_domain.get_index_set(todo_equs); } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: equality_domaint &equality_domain; diff --git a/src/domains/strategy_solver_predabs.cpp b/src/domains/strategy_solver_predabs.cpp index d439a7ca4..353c0ea4f 100644 --- a/src/domains/strategy_solver_predabs.cpp +++ b/src/domains/strategy_solver_predabs.cpp @@ -1,78 +1,104 @@ +/*******************************************************************\ + +Module: Solver for predicate abstraction domain + +Author: Peter Schrammel, Cristina David + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include + #include "strategy_solver_predabs.h" -bool strategy_solver_predabst::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_predabst::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +strategy_solver_predabst::progresst +strategy_solver_predabst::iterate(invariantt &_inv) { - predabs_domaint::templ_valuet &inv = + predabs_domaint::templ_valuet &inv= static_cast(_inv); - worklistt::iterator e_it = todo_preds.begin(); - if(e_it != todo_preds.end()) //check positive preds - { - solver.new_context(); - exprt preinv_expr = predabs_domain.get_row_pre_constraint(*e_it, true_exprt()); + worklistt::iterator e_it=todo_preds.begin(); + if(e_it!=todo_preds.end()) // check positive preds + { + solver.new_context(); + exprt preinv_expr= + predabs_domain.get_row_pre_constraint(*e_it, true_exprt()); #ifdef DEBUG_OUTPUT - debug() << "pre-pred: " << from_expr(ns,"",preinv_expr) << eom; + debug() << "pre-pred: " << from_expr(ns, "", preinv_expr) << eom; #endif - solver << preinv_expr; - - exprt strategy_cond_expr; - strategy_cond_expr = predabs_domain.get_row_post_constraint(*e_it, true_exprt()); + solver << preinv_expr; + + exprt strategy_cond_expr; + strategy_cond_expr= + predabs_domain.get_row_post_constraint(*e_it, true_exprt()); - literalt cond_literal = solver.convert(not_exprt(strategy_cond_expr)); - solver << literal_exprt(cond_literal); + literalt cond_literal=solver.convert(not_exprt(strategy_cond_expr)); + solver << literal_exprt(cond_literal); #ifdef DEBUG_OUTPUT - debug() << "post-pred: " << from_expr(ns,"",not_exprt(strategy_cond_expr)) << eom; + debug() << "post-pred: " + << from_expr(ns, "", not_exprt(strategy_cond_expr)) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { - debug() << "SAT" << eom; - -#if 0 - for(replace_mapt::const_iterator - it=predabs_domain.renaming_map.begin(); - it!=predabs_domain.renaming_map.end(); - ++it) - { - debug() << "replace_map (1st): " << - from_expr(ns, "", it->first) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " - << from_expr(ns, "", solver.get(it->second)) << eom; - } + if(solver()==decision_proceduret::D_SATISFIABLE) + { + debug() << "SAT" << eom; + +#if 0 + for(replace_mapt::const_iterator + it=predabs_domain.renaming_map.begin(); + it!=predabs_domain.renaming_map.end(); + ++it) + { + debug() << "replace_map (1st): " << + from_expr(ns, "", it->first) << " " << + from_expr(ns, "", solver.get(it->first)) << eom; + debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) + << " " << from_expr(ns, "", solver.get(it->second)) << eom; + } #endif - todo_notpreds.insert(*e_it); - - solver.pop_context(); + todo_notpreds.insert(*e_it); - } - else - { - - debug() << "UNSAT" << eom; + solver.pop_context(); + } + else + { + debug() << "UNSAT" << eom; - predabs_domain.set_row_value(*e_it, true_exprt(), inv); + predabs_domain.set_row_value(*e_it, true_exprt(), inv); - solver.pop_context(); + solver.pop_context(); - solver << preinv_expr; //make permanent + solver << preinv_expr; // make permanent - //due to transitivity, we would like to recheck predicates that did not hold - todo_preds.insert(todo_notpreds.begin(),todo_notpreds.end()); - todo_notpreds.clear(); - } + // due to transitivity, we would like to + // recheck predicates that did not hold + todo_preds.insert(todo_notpreds.begin(), todo_notpreds.end()); + todo_notpreds.clear(); + } - todo_preds.erase(e_it); + todo_preds.erase(e_it); - return true; - } + return CHANGED; + } - return false; + return CONVERGED; } diff --git a/src/domains/strategy_solver_predabs.h b/src/domains/strategy_solver_predabs.h index 28a30d7a9..b7ec0695d 100644 --- a/src/domains/strategy_solver_predabs.h +++ b/src/domains/strategy_solver_predabs.h @@ -1,23 +1,32 @@ -#ifndef CPROVER_STRATEGY_SOLVER_PREDABS_H -#define CPROVER_STRATEGY_SOLVER_PREDABS_H +/*******************************************************************\ + +Module: Solver for predicate abstraction domain + +Author: Peter Schrammel, Cristina David + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_PREDABS_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_PREDABS_H #include "strategy_solver_base.h" #include "predabs_domain.h" -class strategy_solver_predabst : public strategy_solver_baset +class strategy_solver_predabst:public strategy_solver_baset { public: explicit strategy_solver_predabst( predabs_domaint &_predabs_domain, - incremental_solvert &_solver, - const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), predabs_domain(_predabs_domain) { predabs_domain.get_row_set(todo_preds); } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: predabs_domaint &predabs_domain; @@ -25,8 +34,6 @@ class strategy_solver_predabst : public strategy_solver_baset typedef std::set worklistt; worklistt todo_preds; worklistt todo_notpreds; - - }; #endif diff --git a/src/domains/template_domain.cpp b/src/domains/template_domain.cpp deleted file mode 100644 index 4b010ccaf..000000000 --- a/src/domains/template_domain.cpp +++ /dev/null @@ -1,1300 +0,0 @@ -#include "template_domain.h" - -#include - -#include -#include -#include -#include - -#define SYMB_BOUND_VAR "symb_bound#" - -/*******************************************************************\ - -Function: template_domaint::initialize - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::initialize(valuet &value) -{ - templ_valuet &v = static_cast(value); - v.resize(templ.size()); - for(unsigned row = 0; row=vlower); - if(vlower+1==vupper) return from_integer(vlower,lower.type()); //floor - return from_integer((vupper+vlower)/2,lower.type()); - } - if(lower.type().id()==ID_floatbv && upper.type().id()==ID_floatbv) - { - ieee_floatt vlower(to_constant_expr(lower)); - ieee_floatt vupper(to_constant_expr(upper)); - if(vlower.get_sign()==vupper.get_sign()) - { - mp_integer plower = vlower.pack(); //compute "median" float number - mp_integer pupper = vupper.pack(); - //assert(pupper>=plower); - ieee_floatt res; - res.unpack((plower+pupper)/2); //...by computing integer mean - return res.to_expr(); - } - ieee_floatt res; - res.make_zero(); - return res.to_expr(); - } - assert(false); //types do not match or are not supported -} - -/*******************************************************************\ - -Function: template_domaint::leq - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::less_than(const row_valuet &v1, const row_valuet &v2) -{ - if(v1.type()==v2.type() && - (v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv)) - { - mp_integer vv1, vv2; - to_integer(v1, vv1); - to_integer(v2, vv2); - return vv1 row_expr <= row_value - -\*******************************************************************/ - -exprt template_domaint::get_row_constraint(const rowt &row, - const row_valuet &row_value) -{ - assert(row (row_expr <= row_value) ) - -\*******************************************************************/ - -exprt template_domaint::to_pre_constraints(const templ_valuet &value) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= row_value)) - to be connected disjunctively - -\*******************************************************************/ - -void template_domaint::make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, - exprt::operandst &value_exprs) -{ - assert(value.size()==templ.size()); - cond_exprs.resize(templ.size()); - value_exprs.resize(templ.size()); - - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_value) - -\*******************************************************************/ - -exprt template_domaint::get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value) -{ - assert(row= row_symb_value) (!!!) - -\*******************************************************************/ - -exprt template_domaint::get_row_symb_post_constraint(const rowt &row) -{ - assert(row (row_expr <= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_pre_constraints(const templ_valuet &value) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr >= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_post_constraints() -{ - exprt::operandst c; - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); - - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) - { - if(vars.find(*it)==vars.end()) - { - pure = false; - break; - } - } - if(!pure) continue; - - const row_valuet &row_v = v[row]; - if(is_row_value_neginf(row_v)) c.push_back(false_exprt()); - else if(is_row_value_inf(row_v)) c.push_back(true_exprt()); - else c.push_back(binary_relation_exprt(templ_row.expr,ID_le,row_v)); - } - result = conjunction(c); -} - -/*******************************************************************\ - -Function: template_domaint::set_row_value - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::set_row_value( - const rowt &row, const template_domaint::row_valuet &row_value, templ_valuet &value) -{ - assert(row(value); - for(unsigned row = 0; row " << std::endl << " "; - break; - case IN: out << "(IN) "; break; - case OUT: case OUTL: out << "(OUT) "; break; - default: assert(false); - } - out << "( " << from_expr(ns,"",templ_row.expr) << " <= "; - if(is_row_value_neginf(v[row])) out << "-oo"; - else if(is_row_value_inf(v[row])) out << "oo"; - else out << from_expr(ns,"",v[row]); - out << " )" << std::endl; - } -} - -/*******************************************************************\ - -Function: template_domaint::output_domain - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::output_domain(std::ostream &out, const namespacet &ns) const -{ - for(unsigned row = 0; row " << std::endl << " "; - break; - case IN: - out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; - break; - case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; - break; - default: assert(false); - } - out << "( " << - from_expr(ns,"",templ_row.expr) << " <= CONST )" << std::endl; - } -} - -/*******************************************************************\ - -Function: template_domaint::template_size - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned template_domaint::template_size() -{ - return templ.size(); -} - -/*******************************************************************\ - -Function: template_domaint::is_row_value_neginf - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::is_row_value_neginf(const row_valuet & row_value) const -{ - return row_value.get(ID_value)==ID_false; -} - -/*******************************************************************\ - -Function: template_domaint::is_row_value_inf - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::is_row_value_inf(const row_valuet & row_value) const -{ - return row_value.get(ID_value)==ID_true; -} - -/*******************************************************************\ - -Function: extend_expr_types - - Inputs: - - Outputs: - - Purpose: increases bitvector sizes such that there are no overflows - -\*******************************************************************/ - -void extend_expr_types(exprt &expr) -{ -// std::cerr << "expr: " << expr << std::endl; - if(expr.id()==ID_typecast) assert(false); - if(expr.id()==ID_constant) return; - if(expr.id()==ID_symbol) return; - if(expr.id()==ID_index) return; - if(expr.id()==ID_unary_minus) - { - extend_expr_types(expr.op0()); - typet new_type = expr.op0().type(); - if(new_type.id()==ID_signedbv) - { - signedbv_typet &new_typebv = to_signedbv_type(new_type); - new_typebv.set_width(new_typebv.get_width()+1); - } - else if(new_type.id()==ID_unsignedbv) - { - unsignedbv_typet &old_type = to_unsignedbv_type(new_type); - new_type = signedbv_typet(old_type.get_width()+1); - } - expr = unary_minus_exprt(typecast_exprt(expr.op0(),new_type),new_type); - return; - } - if(expr.id()==ID_plus || expr.id()==ID_minus) - { - extend_expr_types(expr.op0()); -// std::cerr << "op0: " << expr.op0() << std::endl; - extend_expr_types(expr.op1()); -// std::cerr << "op1: " << expr.op1() << std::endl; - unsigned size0 = 0, size1 = 0; - if(expr.op0().type().id()==ID_signedbv) - size0 = to_signedbv_type(expr.op0().type()).get_width(); - if(expr.op0().type().id()==ID_unsignedbv) - size0 = to_unsignedbv_type(expr.op0().type()).get_width(); - if(expr.op1().type().id()==ID_signedbv) - size1 = to_signedbv_type(expr.op1().type()).get_width(); - if(expr.op1().type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(expr.op1().type()).get_width(); - assert(size0>0); assert(size1>0); //TODO: implement floats - typet new_type = expr.op0().type(); - if(expr.op0().type().id()==expr.op1().type().id()) - { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(std::max(size0,size1)+1); - else if(new_type.id()==ID_unsignedbv) - { - if(expr.id()==ID_minus) - new_type = signedbv_typet(std::max(size0,size1)+1); - else - new_type = unsignedbv_typet(std::max(size0,size1)+1); - } - else assert(false); - } - else - { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(size0<=size1 ? size1+2 : size0+1); - else if(new_type.id()==ID_unsignedbv) - new_type = signedbv_typet(size1<=size0 ? size0+2 : size1+1); - else assert(false); - } - if(expr.id()==ID_plus) - expr = plus_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - else if(expr.id()==ID_minus) - expr = minus_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - else assert(false); - return; - } - //TODO: implement mult - if(expr.id()==ID_mult) - { - extend_expr_types(expr.op0()); - extend_expr_types(expr.op1()); - unsigned size0 = 0, size1 = 0; - if(expr.op0().type().id()==ID_signedbv) - size0 = to_signedbv_type(expr.op0().type()).get_width(); - if(expr.op0().type().id()==ID_unsignedbv) - size0 = to_unsignedbv_type(expr.op0().type()).get_width(); - if(expr.op1().type().id()==ID_signedbv) - size1 = to_signedbv_type(expr.op1().type()).get_width(); - if(expr.op1().type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(expr.op1().type()).get_width(); - assert(size0>0); assert(size1>0); //TODO: implement floats - typet new_type = signedbv_typet(size0+size1+1); - expr = mult_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - return; - } - std::cerr << "expr: " << expr << std::endl; - assert(false); -} - -/*******************************************************************\ - -Function: make_interval_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_interval_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size(); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) - { - if(v->kind==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v->var; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v->var,v->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } - } -} - -/*******************************************************************\ - -Function: make_zone_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_zone_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size()+var_specs.size()*(var_specs.size()-1); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) - { - if(v1->kind!=IN) //TODO: must be done in caller (for preconditions, e.g.) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) - { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); - - // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // x2 - x1 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - } - } -} - -/*******************************************************************\ - -Function: make_octagon_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_octagon_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size()+2*var_specs.size()*(var_specs.size()-1); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) - { - if(v1->kind!=IN) //TODO: must be done in caller (for preconditions, e.g.) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) - { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); - - // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // -x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // -x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt p_expr(unary_minus_exprt(v1->var,v1->var.type()),v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - plus_exprt p_expr(v1->var,v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - } - } - -} - -/*******************************************************************\ - -Function: simplify_const - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -mp_integer simplify_const_int(const exprt &expr) -{ - if(expr.id()==ID_constant) - { - mp_integer v; - to_integer(expr, v); - return v; - } - if(expr.id()==ID_typecast) - { - const exprt &op0 = expr.op0(); - assert(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv); - return simplify_const_int(op0); - } - if(expr.id()==ID_unary_minus) return -simplify_const_int(expr.op0()); - if(expr.id()==ID_plus) return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); - if(expr.id()==ID_minus) return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); - if(expr.id()==ID_mult) return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); - if(expr.id()==ID_symbol) - { - std::cout << "substituting default value for " << expr << std::endl; - return 0; //default value if not substituted in expr - } - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_signedbv || array_type.id()==ID_unsignedbv) - { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow - assert(index<(index_expr.array().operands().size())); - return simplify_const_int(index_expr.array().operands()[index]); - } - assert(false); //not implemented - } - assert(false); //not implemented -} - -ieee_floatt simplify_const_float(const exprt &expr) -{ - if(expr.id()==ID_constant) - { - ieee_floatt v(to_constant_expr(expr)); - return v; - } - if(expr.id()==ID_typecast) - { - const exprt &op0 = expr.op0(); - if(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv) - { - ieee_floatt v; - v.from_integer(simplify_const_int(op0)); - return v; - } - assert(false); - } - if(expr.id()==ID_unary_minus) - { - ieee_floatt v = simplify_const_float(expr.op0()); - v.set_sign(!v.get_sign()); - return v; - } - if(expr.id()==ID_plus) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 += v2; - return v1; - } - if(expr.id()==ID_minus) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 -= v2; - return v1; - } - if(expr.id()==ID_mult) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 *= v2; - return v1; - } - if(expr.id()==ID_symbol) //default value if not substituted in expr - { - ieee_floatt v; - v.make_zero(); - - std::cout << "substituting default value for " << expr << std::endl; - - return v; - } - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_float) - { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow - assert(index<(index_expr.array().operands().size())); - return simplify_const_float(index_expr.array().operands()[index]); - } - assert(false); //not implemented - } - assert(false); //not implemented -} - -constant_exprt simplify_const(const exprt &expr) -{ - if(expr.id()==ID_constant) return to_constant_expr(expr); - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_signedbv) - { - mp_integer res = simplify_const_int(index_expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(array_type.id()==ID_unsignedbv) - { - mp_integer res = simplify_const_int(index_expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(array_type.id()==ID_float) - return to_constant_expr(simplify_const_float(index_expr).to_expr()); - assert(false); //not implemented - } - // if(expr.id()==ID_typecast) return to_constant_expr(expr.op0()); - if(expr.type().id()==ID_signedbv) - { - mp_integer res = simplify_const_int(expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(expr.type().id()==ID_unsignedbv) - { - mp_integer res = simplify_const_int(expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(expr.type().id()==ID_floatbv) - { - return to_constant_expr(simplify_const_float(expr).to_expr()); - } - assert(false); //type not supported -} diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index 7c35aa4b7..8a423b426 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -6,17 +6,17 @@ Author: Peter Schrammel \*******************************************************************/ -#include "template_generator_base.h" -#include "equality_domain.h" -#include "tpolyhedra_domain.h" -#include "predabs_domain.h" - #include #include #include #include #include +#include "template_generator_base.h" +#include "equality_domain.h" +#include "tpolyhedra_domain.h" +#include "predabs_domain.h" + #ifdef DEBUG #include #endif @@ -33,29 +33,31 @@ Function: template_generator_baset::get_pre_post_guards \*******************************************************************/ -void template_generator_baset::get_pre_post_guards(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard) +void template_generator_baset::get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, + exprt &post_guard) { #if 0 - std::cout << "post-location: " - << n_it->location->location_number << std::endl; - assert(n_it->loophead != SSA.nodes.end()); - std::cout << "pre-location: " - << n_it->loophead->location->location_number << std::endl; + std::cout << "post-location: " + << n_it->location->location_number << std::endl; + assert(n_it->loophead!=SSA.nodes.end()); + std::cout << "pre-location: " + << n_it->loophead->location->location_number << std::endl; #endif - exprt lhguard = SSA.guard_symbol(n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); - exprt lsguard = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); - pre_guard = and_exprt(lhguard,lsguard); - - exprt pguard = SSA.guard_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); - exprt pcond = SSA.cond_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); - post_guard = and_exprt(pguard,pcond); + exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), *n_it, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + pre_guard=and_exprt(lhguard, lsguard); + + exprt pguard=SSA.guard_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), *n_it, false); + exprt pcond=SSA.cond_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), *n_it, false); + post_guard=and_exprt(pguard, pcond); } /*******************************************************************\ @@ -70,20 +72,21 @@ Function: template_generator_baset::get_pre_var \*******************************************************************/ -void template_generator_baset::get_pre_var(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - symbol_exprt &pre_var) +void template_generator_baset::get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var) { - pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); + pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); - symbol_exprt post_var = SSA.read_rhs(*o_it, n_it->location); - ssa_local_unwinder.unwinder_rename(post_var,*n_it,false); - post_renaming_map[pre_var] = post_var; + symbol_exprt post_var=SSA.read_rhs(*o_it, n_it->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it, false); + post_renaming_map[pre_var]=post_var; rename_aux_post(post_var); - aux_renaming_map[pre_var]=post_var; + aux_renaming_map[pre_var]=post_var; } /*******************************************************************\ @@ -94,36 +97,35 @@ Function: template_generator_baset::get_init_expr Outputs: - Purpose: supposes that loop head PHIs are of the form - xphi = gls?xlb:x0 + Purpose: supposes that loop head PHIs are of the form + xphi=gls?xlb:x0 \*******************************************************************/ -void template_generator_baset::get_init_expr(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - exprt &init_expr) +void template_generator_baset::get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr) { - symbol_exprt phi_var = SSA.name(*o_it, local_SSAt::PHI, - n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(phi_var,*n_it->loophead,true); - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->loophead->equalities.begin(); - e_it != n_it->loophead->equalities.end(); e_it++) + symbol_exprt phi_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + for(const auto &e : n_it->loophead->equalities) { - if (e_it->rhs().id() == ID_if && - to_symbol_expr(e_it->lhs()).get_identifier()==phi_var.get_identifier()) + if(e.rhs().id()==ID_if && + to_symbol_expr(e.lhs()).get_identifier()==phi_var.get_identifier()) { - const if_exprt &if_expr = to_if_expr(e_it->rhs()); - init_expr = if_expr.false_case(); - //should already be renamed for inner loops + const if_exprt &if_expr=to_if_expr(e.rhs()); + init_expr=if_expr.false_case(); + // should already be renamed for inner loops break; } } - symbol_exprt pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); - init_renaming_map[pre_var]=init_expr; + symbol_exprt pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + init_renaming_map[pre_var]=init_expr; } /*******************************************************************\ @@ -138,47 +140,50 @@ Function: template_generator_baset::collect_variables_loop \*******************************************************************/ -void template_generator_baset::collect_variables_loop(const local_SSAt &SSA,bool forward) +void template_generator_baset::collect_variables_loop( + const local_SSAt &SSA, + bool forward) { // used for renaming map var_listt pre_state_vars, post_state_vars; // add loop variables - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop { exprt pre_guard, post_guard; - get_pre_post_guards(SSA,n_it,pre_guard, post_guard); + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); - const ssa_domaint::phi_nodest &phi_nodes = + const ssa_domaint::phi_nodest &phi_nodes= SSA.ssa_analysis[n_it->loophead->location].phi_nodes; - + // Record the objects modified by the loop to get // 'primed' (post-state) and 'unprimed' (pre-state) variables. for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); + o_it=SSA.ssa_objects.objects.begin(); o_it!=SSA.ssa_objects.objects.end(); o_it++) { ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + phi_nodes.find(o_it->get_identifier()); - if(p_it==phi_nodes.end()) continue; // object not modified in this loop + if(p_it==phi_nodes.end()) // object not modified in this loop + continue; symbol_exprt pre_var; - get_pre_var(SSA,o_it,n_it,pre_var); + get_pre_var(SSA, o_it, n_it, pre_var); exprt init_expr; - get_init_expr(SSA,o_it,n_it,init_expr); - add_var(pre_var,pre_guard,post_guard,domaint::LOOP,var_specs); - - #ifdef DEBUG - std::cout << "Adding " << from_expr(ns, "", in) << " " << - from_expr(ns, "", out) << std::endl; - #endif - } - } + get_init_expr(SSA, o_it, n_it, init_expr); + add_var(pre_var, pre_guard, post_guard, domaint::LOOP, var_specs); + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " << + from_expr(ns, "", out) << std::endl; +#endif + } + } } } @@ -197,10 +202,9 @@ Function: template_generator_baset::all_vars domaint::var_sett template_generator_baset::all_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - vars.insert(v->var); + vars.insert(v.var); } return vars; } @@ -221,19 +225,18 @@ void template_generator_baset::filter_template_domain() { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - const domaint::vart &s = v->var; + const domaint::vart &s=v.var; #ifdef DEBUG std::cout << "var: " << s << std::endl; #endif if((s.type().id()==ID_unsignedbv || s.type().id()==ID_signedbv || - s.type().id()==ID_floatbv /*|| s.type().id()==ID_c_enum_tag*/)) + s.type().id()==ID_floatbv /*|| s.type().id()==ID_c_enum_tag*/)) { - var_specs.push_back(*v); + var_specs.push_back(v); } } } @@ -254,16 +257,15 @@ void template_generator_baset::filter_equality_domain() { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - var_specs.push_back(*v); + var_specs.push_back(v); } } /*******************************************************************\ -Function: template_generator_baset::add_vars +Function: template_generator_baset::add_var Inputs: @@ -273,86 +275,146 @@ Function: template_generator_baset::add_vars \*******************************************************************/ -void template_generator_baset::add_var(const domaint::vart &var, - const domaint::guardt &pre_guard, - domaint::guardt post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +void template_generator_baset::add_var( + const domaint::vart &var, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - exprt aux_expr = true_exprt(); + exprt aux_expr=true_exprt(); if(std_invariants && pre_guard.id()==ID_and) { - exprt init_guard = and_exprt(pre_guard.op0(),not_exprt(pre_guard.op1())); - exprt post_var = post_renaming_map[var]; - exprt aux_var = aux_renaming_map[var]; - aux_expr = and_exprt( + exprt init_guard=and_exprt(pre_guard.op0(), not_exprt(pre_guard.op1())); + exprt post_var=post_renaming_map[var]; + exprt aux_var=aux_renaming_map[var]; + aux_expr=and_exprt( implies_exprt(and_exprt(post_guard, not_exprt(init_guard)), - equal_exprt(aux_var,post_var)), - implies_exprt(init_guard,equal_exprt(aux_var,init_renaming_map[var]))); - post_guard = or_exprt(post_guard,init_guard); + equal_exprt(aux_var, post_var)), + implies_exprt(init_guard, equal_exprt(aux_var, init_renaming_map[var]))); + post_guard=or_exprt(post_guard, init_guard); } if(var.type().id()!=ID_array) { var_specs.push_back(domaint::var_spect()); - domaint::var_spect &var_spec = var_specs.back(); - var_spec.pre_guard = pre_guard; - var_spec.post_guard = post_guard; - var_spec.aux_expr = aux_expr; - var_spec.kind = kind; - var_spec.var = var; + domaint::var_spect &var_spec=var_specs.back(); + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=aux_expr; + var_spec.kind=kind; + var_spec.var=var; } - //arrays + // arrays if(var.type().id()==ID_array && options.get_bool_option("arrays")) { - const array_typet &array_type = to_array_type(var.type()); + const array_typet &array_type=to_array_type(var.type()); mp_integer size; to_integer(array_type.size(), size); - for(mp_integer i=0; i &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(local_SSAt::var_listt::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } -void template_generator_baset::add_vars(const local_SSAt::var_sett &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::add_vars( + const local_SSAt::var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(local_SSAt::var_sett::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } -void template_generator_baset::add_vars(const var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::add_vars( + const local_SSAt::var_sett &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(var_listt::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); +} + +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::add_vars( + const var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) +{ + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } /*******************************************************************\ @@ -369,17 +431,18 @@ Function: template_generator_baset::handle_special_functions void template_generator_baset::handle_special_functions(const local_SSAt &SSA) { - const irep_idt &function_id = SSA.goto_function.body.instructions.front().function; - if(id2string(function_id) == "__CPROVER_initialize") + const irep_idt &function_id= + SSA.goto_function.body.instructions.front().function; + if(id2string(function_id)=="__CPROVER_initialize") { - options.set_option("intervals",true); - options.set_option("enum-solver",true); + options.set_option("intervals", true); + options.set_option("enum-solver", true); } } /*******************************************************************\ -Function: template_generator_baset::build_custom_expr +Function: template_generator_baset::replace_post Inputs: @@ -389,69 +452,81 @@ Function: template_generator_baset::build_custom_expr \*******************************************************************/ -bool template_generator_baset::replace_post(replace_mapt replace_map, exprt &expr) +bool template_generator_baset::replace_post( + replace_mapt replace_map, + exprt &expr) { - bool replaced = false; + bool replaced=false; if(expr.id()==ID_function_application) { - const function_application_exprt &f = to_function_application_expr(expr); - if(f.function().get(ID_identifier) == TEMPLATE_NEWVAR) + const function_application_exprt &f=to_function_application_expr(expr); + if(f.function().get(ID_identifier)==TEMPLATE_NEWVAR) { assert(f.arguments().size()==1); - if(f.arguments()[0].id()==ID_typecast) - expr = replace_map[f.arguments()[0].op0()]; + if(f.arguments()[0].id()==ID_typecast) + expr=replace_map[f.arguments()[0].op0()]; else - expr = replace_map[f.arguments()[0]]; + expr=replace_map[f.arguments()[0]]; return true; } } for(unsigned i=0; iloophead->location].phi_nodes; - - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) + + for(const auto &object : SSA.ssa_objects.objects) { ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + phi_nodes.find(object.get_identifier()); - if(p_it!=phi_nodes.end()) //modified in loop + if(p_it!=phi_nodes.end()) // modified in loop { - //rename to pre - replace_map[o_it->get_expr()] = - SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - - //rename to post - replace_post_map[o_it->get_expr()] = - SSA.read_rhs(*o_it, n_it->location); - //TODO: unwinding + // rename to pre + replace_map[object.get_expr()]= + SSA.name(object, local_SSAt::LOOP_BACK, n_it->location); + + // rename to post + replace_post_map[object.get_expr()]= + SSA.read_rhs(object, n_it->location); + // TODO: unwinding } - else //not modified in loop + else // not modified in loop { - //rename to id valid at loop head - replace_map[o_it->get_expr()] = - SSA.read_rhs(*o_it,n_it->loophead->location); - //TODO: unwinding + // rename to id valid at loop head + replace_map[object.get_expr()]= + SSA.read_rhs(object, n_it->loophead->location); + // TODO: unwinding } } - bool contains_newvar = replace_post(replace_post_map,expr); - replace_expr(replace_map,expr); + bool contains_newvar=replace_post(replace_post_map, expr); + replace_expr(replace_map, expr); return contains_newvar; } @@ -468,118 +543,132 @@ Function: template_generator_baset::instantiate_custom_templates \*******************************************************************/ bool template_generator_baset::instantiate_custom_templates( - const local_SSAt &SSA) + const local_SSAt &SSA) { - //TODO: the code below cannot work for unwound SSA + // TODO: the code below cannot work for unwound SSA // we deactivate it for now return false; // used for renaming map var_listt pre_state_vars, post_state_vars; - bool found_poly = false, found_predabs = false; - for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + bool found_poly=false, found_predabs=false; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop { exprt pre_guard, post_guard, aux_expr; - get_pre_post_guards(SSA,n_it,pre_guard, post_guard); - aux_expr = true_exprt(); //TODO: change to "standard" invariant semantics - bool add_post_vars = false; + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); + aux_expr=true_exprt(); // TODO: change to "standard" invariant semantics + bool add_post_vars=false; - //search for templates in the loop - for(local_SSAt::nodest::const_iterator nn_it=n_it->loophead; - nn_it!=n_it; nn_it++) + // search for templates in the loop + for(local_SSAt::nodest::const_iterator nn_it=n_it->loophead; + nn_it!=n_it; nn_it++) { - if(nn_it->templates.empty()) continue; - if(nn_it->templates.size()>1000) continue; //TODO: there is an unwinder-related bug - for(local_SSAt::nodet::templatest::const_iterator - t_it=nn_it->templates.begin(); - t_it!=nn_it->templates.end(); t_it++) - { - debug() << "Template expression: " - << from_expr(SSA.ns,"",*t_it) << eom; - - // check whether it is a template polyhedra or a pred abs - std::set symbols; - find_symbols(*t_it, symbols); - - bool predabs = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) - { - std::size_t found_param = - id2string(it->get_identifier()).find(TEMPLATE_PARAM_PREFIX); - if (found_param != std::string::npos) - { - predabs = false; - break; - } - } - - //template polyhedra - if(!predabs && t_it->id()==ID_le) - { - debug() << "Custom template polyhedron found" << eom; - if(!found_poly) //create domain - { - domain_ptr = new tpolyhedra_domaint(domain_number, - post_renaming_map, - SSA.ns); //TODO: aux_renaming_map - found_poly = true; - } - exprt expr = t_it->op0(); - bool contains_new_var = build_custom_expr(SSA,n_it,expr); - if(contains_new_var) add_post_vars = true; - static_cast(domain_ptr)->add_template_row( - expr,pre_guard, - contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); - } - // pred abs domain - else if (predabs) - { - options.set_option("predabs-solver",true); - - debug() << "Custom predicate template found" << eom; - if(!found_predabs) //create domain - { - domain_ptr = new predabs_domaint(domain_number, - post_renaming_map, SSA.ns); //TODO: aux_renaming_map - found_predabs = true; - } - exprt expr = *t_it; - bool contains_new_var = build_custom_expr(SSA,n_it,expr); - if(contains_new_var) add_post_vars = true; - static_cast(domain_ptr)->add_template_row( - expr,pre_guard, - contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); - - } - else // neither pred abs, nor polyhedra - warning() << "ignoring unsupported template " - << from_expr(SSA.ns,"",*t_it) << eom; - } - if(add_post_vars) //for result retrieval via all_vars() only - { - domaint::var_specst new_var_specs(var_specs); - var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) - { - var_specs.push_back(*v); - if(v->kind==domaint::LOOP) - { - var_specs.push_back(*v); - var_specs.back().kind = domaint::OUTL; - replace_expr(aux_renaming_map,var_specs.back().var); - } - } - } + if(nn_it->templates.empty()) + continue; +#if 1 + // TODO: there is an unwinder-related bug + if(nn_it->templates.size()>1000) + continue; +#endif + for(local_SSAt::nodet::templatest::const_iterator t_it= + nn_it->templates.begin(); + t_it!=nn_it->templates.end(); t_it++) + { + debug() << "Template expression: " + << from_expr(SSA.ns, "", *t_it) << eom; + + // check whether it is a template polyhedra or a pred abs + std::set symbols; + find_symbols(*t_it, symbols); + + bool predabs=true; + for(std::set::iterator it=symbols.begin(); + it!=symbols.end(); it++) + { + std::size_t found_param= + id2string(it->get_identifier()).find(TEMPLATE_PARAM_PREFIX); + if(found_param!=std::string::npos) + { + predabs=false; + break; + } + } + + // template polyhedra + if(!predabs && t_it->id()==ID_le) + { + debug() << "Custom template polyhedron found" << eom; + if(!found_poly) // create domain + { + domain_ptr=new tpolyhedra_domaint( + domain_number, + post_renaming_map, + SSA.ns); // TODO: aux_renaming_map + found_poly=true; + } + + exprt expr=t_it->op0(); + bool contains_new_var=build_custom_expr(SSA, n_it, expr); + if(contains_new_var) + add_post_vars=true; + + static_cast(domain_ptr)->add_template_row( + expr, pre_guard, + contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + } + // pred abs domain + else if(predabs) + { + options.set_option("predabs-solver", true); + + debug() << "Custom predicate template found" << eom; + if(!found_predabs) // create domain + { + domain_ptr=new predabs_domaint( + domain_number, + post_renaming_map, SSA.ns); // TODO: aux_renaming_map + found_predabs=true; + } + + exprt expr=*t_it; + bool contains_new_var=build_custom_expr(SSA, n_it, expr); + if(contains_new_var) + add_post_vars=true; + + static_cast(domain_ptr)->add_template_row( + expr, pre_guard, + contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + } + else // neither pred abs, nor polyhedra + { + warning() << "ignoring unsupported template " + << from_expr(SSA.ns, "", *t_it) << eom; + } + } + if(add_post_vars) // for result retrieval via all_vars() only + { + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(domaint::var_specst::const_iterator v=new_var_specs.begin(); + v!=new_var_specs.end(); v++) + { + var_specs.push_back(*v); + if(v->kind==domaint::LOOP) + { + var_specs.push_back(*v); + var_specs.back().kind=domaint::OUTL; + replace_expr(aux_renaming_map, var_specs.back().var); + } + } + } } } } @@ -599,30 +688,31 @@ Function: template_generator_baset::instantiate_standard_domains \*******************************************************************/ -void template_generator_baset::instantiate_standard_domains(const local_SSAt &SSA) +void template_generator_baset::instantiate_standard_domains( + const local_SSAt &SSA) { - replace_mapt &renaming_map = + replace_mapt &renaming_map= std_invariants ? aux_renaming_map : post_renaming_map; - - //get domain from command line options + + // get domain from command line options if(options.get_bool_option("equalities")) { filter_equality_domain(); - domain_ptr = new equality_domaint(domain_number, - renaming_map, var_specs, SSA.ns); + domain_ptr= + new equality_domaint(domain_number, renaming_map, var_specs, SSA.ns); } else if(options.get_bool_option("intervals")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_interval_template( var_specs, SSA.ns); } else if(options.get_bool_option("zones")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); @@ -631,8 +721,8 @@ void template_generator_baset::instantiate_standard_domains(const local_SSAt &SS } else if(options.get_bool_option("octagons")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_sum_template( var_specs, SSA.ns); @@ -643,8 +733,8 @@ void template_generator_baset::instantiate_standard_domains(const local_SSAt &SS } else if(options.get_bool_option("qzones")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index a09cb315d..031d4ac49 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -6,118 +6,140 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_BASE_H -#define DELTACHECK_TEMPLATE_GENERATOR_BASE_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" +#include +#include #include "strategy_solver_base.h" -//#define SHOW_TEMPLATE_VARIABLES -//#define SHOW_TEMPLATE +// #define SHOW_TEMPLATE_VARIABLES +// #define SHOW_TEMPLATE -class template_generator_baset : public messaget +class template_generator_baset:public messaget { public: typedef strategy_solver_baset::var_listt var_listt; - explicit template_generator_baset(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - options(_options), ssa_db(_ssa_db), - ssa_local_unwinder(_ssa_local_unwinder) + explicit template_generator_baset( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + options(_options), ssa_db(_ssa_db), + ssa_local_unwinder(_ssa_local_unwinder) { - std_invariants = options.get_bool_option("std-invariants"); - } + std_invariants=options.get_bool_option("std-invariants"); + } - virtual ~template_generator_baset() + virtual ~template_generator_baset() { - if(domain_ptr!=NULL) delete domain_ptr; + if(domain_ptr!=NULL) + delete domain_ptr; } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true) - { - domain_number = _domain_number; + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true) + { + domain_number=_domain_number; assert(false); } virtual domaint::var_sett all_vars(); - - domaint *domain() { assert(domain_ptr!=NULL); return domain_ptr; } + bool empty() + { + assert(domain_ptr!=nullptr); + return domain_ptr->is_spec_empty(); + } + domaint *domain() { assert(domain_ptr!=nullptr); return domain_ptr; } domaint::var_specst var_specs; replace_mapt post_renaming_map; replace_mapt init_renaming_map; replace_mapt aux_renaming_map; - unsigned domain_number; //serves as id for variables names + unsigned domain_number; // serves as id for variables names optionst options; // copy: we may override options protected: const ssa_dbt &ssa_db; const ssa_local_unwindert &ssa_local_unwinder; - domaint* domain_ptr; - bool std_invariants; //include value at loop entry + domaint *domain_ptr; + bool std_invariants; // include value at loop entry - virtual void collect_variables_loop(const local_SSAt &SSA, - bool forward); + virtual void collect_variables_loop( + const local_SSAt &SSA, + bool forward); void filter_template_domain(); void filter_equality_domain(); - void add_var(const domaint::vart &var_to_add, - const domaint::guardt &pre_guard, - domaint::guardt post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const local_SSAt::var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const local_SSAt::var_sett &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - - void get_pre_post_guards(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard); - void get_pre_var(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - symbol_exprt &pre_var); - - void get_init_expr(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - exprt &init_expr); + void add_var( + const domaint::vart &var_to_add, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const std::set &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const local_SSAt::var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const local_SSAt::var_sett &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + + void get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, + exprt &post_guard); + void get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var); + + void get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr); bool replace_post(replace_mapt replace_map, exprt &expr); - bool build_custom_expr(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &expr); + bool build_custom_expr( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &expr); virtual void handle_special_functions(const local_SSAt &SSA); void instantiate_standard_domains(const local_SSAt &SSA); bool instantiate_custom_templates(const local_SSAt &SSA); - void rename_aux_post(symbol_exprt &expr) - { + void rename_aux_post(symbol_exprt &expr) + { expr.set_identifier(id2string(expr.get_identifier())+"'"); } }; - -#endif +#endif // CROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H diff --git a/src/domains/template_generator_callingcontext.cpp b/src/domains/template_generator_callingcontext.cpp index bf38e3e6f..310ea4303 100644 --- a/src/domains/template_generator_callingcontext.cpp +++ b/src/domains/template_generator_callingcontext.cpp @@ -6,12 +6,13 @@ Author: Peter Schrammel \*******************************************************************/ +#include + +#include + #include "template_generator_callingcontext.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" -#include "../ssa/ssa_inliner.h" - -#include /*******************************************************************\ @@ -25,27 +26,28 @@ Function: template_generator_callingcontextt::operator() \*******************************************************************/ -void template_generator_callingcontextt::operator()(unsigned _domain_number, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward) +void template_generator_callingcontextt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! - collect_variables_loop(SSA,forward); - collect_variables_callingcontext(SSA,n_it,f_it,forward); + collect_variables_loop(SSA, forward); + collect_variables_callingcontext(SSA, n_it, f_it, forward); - //get domain from command line options - instantiate_standard_domains(SSA); + // get domain from command line options + instantiate_standard_domains(SSA); #if 1 debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -61,53 +63,58 @@ Function: template_generator_callingcontextt::collect_variables_callingcontext \*******************************************************************/ void template_generator_callingcontextt::collect_variables_callingcontext( - const local_SSAt &SSA, + const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, bool forward) { - exprt guard = SSA.guard_symbol(n_it->location); + exprt guard=SSA.guard_symbol(n_it->location); - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - const local_SSAt &fSSA = ssa_db.get(fname); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const local_SSAt &fSSA=ssa_db.get(fname); - //getting globals at call site + // getting globals at call site local_SSAt::var_sett cs_globals_in, globals_in; if(forward) { - SSA.get_globals(n_it->location,cs_globals_in,true,false); - //filter out return values - globals_in = fSSA.globals_in; + SSA.get_globals(n_it->location, cs_globals_in, true, false); + // filter out return values + globals_in=fSSA.globals_in; } else { - SSA.get_globals(n_it->location,cs_globals_in,false,true,fname); - //with return values for function call - globals_in = fSSA.globals_out; + SSA.get_globals(n_it->location, cs_globals_in, false, true, fname); + // with return values for function call + globals_in=fSSA.globals_out; } - for(local_SSAt::var_sett::iterator v_it = cs_globals_in.begin(); - v_it != cs_globals_in.end(); v_it++) + for(local_SSAt::var_sett::iterator v_it=cs_globals_in.begin(); + v_it!=cs_globals_in.end(); v_it++) { symbol_exprt dummy; - if(ssa_inlinert::find_corresponding_symbol(*v_it,globals_in,dummy)) - add_var(*v_it,guard,guard, - domaint::OUT, //the same for both forward and backward - var_specs); + if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy)) + add_var( + *v_it, + guard, + guard, + domaint::OUT, // the same for both forward and backward + var_specs); } - if(!forward) return; //TODO: actually, the context should contain both, arguments and return values + // TODO: actually, the context should contain both, + // arguments and return values + if(!forward) + return; - //add function arguments - for(exprt::operandst::const_iterator a_it = f_it->arguments().begin(); - a_it != f_it->arguments().end(); a_it++) + // add function arguments + for(exprt::operandst::const_iterator a_it=f_it->arguments().begin(); + a_it!=f_it->arguments().end(); a_it++) { std::set args; - find_symbols(*a_it,args); - add_vars(args,guard,guard,domaint::OUT,var_specs); + find_symbols(*a_it, args); + add_vars(args, guard, guard, domaint::OUT, var_specs); } - } /*******************************************************************\ @@ -125,10 +132,10 @@ Function: template_generator_callingcontextt::callingcontext_vars domaint::var_sett template_generator_callingcontextt::callingcontext_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - if(v->kind==domaint::OUT) vars.insert(v->var); + if(v.kind==domaint::OUT) + vars.insert(v.var); } return vars; } diff --git a/src/domains/template_generator_callingcontext.h b/src/domains/template_generator_callingcontext.h index 946a63ab6..a86d35db8 100644 --- a/src/domains/template_generator_callingcontext.h +++ b/src/domains/template_generator_callingcontext.h @@ -6,36 +6,37 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_CALLINGCONTEXT_H -#define DELTACHECK_TEMPLATE_GENERATOR_CALLINGCONTEXT_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_CALLINGCONTEXT_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_CALLINGCONTEXT_H #include "template_generator_base.h" -class template_generator_callingcontextt : public template_generator_baset +class template_generator_callingcontextt:public template_generator_baset { public: - explicit template_generator_callingcontextt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_callingcontextt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } + } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward=true); + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward=true); virtual domaint::var_sett callingcontext_vars(); protected: - - virtual void collect_variables_callingcontext(const local_SSAt &SSA, + virtual void collect_variables_callingcontext( + const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, bool forward); }; - #endif diff --git a/src/domains/template_generator_ranking.cpp b/src/domains/template_generator_ranking.cpp index 0d1b331f8..fab04e251 100644 --- a/src/domains/template_generator_ranking.cpp +++ b/src/domains/template_generator_ranking.cpp @@ -7,7 +7,6 @@ Author: Peter Schrammel \*******************************************************************/ #include "template_generator_ranking.h" - #include "linrank_domain.h" #include "lexlinrank_domain.h" @@ -32,30 +31,32 @@ Function: template_generator_rankingt::operator() \*******************************************************************/ -void template_generator_rankingt::operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward) +void template_generator_rankingt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! if(options.get_bool_option("monolithic-ranking-function")) { - domain_ptr = new linrank_domaint(domain_number,post_renaming_map, SSA.ns); + domain_ptr=new linrank_domaint(domain_number, post_renaming_map, SSA.ns); } else { - domain_ptr = new lexlinrank_domaint(domain_number,post_renaming_map, SSA.ns); + domain_ptr=new lexlinrank_domaint(domain_number, post_renaming_map, SSA.ns); } - collect_variables_ranking(SSA,forward); + collect_variables_ranking(SSA, forward); - options.set_option("compute-ranking-functions",true); + options.set_option("compute-ranking-functions", true); #if 1 debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -70,61 +71,63 @@ Function: template_generator_rankingt::collect_variables_ranking \*******************************************************************/ -void template_generator_rankingt::collect_variables_ranking(const local_SSAt &SSA,bool forward) +void template_generator_rankingt::collect_variables_ranking( + const local_SSAt &SSA, + bool forward) { // used for renaming map var_listt pre_state_vars, post_state_vars; // add loop variables - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(const auto &node : SSA.nodes) { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop + // we've found a loop + if(node.loophead!=SSA.nodes.end()) { domaint::var_specst new_var_specs; - exprt lhguard = SSA.guard_symbol(n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); - exprt lsguard = SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); - exprt pre_guard = lhguard; //and_exprt(lhguard,lsguard); - - exprt pguard = SSA.guard_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); - exprt pcond = SSA.cond_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); - exprt post_guard = and_exprt(pguard,pcond); - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[n_it->loophead->location].phi_nodes; - + exprt lhguard=SSA.guard_symbol(node.loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), node, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), node, true); + exprt pre_guard=lhguard; + + exprt pguard=SSA.guard_symbol(node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), node, false); + exprt pcond=SSA.cond_symbol(node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), node, false); + exprt post_guard=and_exprt(pguard, pcond); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[node.loophead->location].phi_nodes; + // Record the objects modified by the loop to get // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) + for(const auto &object : SSA.ssa_objects.objects) { ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + phi_nodes.find(object.get_identifier()); + + // object not modified in this loop + if(p_it==phi_nodes.end()) + continue; - if(p_it==phi_nodes.end()) continue; // object not modified in this loop + symbol_exprt in= + SSA.name(object, local_SSAt::PHI, node.loophead->location); + ssa_local_unwinder.unwinder_rename(in, node, true); + symbol_exprt out=SSA.read_rhs(object, node.location); + ssa_local_unwinder.unwinder_rename(out, node, false); -// symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - symbol_exprt in=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(in,*n_it,true); - symbol_exprt out=SSA.read_rhs(*o_it, n_it->location); - ssa_local_unwinder.unwinder_rename(out,*n_it,false); + add_var(in, pre_guard, post_guard, domaint::LOOP, new_var_specs); - add_var(in,pre_guard,post_guard,domaint::LOOP,new_var_specs); - // building map for renaming from pre into post-state - post_renaming_map[in] = out; - - #ifdef DEBUG - std::cout << "Adding " << from_expr(ns, "", in) << " " << - from_expr(ns, "", out) << std::endl; - #endif + post_renaming_map[in]=out; + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " + << from_expr(ns, "", out) << std::endl; +#endif } filter_ranking_domain(new_var_specs); @@ -137,8 +140,9 @@ void template_generator_rankingt::collect_variables_ranking(const local_SSAt &SS new_var_specs, SSA.ns); #endif - var_specs.insert(var_specs.end(),new_var_specs.begin(),new_var_specs.end()); - } + var_specs.insert( + var_specs.end(), new_var_specs.begin(), new_var_specs.end()); + } } } @@ -154,24 +158,27 @@ Function: template_generator_rankingt::filter_ranking_domain \*******************************************************************/ -void template_generator_rankingt::filter_ranking_domain(domaint::var_specst &var_specs) +void template_generator_rankingt::filter_ranking_domain( + domaint::var_specst &var_specs) { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - const domaint::vart &s = v->var; - if(s.type().id()==ID_unsignedbv || s.type().id()==ID_signedbv || + const domaint::vart &s=v.var; + if(s.type().id()==ID_unsignedbv || + s.type().id()==ID_signedbv || s.type().id()==ID_floatbv) { - var_specs.push_back(*v); + var_specs.push_back(v); } + #if 0 if(s.type().id()==ID_pointer) { - domaint::var_spect new_varspec = *v; - new_varspec.var = typecast_exprt(v->var,to_pointer_type(v->var.type()).subtype()); + domaint::var_spect new_varspec=v; + new_varspec.var= + typecast_exprt(v.var, to_pointer_type(v.var.type()).subtype()); var_specs.push_back(new_varspec); } #endif diff --git a/src/domains/template_generator_ranking.h b/src/domains/template_generator_ranking.h index cec4d5d6b..037c97358 100644 --- a/src/domains/template_generator_ranking.h +++ b/src/domains/template_generator_ranking.h @@ -2,38 +2,37 @@ Module: Template Generator for Ranking Functions -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_RANKING_H -#define DELTACHECK_TEMPLATE_GENERATOR_RANKING_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H #include "template_generator_base.h" -class template_generator_rankingt : public template_generator_baset +class template_generator_rankingt:public template_generator_baset { public: - - explicit template_generator_rankingt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_rankingt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } - - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true); + } -protected: + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true); - void collect_variables_ranking(const local_SSAt &SSA, - bool forward); +protected: + void collect_variables_ranking( + const local_SSAt &SSA, + bool forward); void filter_ranking_domain(domaint::var_specst &var_specs); - }; - -#endif +#endif // CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index e3c1162a0..b9fc4f68b 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -33,18 +33,20 @@ Function: template_generator_summaryt::operator() \*******************************************************************/ -void template_generator_summaryt::operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward) +void template_generator_summaryt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! - collect_variables_loop(SSA,forward); + collect_variables_loop(SSA, forward); // do not compute summary for entry-point - if(SSA.goto_function.body.instructions.front().function != ID__start || + if(SSA.goto_function.body.instructions.front().function!=ID__start || options.get_bool_option("preconditions")) - collect_variables_inout(SSA,forward); + collect_variables_inout(SSA, forward); // either use standard templates or user-supplied ones if(!instantiate_custom_templates(SSA)) @@ -52,12 +54,12 @@ void template_generator_summaryt::operator()(unsigned _domain_number, #ifdef SHOW_TEMPLATE_VARIABLES debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; -#endif + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; +#endif #ifdef SHOW_TEMPLATE debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -72,23 +74,41 @@ Function: template_generator_summaryt::collect_variables_inout \*******************************************************************/ -void template_generator_summaryt::collect_variables_inout(const local_SSAt &SSA,bool forward) +void template_generator_summaryt::collect_variables_inout( + const local_SSAt &SSA, + bool forward) { // add params and globals_in - exprt first_guard = SSA.guard_symbol(SSA.goto_function.body.instructions.begin()); - add_vars(SSA.params,first_guard,first_guard, - forward ? domaint::IN : domaint::OUT, - var_specs); - add_vars(SSA.globals_in,first_guard,first_guard, - forward ? domaint::IN : domaint::OUT, - var_specs); + exprt first_guard= + SSA.guard_symbol(SSA.goto_function.body.instructions.begin()); + add_vars( + SSA.params, + first_guard, + first_guard, + forward ? domaint::IN : domaint::OUT, + var_specs); + add_vars( + SSA.globals_in, + first_guard, + first_guard, + forward ? domaint::IN : domaint::OUT, + var_specs); // add globals_out (includes return values) - exprt last_guard = + exprt last_guard= SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - add_vars(SSA.globals_out,last_guard,last_guard, - forward ? domaint::OUT : domaint::IN, - var_specs); + add_vars( + SSA.globals_out, + last_guard, + last_guard, + forward ? domaint::OUT : domaint::IN, + var_specs); + + // add nondets for backwards analysis + if(!forward) + { + add_vars(SSA.nondets, first_guard, first_guard, domaint::OUT, var_specs); + } } /*******************************************************************\ @@ -106,10 +126,11 @@ Function: template_generator_summaryt::inout_vars domaint::var_sett template_generator_summaryt::inout_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::IN || v->kind==domaint::OUT) vars.insert(v->var); + if(v->kind==domaint::IN || v->kind==domaint::OUT) + vars.insert(v->var); } return vars; } @@ -129,10 +150,11 @@ Function: template_generator_summaryt::out_vars domaint::var_sett template_generator_summaryt::out_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::OUT) vars.insert(v->var); + if(v->kind==domaint::OUT) + vars.insert(v->var); } return vars; } @@ -152,10 +174,10 @@ Function: template_generator_summaryt::loop_vars domaint::var_sett template_generator_summaryt::loop_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::LOOP || v->kind==domaint::IN) + if(v->kind==domaint::LOOP || v->kind==domaint::IN) vars.insert(v->var); } return vars; diff --git a/src/domains/template_generator_summary.h b/src/domains/template_generator_summary.h index bb3a22567..45aa4422e 100644 --- a/src/domains/template_generator_summary.h +++ b/src/domains/template_generator_summary.h @@ -2,40 +2,37 @@ Module: Template Generator for Summaries -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_SUMMARY_H -#define DELTACHECK_TEMPLATE_GENERATOR_SUMMARY_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_SUMMARY_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_SUMMARY_H #include "template_generator_base.h" -class template_generator_summaryt : public template_generator_baset +class template_generator_summaryt:public template_generator_baset { public: - - explicit template_generator_summaryt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_summaryt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } + } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true); + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true); virtual domaint::var_sett inout_vars(); virtual domaint::var_sett loop_vars(); virtual domaint::var_sett out_vars(); -protected: - - virtual void collect_variables_inout(const local_SSAt &SSA, - bool forward); - +protected: + virtual void collect_variables_inout(const local_SSAt &SSA, bool forward); }; - #endif diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 800ea8ef7..b2c7a2075 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -1,12 +1,23 @@ -#include "tpolyhedra_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Template polyhedra domain + +Author: Peter Schrammel + +\*******************************************************************/ +#ifdef DEBUG #include +#include +#endif #include #include #include -#include + +#include "tpolyhedra_domain.h" +#include "util.h" +#include "simplify_bounds.h" #define SYMB_BOUND_VAR "symb_bound#" @@ -27,16 +38,21 @@ Function: tpolyhedra_domaint::initialize void tpolyhedra_domaint::initialize(valuet &value) { #if 0 - if(templ.size()==0) return domaint::initialize(value); + if(templ.size()==0) + return domaint::initialize(value); #endif - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row(value1); - const templ_valuet &v2 = static_cast(value2); + templ_valuet &v1=static_cast(value1); + const templ_valuet &v2=static_cast(value2); assert(v1.size()==templ.size()); assert(v1.size()==v2.size()); - for(unsigned row = 0; row=vlower); - if(vlower+1==vupper) return from_integer(vlower,lower.type()); //floor + if(vlower+1==vupper) + return from_integer(vlower, lower.type()); // floor #ifdef ENABLE_HEURISTICS - //heuristics + // heuristics if(type.id()==ID_unsignedbv) { - mp_integer vlargest = to_unsignedbv_type(type).largest(); + mp_integer vlargest=to_unsignedbv_type(type).largest(); if(vlower==mp_integer(0) && vupper==vlargest) - return from_integer(mp_integer(1),type); + return from_integer(mp_integer(1), type); if(vlower==mp_integer(1) && vupper==vlargest) - return from_integer(mp_integer(vupper-1),type); + return from_integer(mp_integer(vupper-1), type); if(vlower==mp_integer(1) && vupper==vlargest-1) - return from_integer(mp_integer(2),type); + return from_integer(mp_integer(2), type); if(vlower=plower); + mp_integer plower=vlower.pack(); // compute "median" float number + mp_integer pupper=vupper.pack(); +#if 0 + assert(pupper>=plower); +#endif ieee_floatt res(to_floatbv_type(lower.type())); - res.unpack((plower+pupper)/2); //...by computing integer mean + res.unpack((plower+pupper)/2); // ...by computing integer mean return res.to_expr(); } ieee_floatt res(to_floatbv_type(lower.type())); res.make_zero(); return res.to_expr(); } - assert(false); //types do not match or are not supported + assert(false); // types do not match or are not supported } /*******************************************************************\ @@ -176,7 +198,7 @@ Function: tpolyhedra_domaint::less_than bool tpolyhedra_domaint::less_than(const row_valuet &v1, const row_valuet &v2) { - if(v1.type()==v2.type() && + if(v1.type()==v2.type() && (v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv)) { mp_integer vv1, vv2; @@ -190,53 +212,84 @@ bool tpolyhedra_domaint::less_than(const row_valuet &v1, const row_valuet &v2) ieee_floatt vv2(to_constant_expr(v2)); return vv1 row_expr <= row_value + Purpose: pre_guard==> row_expr<=row_value \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_constraint( + const rowt &row, const row_valuet &row_value) { assert(row row_expr<=row_value + +\*******************************************************************/ + +exprt tpolyhedra_domaint::get_row_pre_constraint( + const rowt &row, const row_valuet &row_value) { assert(row row_expr<=row_value + +\*******************************************************************/ -exprt tpolyhedra_domaint::get_row_pre_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_pre_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_pre_constraint(row,value[row]); + return get_row_pre_constraint(row, value[row]); } /*******************************************************************\ @@ -247,284 +300,328 @@ Function: tpolyhedra_domaint::get_row_post_constraint Outputs: - Purpose: row_expr <= row_value + Purpose: row_expr<=row_value \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_post_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_post_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_expr <= row_value) ) + Purpose: /\_all_rows ( pre_guard==> (row_expr<=row_value) ) \*******************************************************************/ exprt tpolyhedra_domaint::to_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= row_value)) - to be connected disjunctively + Purpose: for all rows !(post_guard==> (row_expr<=row_value)) + to be connected disjunctively \*******************************************************************/ -void tpolyhedra_domaint::make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, +void tpolyhedra_domaint::make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, exprt::operandst &value_exprs) { assert(value.size()==templ.size()); cond_exprs.resize(templ.size()); value_exprs.resize(templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_value) + Purpose: pre_guard==> (row_expr<=symb_value) \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value) +exprt tpolyhedra_domaint::get_row_symb_pre_constraint( + const rowt &row, + const row_valuet &row_value) { assert(row loop15 regression - binary_relation_exprt(templ_row.expr,ID_le,get_row_symb_value(row))); + const template_rowt &templ_row=templ[row]; + if(templ_row.kind==OUT || templ_row.kind==OUTL) + return true_exprt(); + return implies_exprt( + templ_row.pre_guard, // REMARK: and_expr==> loop15 regression + binary_relation_exprt(templ_row.expr, ID_le, get_row_symb_value(row))); } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_symb_post_constraint + Function: tpolyhedra_domaint::get_row_symb_post_constraint - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: post_guard && (row_expr >= row_symb_value) (!!!) + Purpose: post_guard && (row_expr >= row_symb_value) (!!!) \*******************************************************************/ exprt tpolyhedra_domaint::get_row_symb_post_constraint(const rowt &row) { assert(row (row_expr <= symb_row_value) + Purpose: pre_guard==> (row_expr<=symb_row_value) \*******************************************************************/ exprt tpolyhedra_domaint::to_symb_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_row_value) + Purpose: pre_guard==> (row_expr<=symb_row_value) \*******************************************************************/ -exprt tpolyhedra_domaint::to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows) +exprt tpolyhedra_domaint::to_symb_pre_constraints( + const templ_valuet &value, + const std::set &symb_rows) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr >= symb_row_value) + Purpose: /\_i post_guard==> (row_expr >= symb_row_value) \*******************************************************************/ exprt tpolyhedra_domaint::to_symb_post_constraints( - const std::set &symb_rows) + const std::set &symb_rows) { - exprt::operandst c; - for(std::set::const_iterator it = symb_rows.begin(); - it != symb_rows.end(); it++) + exprt::operandst c; + for(const auto &row : symb_rows) { - c.push_back(get_row_symb_post_constraint(*it)); + c.push_back(get_row_symb_post_constraint(row)); } - return conjunction(c); + return conjunction(c); } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_symb_value_constraint + Function: tpolyhedra_domaint::get_row_symb_value_constraint - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: row_value_value <= symb_row + Purpose: row_value_value<=symb_row \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value, bool geq) +exprt tpolyhedra_domaint::get_row_symb_value_constraint( + const rowt &row, + const row_valuet &row_value, + bool geq) { - if(is_row_value_neginf(row_value)) return false_exprt(); - if(is_row_value_inf(row_value)) return true_exprt(); - exprt c = binary_relation_exprt(get_row_symb_value(row), - geq ? ID_ge : ID_le,row_value); + if(is_row_value_neginf(row_value)) + return false_exprt(); + if(is_row_value_inf(row_value)) + return true_exprt(); + exprt c=binary_relation_exprt( + get_row_symb_value(row), + geq ? ID_ge : ID_le, + row_value); return c; } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_value + Function: tpolyhedra_domaint::get_row_value - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ tpolyhedra_domaint::row_valuet tpolyhedra_domaint::get_row_value( - const rowt &row, const templ_valuet &value) + const rowt &row, + const templ_valuet &value) { assert(row(value); + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); + find_symbols(templ_row.expr, symbols); - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) + bool pure=true; + for(const auto &symbol : symbols) { - if(vars.find(*it)==vars.end()) + if(vars.find(symbol)==vars.end()) { - pure = false; + pure=false; break; } } - if(!pure) continue; + if(!pure) + continue; - const row_valuet &row_v = v[row]; + const row_valuet &row_v=v[row]; if(templ_row.kind==LOOP) { - if(is_row_value_neginf(row_v)) - c.push_back(implies_exprt(templ_row.pre_guard,false_exprt())); - else if(is_row_value_inf(row_v)) - c.push_back(implies_exprt(templ_row.pre_guard,true_exprt())); - else c.push_back(implies_exprt(templ_row.pre_guard, - binary_relation_exprt(templ_row.expr,ID_le,row_v))); + if(is_row_value_neginf(row_v)) + c.push_back(implies_exprt(templ_row.pre_guard, false_exprt())); + else if(is_row_value_inf(row_v)) + c.push_back(implies_exprt(templ_row.pre_guard, true_exprt())); + else + c.push_back( + implies_exprt( + templ_row.pre_guard, + binary_relation_exprt(templ_row.expr, ID_le, row_v))); } else { - if(is_row_value_neginf(row_v)) - c.push_back(false_exprt()); - else if(is_row_value_inf(row_v)) - c.push_back(true_exprt()); - else c.push_back(binary_relation_exprt(templ_row.expr,ID_le,row_v)); + if(is_row_value_neginf(row_v)) + c.push_back(false_exprt()); + else if(is_row_value_inf(row_v)) + c.push_back(true_exprt()); + else + c.push_back(binary_relation_exprt(templ_row.expr, ID_le, row_v)); } } - result = conjunction(c); + result=conjunction(c); + simplify_bounds(result, ns); + simplify_bounds(result, ns); } /*******************************************************************\ -Function: tpolyhedra_domaint::set_row_value + Function: tpolyhedra_domaint::set_row_value - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ void tpolyhedra_domaint::set_row_value( - const rowt &row, const tpolyhedra_domaint::row_valuet &row_value, templ_valuet &value) + const rowt &row, + const tpolyhedra_domaint::row_valuet &row_value, + templ_valuet &value) { assert(row(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; case IN: out << "(IN) "; break; case OUT: case OUTL: out << "(OUT) "; break; default: assert(false); } - out << "( " << from_expr(ns,"",templ_row.expr) << " <= "; - if(is_row_value_neginf(v[row])) out << "-oo"; - else if(is_row_value_inf(v[row])) out << "oo"; - else out << from_expr(ns,"",v[row]); + out << "( " << from_expr(ns, "", templ_row.expr) << "<="; + if(is_row_value_neginf(v[row])) + out << "-oo"; + else if(is_row_value_inf(v[row])) + out << "oo"; + else + out << from_expr(ns, "", v[row]); out << " )" << std::endl; } } /*******************************************************************\ -Function: tpolyhedra_domaint::output_domain + Function: tpolyhedra_domaint::output_domain - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ -void tpolyhedra_domaint::output_domain(std::ostream &out, const namespacet &ns) const +void tpolyhedra_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) << " ]===> " + << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) << "===> " + << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << "===> " + << std::endl << " "; break; default: assert(false); } - out << "( " << - from_expr(ns,"",templ_row.expr) << " <= CONST )" << std::endl; + out << "( " << + from_expr(ns, "", templ_row.expr) << "<=CONST )" << std::endl; } } /*******************************************************************\ -Function: tpolyhedra_domaint::template_size + Function: tpolyhedra_domaint::template_size - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ @@ -778,30 +896,50 @@ unsigned tpolyhedra_domaint::template_size() /*******************************************************************\ -Function: tpolyhedra_domaint::is_row_value_neginf + Function: tpolyhedra_domaint::is_row_value_neginf - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ -bool tpolyhedra_domaint::is_row_value_neginf(const row_valuet & row_value) const +bool tpolyhedra_domaint::is_row_value_neginf( + const row_valuet & row_value) const { return row_value.get(ID_value)==ID_false; } /*******************************************************************\ -Function: tpolyhedra_domaint::is_row_value_inf + Function: tpolyhedra_domaint::is_row_value_neginf - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: + +\*******************************************************************/ + +bool tpolyhedra_domaint::is_row_value_neginf( + const valuet &value, const rowt &row) const +{ + const templ_valuet &v=static_cast(value); + return v.at(row).get(ID_value)==ID_false; +} + +/*******************************************************************\ + + Function: tpolyhedra_domaint::is_row_value_inf + + Inputs: + + Outputs: + + Purpose: \*******************************************************************/ @@ -812,43 +950,85 @@ bool tpolyhedra_domaint::is_row_value_inf(const row_valuet & row_value) const /*******************************************************************\ -Function: tpolyhedra_domaint::rename_for_row + Function: tpolyhedra_domaint::is_row_value_inf - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: add row suffix to non-symbolic-bound variables in expression - (required for strategy iteration (binsearch3)) + Purpose: \*******************************************************************/ +bool tpolyhedra_domaint::is_row_value_inf( + const valuet &value, const rowt &row) const +{ + const templ_valuet &v=static_cast(value); + const row_valuet &row_value=v.at(row); + if(row_value.get(ID_value)==ID_true) + return true; + if(row_value==get_max_row_value(row)) + return true; + const row_exprt &row_expr=templ[row].expr; + if(row_expr.id()==ID_unary_minus && + row_expr.op0().id()==ID_typecast) + { + mp_integer rvalue; + to_integer(row_value, rvalue); + const typet &inner_type=row_expr.op0().op0().type(); + mp_integer smallest; + if(inner_type.id()==ID_unsignedbv) + smallest=to_unsignedbv_type(inner_type).smallest(); + else if(inner_type.id()==ID_signedbv) + smallest=to_signedbv_type(inner_type).smallest(); + else + return false; + if(smallest==rvalue) + return true; + } + + return false; +} + +/*******************************************************************\ + + Function: tpolyhedra_domaint::rename_for_row + + Inputs: + + Outputs: + + Purpose: add row suffix to non-symbolic-bound variables in expression + (required for strategy iteration (binsearch3)) + +\*******************************************************************/ void tpolyhedra_domaint::rename_for_row(exprt &expr, const rowt &row) { - if(row==0) return; //do not rename + if(row==0) + return; // do not rename if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - const std::string &old_id = expr.get_string(ID_identifier); - if(old_id.find(SYMB_BOUND_VAR)==std::string::npos) + const std::string &old_id=expr.get_string(ID_identifier); + if(old_id.find(SYMB_BOUND_VAR)==std::string::npos) { - irep_idt id = old_id + "_" + i2string(row); - expr.set(ID_identifier,id); + irep_idt id=old_id+"_"+i2string(row); + expr.set(ID_identifier, id); } } - for(unsigned i=0; i< expr.operands().size(); i++) - rename_for_row(expr.operands()[i],row); + for(std::size_t i=0; ikind==IN) continue; + if(v.kind==IN) + continue; // x - add_template_row(v->var,v->pre_guard,v->post_guard, - v->aux_expr, v->kind); + add_template_row( + v.var, + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); // -x - add_template_row(unary_minus_exprt(v->var,v->var.type()), - v->pre_guard,v->post_guard,v->aux_expr, v->kind); + add_template_row( + unary_minus_exprt(v.var, v.var.type()), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); } } /*******************************************************************\ -Function: add_difference_template + Function: tpolyhedra_domaint::add_difference_template - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: x+-y<=c + Purpose: x+-y<=c \*******************************************************************/ -void tpolyhedra_domaint::add_difference_template(const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = var_specs.size()*(var_specs.size()-1); +void tpolyhedra_domaint::add_difference_template( + const var_specst &var_specs, + const namespacet &ns) +{ + std::size_t size=var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) + + for(var_specst::const_iterator v1=var_specs.begin(); + v1!=var_specs.end(); ++v1) { - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) + var_specst::const_iterator v2=v1; ++v2; + for(; v2!=var_specs.end(); ++v2) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; - if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + if(k==IN) + continue; + if(k==LOOP && v1->pre_guard!=v2->pre_guard) + continue; // TEST: we need better heuristics exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - // x1 - x2 - add_template_row(minus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); + // x1-x2 + add_template_row( + minus_exprt(v1->var, v2->var), pre_g, post_g, aux_expr, k); - // x2 - x1 - add_template_row(minus_exprt(v2->var,v1->var),pre_g,post_g,aux_expr,k); + // x2-x1 + add_template_row( + minus_exprt(v2->var, v1->var), pre_g, post_g, aux_expr, k); } } } /*******************************************************************\ -Function: add_quadratic_template + Function: tpolyhedra_domaint::add_quadratic_template - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: +-x^2<=c + Purpose: +-x^2<=c \*******************************************************************/ -void tpolyhedra_domaint::add_quadratic_template(const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size(); +void tpolyhedra_domaint::add_quadratic_template( + const var_specst &var_specs, + const namespacet &ns) +{ + unsigned size=2*var_specs.size(); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + + for(const auto v : var_specs) { - if(v->kind==IN) continue; + if(v.kind==IN) + continue; // x - add_template_row(mult_exprt(v->var,v->var), - v->pre_guard,v->post_guard,v->aux_expr,v->kind); + add_template_row( + mult_exprt(v.var, v.var), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); // -x - add_template_row(unary_minus_exprt(mult_exprt(v->var,v->var),v->var.type()), - v->pre_guard,v->post_guard,v->aux_expr,v->kind); - }} + add_template_row( + unary_minus_exprt(mult_exprt(v.var, v.var), v.var.type()), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); + } +} /*******************************************************************\ -Function: add_sum_template +Function: tpolyhedra_domaint::add_sum_template Inputs: @@ -990,34 +1194,94 @@ Function: add_sum_template \*******************************************************************/ -void tpolyhedra_domaint::add_sum_template(const var_specst &var_specs, - const namespacet &ns) +void tpolyhedra_domaint::add_sum_template( + const var_specst &var_specs, + const namespacet &ns) { - unsigned size = var_specs.size()*(var_specs.size()-1); + unsigned size=var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) + + for(var_specst::const_iterator v1=var_specs.begin(); + v1!=var_specs.end(); ++v1) { - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) + var_specst::const_iterator v2=v1; ++v2; + for(; v2!=var_specs.end(); ++v2) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; - if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + if(k==IN) + continue; + if(k==LOOP && v1->pre_guard!=v2->pre_guard) + continue; // TEST: we need better heuristics exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - // -x1 - x2 - add_template_row(minus_exprt(unary_minus_exprt(v1->var,v1->var.type()),v2->var), - pre_g,post_g,aux_expr,k); + // -x1-x2 + add_template_row( + minus_exprt(unary_minus_exprt(v1->var, v1->var.type()), v2->var), + pre_g, + post_g, + aux_expr, + k); + + // x1+x2 + add_template_row( + plus_exprt(v1->var, v2->var), pre_g, post_g, aux_expr, k); + } + } +} + +/*******************************************************************\ + +Function: tpolyhedra_domaint::refine + + Inputs: + + Outputs: + + Purpose: non-monotone condition refinement + +\*******************************************************************/ + +void tpolyhedra_domaint::replace_comparison(exprt &expr, bool greater) +{ + // TODO +} + +bool tpolyhedra_domaint::refine() +{ + return false; - // x1 + x2 - add_template_row(plus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); + // TODO + if(current_refinement==0) // initialise + { + if(refinement_exprs.size()==0) + { + max_refinements=0; + return false; } + max_refinements=3; + current_refinement=1; + exprt::operandst c; + // TODO + current_refinement_expr=conjunction(c); + return true; + } + + if(current_refinement>max_refinements) + return false; + + if(current_refinement==1) + { + exprt::operandst c; + // TODO + current_refinement_expr=conjunction(c); } + else if(current_refinement==2) + current_refinement_expr=true_exprt(); + current_refinement++; + return true; } diff --git a/src/domains/tpolyhedra_domain.h b/src/domains/tpolyhedra_domain.h index c1d61a8d9..efe7ad4b6 100644 --- a/src/domains/tpolyhedra_domain.h +++ b/src/domains/tpolyhedra_domain.h @@ -1,25 +1,34 @@ -#ifndef CPROVER_TEMPLATE_DOMAIN_H -#define CPROVER_TEMPLATE_DOMAIN_H +/*******************************************************************\ -#include "domain.h" +Module: Template polyhedra domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_TPOLYHEDRA_DOMAIN_H +#define CPROVER_2LS_DOMAINS_TPOLYHEDRA_DOMAIN_H + +#include #include #include #include -#include -class tpolyhedra_domaint : public domaint +#include "domain.h" + +class tpolyhedra_domaint:public domaint { public: typedef unsigned rowt; - typedef exprt row_exprt; + typedef exprt row_exprt; typedef constant_exprt row_valuet; // "bound" - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -30,15 +39,21 @@ class tpolyhedra_domaint : public domaint typedef std::vector templatet; - tpolyhedra_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number,_renaming_map, _ns) - {} + tpolyhedra_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns) + { + } // initialize value virtual void initialize(valuet &value); + virtual void reset_refinements() { current_refinement=0; } + virtual bool refine(); // non-monotone condition refinement + std::vector &refinement_expressions() { return refinement_exprs; } + virtual void join(valuet &value1, const valuet &value2); // value -> constraints @@ -49,42 +64,56 @@ class tpolyhedra_domaint : public domaint exprt get_row_post_constraint(const rowt &row, const templ_valuet &value); exprt to_pre_constraints(const templ_valuet &value); - void make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, - exprt::operandst &value_exprs); + void make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, + exprt::operandst &value_exprs); // value -> symbolic bound constraints (for optimization) exprt to_symb_pre_constraints(const templ_valuet &value); - exprt to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows); + exprt to_symb_pre_constraints( + const templ_valuet &value, + const std::set &symb_rows); exprt to_symb_post_constraints(const std::set &symb_rows); - exprt get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value, bool geq=false); - exprt get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value); + exprt get_row_symb_value_constraint( + const rowt &row, + const row_valuet &row_value, + bool geq=false); + exprt get_row_symb_pre_constraint( + const rowt &row, + const row_valuet &row_value); exprt get_row_symb_post_constraint(const rowt &row); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); // max, min, comparison - row_valuet get_max_row_value(const rowt &row); - row_valuet get_min_row_value(const rowt &row); + row_valuet get_max_row_value(const rowt &row) const; + row_valuet get_min_row_value(const rowt &row) const; row_valuet between(const row_valuet &lower, const row_valuet &upper); bool less_than(const row_valuet &v1, const row_valuet &v2); bool is_row_value_inf(const row_valuet & row_value) const; bool is_row_value_neginf(const row_valuet & row_value) const; + bool is_row_value_inf(const valuet &value, const rowt & row) const; + bool is_row_value_neginf(const valuet &value, const rowt & row) const; // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; + virtual void output_value( + std::ostream &out, const valuet &value, const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, const namespacet &ns) const; - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + // projection + virtual void project_on_vars( + valuet &value, const var_sett &vars, exprt &result); unsigned template_size(); + virtual bool is_spec_empty() const { return templ.size()==0; } // generating templates template_rowt &add_template_row( @@ -92,17 +121,16 @@ class tpolyhedra_domaint : public domaint const exprt& pre_guard, const exprt& post_guard, const exprt& aux_expr, - kindt kind - ); - - void add_interval_template(const var_specst &var_specs, - const namespacet &ns); - void add_difference_template(const var_specst &var_specs, - const namespacet &ns); - void add_sum_template(const var_specst &var_specs, - const namespacet &ns); - void add_quadratic_template(const var_specst &var_specs, - const namespacet &ns); + kindt kind); + + void add_interval_template( + const var_specst &var_specs, const namespacet &ns); + void add_difference_template( + const var_specst &var_specs, const namespacet &ns); + void add_sum_template( + const var_specst &var_specs, const namespacet &ns); + void add_quadratic_template( + const var_specst &var_specs, const namespacet &ns); symbol_exprt get_row_symb_value(const rowt &row); @@ -113,7 +141,13 @@ class tpolyhedra_domaint : public domaint friend class strategy_solver_enumerationt; templatet templ; - + + // non-monotone condition refinement + std::vector refinement_exprs; + unsigned current_refinement, max_refinements; + exprt current_refinement_expr; + + void replace_comparison(exprt &expr, bool greater); }; #endif diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 97e856df5..159a813d8 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -1,11 +1,18 @@ +/*******************************************************************\ + +Module: Domain utilities + +Author: Peter Schrammel + +\*******************************************************************/ + #include #include "util.h" - /*******************************************************************\ -Function: extend_expr_types +Function: get_bitvector_width Inputs: @@ -20,30 +27,44 @@ unsigned get_bitvector_width(const exprt &expr) return to_bitvector_type(expr.type()).get_width(); } +/*******************************************************************\ + +Function: extend_expr_types + + Inputs: + + Outputs: + + Purpose: increases bitvector sizes such that there are no overflows + +\*******************************************************************/ + void extend_expr_types(exprt &expr) { // std::cerr << "expr: " << expr << std::endl; - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - exprt new_expr = expr.op0(); + exprt new_expr=expr.op0(); extend_expr_types(new_expr); - expr = new_expr; + expr=new_expr; return; } - if(expr.id()==ID_constant) return; - if(expr.id()==ID_symbol) return; - if(expr.id()==ID_index) return; + if(expr.id()==ID_constant || + expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol || + expr.id()==ID_index) + return; if(expr.id()==ID_unary_minus) { extend_expr_types(expr.op0()); - if(expr.op0().type().id()==ID_signedbv || + if(expr.op0().type().id()==ID_signedbv || expr.op0().type().id()==ID_unsignedbv) { - typet new_type = signedbv_typet(get_bitvector_width(expr.op0())+1); - expr = unary_minus_exprt(typecast_exprt(expr.op0(),new_type),new_type); + typet new_type=signedbv_typet(get_bitvector_width(expr.op0())+1); + expr=unary_minus_exprt(typecast_exprt(expr.op0(), new_type), new_type); } - //TODO: shall we extend floats? + // TODO: shall we extend floats? return; } if(expr.id()==ID_plus || expr.id()==ID_minus) @@ -52,119 +73,138 @@ void extend_expr_types(exprt &expr) // std::cerr << "op0: " << expr.op0() << std::endl; extend_expr_types(expr.op1()); // std::cerr << "op1: " << expr.op1() << std::endl; - unsigned size0 = get_bitvector_width(expr.op0()); - unsigned size1 = get_bitvector_width(expr.op1()); - assert(size0>0); assert(size1>0); - typet new_type = expr.op0().type(); + unsigned size0=get_bitvector_width(expr.op0()); + unsigned size1=get_bitvector_width(expr.op1()); + assert(size0>0); assert(size1>0); + typet new_type=expr.op0().type(); if(expr.op0().type().id()==expr.op1().type().id()) { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(std::max(size0,size1)+1); - else if(new_type.id()==ID_unsignedbv) + if(new_type.id()==ID_signedbv) + new_type=signedbv_typet(std::max(size0, size1)+1); + else if(new_type.id()==ID_unsignedbv) { - if(expr.id()==ID_minus) - new_type = signedbv_typet(std::max(size0,size1)+1); - else - new_type = unsignedbv_typet(std::max(size0,size1)+1); + if(expr.id()==ID_minus) + new_type=signedbv_typet(std::max(size0, size1)+1); + else + new_type=unsignedbv_typet(std::max(size0, size1)+1); } - else if(new_type.id()==ID_floatbv) {} //TODO: shall we extend floats? - else assert(false); + else if(new_type.id()==ID_floatbv) + { + // TODO: shall we extend floats? + } + else + assert(false); } - else //operands do not have the same type + else // operands do not have the same type { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(size0<=size1 ? size1+2 : size0+1); - else if(new_type.id()==ID_unsignedbv) - new_type = signedbv_typet(size1<=size0 ? size0+2 : size1+1); - else assert(false); //TODO: implement floats + if(new_type.id()==ID_signedbv) + new_type=signedbv_typet(size0<=size1 ? size1+2 : size0+1); + else if(new_type.id()==ID_unsignedbv) + new_type=signedbv_typet(size1<=size0 ? size0+2 : size1+1); + else + assert(false); // TODO: implement floats } if(expr.id()==ID_plus) - expr = plus_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + expr=plus_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); else if(expr.id()==ID_minus) - expr = minus_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); - else assert(false); //TODO: implement floats + { + expr=minus_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); + } + else + assert(false); // TODO: implement floats return; } if(expr.id()==ID_mult) { extend_expr_types(expr.op0()); extend_expr_types(expr.op1()); - unsigned size0 = get_bitvector_width(expr.op0()); -// std::cerr << "expr1: " << expr.op1() << std::endl; - unsigned size1 = get_bitvector_width(expr.op1()); - - assert(size0>0); assert(size1>0); - if((expr.op0().type().id()==ID_unsignedbv || - expr.op0().type().id()==ID_signedbv) && - (expr.op1().type().id()==ID_unsignedbv || - expr.op1().type().id()==ID_signedbv)) + unsigned size0=get_bitvector_width(expr.op0()); + unsigned size1=get_bitvector_width(expr.op1()); + + assert(size0>0); assert(size1>0); + if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + (expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv)) { - typet new_type = signedbv_typet(size0+size1+1); - expr = mult_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + typet new_type=signedbv_typet(size0+size1+1); + expr=mult_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } - else if(expr.op0().type().id()==ID_floatbv && - expr.op1().type().id()==ID_floatbv) + else if(expr.op0().type().id()==ID_floatbv && + expr.op1().type().id()==ID_floatbv) { - // TODO: shall we extend floats? + // TODO: shall we extend floats? } - else if((expr.op0().type().id()==ID_unsignedbv || - expr.op0().type().id()==ID_signedbv) && - expr.op1().type().id()==ID_floatbv) + else if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + expr.op1().type().id()==ID_floatbv) { - typet new_type = expr.op1().type(); // TODO: shall we extend floats? - expr = mult_exprt(typecast_exprt(expr.op0(),new_type),expr.op1()); + typet new_type=expr.op1().type(); // TODO: shall we extend floats? + expr=mult_exprt(typecast_exprt(expr.op0(), new_type), expr.op1()); return; } - else if((expr.op1().type().id()==ID_unsignedbv || - expr.op1().type().id()==ID_signedbv) && - expr.op0().type().id()==ID_floatbv) + else if((expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv) && + expr.op0().type().id()==ID_floatbv) { - typet new_type = expr.op0().type(); // TODO: shall we extend floats? - expr = mult_exprt(expr.op0(),typecast_exprt(expr.op1(),new_type)); + typet new_type=expr.op0().type(); // TODO: shall we extend floats? + expr=mult_exprt(expr.op0(), typecast_exprt(expr.op1(), new_type)); return; } - else assert(false); + else + assert(false); } if(expr.id()==ID_div) { extend_expr_types(expr.op0()); extend_expr_types(expr.op1()); - unsigned size0 = get_bitvector_width(expr.op0()); - unsigned size1 = get_bitvector_width(expr.op1()); - assert(size0>0); assert(size1>0); - if((expr.op0().type().id()==ID_unsignedbv || expr.op0().type().id()==ID_signedbv) && - (expr.op1().type().id()==ID_unsignedbv || expr.op1().type().id()==ID_signedbv)) + unsigned size0=get_bitvector_width(expr.op0()); + unsigned size1=get_bitvector_width(expr.op1()); + assert(size0>0); + assert(size1>0); + if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + (expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv)) { typet new_type; - if(expr.op0().type().id()==ID_unsignedbv && expr.op0().type().id()==ID_unsignedbv) - new_type = unsignedbv_typet(std::max(size0,size1)); - else if(expr.op0().type().id()==ID_signedbv && expr.op0().type().id()==ID_unsignedbv) - new_type = signedbv_typet(size0>size1 ? size0 : size1+1); - else new_type = signedbv_typet(size0>=size1 ? size0+1 : size1); - - expr = div_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + if(expr.op0().type().id()==ID_unsignedbv && + expr.op0().type().id()==ID_unsignedbv) + new_type=unsignedbv_typet(std::max(size0, size1)); + else if(expr.op0().type().id()==ID_signedbv && + expr.op0().type().id()==ID_unsignedbv) + new_type=signedbv_typet(size0>size1 ? size0 : size1+1); + else + new_type=signedbv_typet(size0>=size1 ? size0+1 : size1); + + expr=div_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } - else if(expr.op0().type().id()==ID_floatbv || expr.op1().type().id()==ID_floatbv) + else if(expr.op0().type().id()==ID_floatbv || + expr.op1().type().id()==ID_floatbv) { - // TODO: shall we extend floats? + // TODO: shall we extend floats? return; } - else assert(false); + else + assert(false); } - std::cerr << "failed expr: " << expr << std::endl; + std::cerr << "failed expr: " << expr.pretty() << std::endl; assert(false); } - /*******************************************************************\ -Function: simplify_const +Function: simplify_const_int Inputs: @@ -176,69 +216,82 @@ Function: simplify_const mp_integer simplify_const_int(const exprt &expr) { - if(expr.id()==ID_constant) + if(expr.id()==ID_constant) { mp_integer v; to_integer(expr, v); return v; } - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - const exprt &op0 = expr.op0(); + const exprt &op0=expr.op0(); assert(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv); return simplify_const_int(op0); } - if(expr.id()==ID_unary_minus) return -simplify_const_int(expr.op0()); - if(expr.id()==ID_plus) + if(expr.id()==ID_unary_minus) + return -simplify_const_int(expr.op0()); + if(expr.id()==ID_plus) return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); - if(expr.id()==ID_minus) + if(expr.id()==ID_minus) return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); - if(expr.id()==ID_mult) - return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); - if(expr.id()==ID_div) + if(expr.id()==ID_mult) + return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); + if(expr.id()==ID_div) { - mp_integer d = simplify_const_int(expr.op1()); + mp_integer d=simplify_const_int(expr.op1()); assert(d!=0); - return simplify_const_int(expr.op0())/d; + return simplify_const_int(expr.op0())/d; } - if(expr.id()==ID_symbol) + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { #if 0 - std::cout << "substituting default value for " << expr << std::endl; + std::cerr << "substituting default value for " << expr << std::endl; #endif - return 0; //default value if not substituted in expr + return 0; // default value if not substituted in expr } - if(expr.id()==ID_index) + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_signedbv || array_type.id()==ID_unsignedbv) { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow + mp_integer mp_index=simplify_const_int(index_expr.index()); + unsigned index=integer2unsigned(mp_index); // TODO: might overflow assert(index<(index_expr.array().operands().size())); return simplify_const_int(index_expr.array().operands()[index]); } - assert(false); //not implemented + assert(false); // not implemented } - assert(false); //not implemented + assert(false); // not implemented } +/*******************************************************************\ + +Function: simplify_const_float + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + ieee_floatt simplify_const_float(const exprt &expr) { - if(expr.id()==ID_constant) + if(expr.id()==ID_constant) { ieee_floatt v(to_constant_expr(expr)); return v; } - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - const exprt &op0 = expr.op0(); + const exprt &op0=expr.op0(); if(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv) { ieee_floatt v; v.from_integer(simplify_const_int(op0)); - return v; + return v; } if(op0.type().id()==ID_floatbv) { @@ -246,131 +299,158 @@ ieee_floatt simplify_const_float(const exprt &expr) } assert(false); } - if(expr.id()==ID_unary_minus) + if(expr.id()==ID_unary_minus) { - ieee_floatt v = simplify_const_float(expr.op0()); + ieee_floatt v=simplify_const_float(expr.op0()); v.set_sign(!v.get_sign()); - return v; + return v; } - if(expr.id()==ID_plus) + if(expr.id()==ID_plus) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 += v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1+=v2; + return v1; } if(expr.id()==ID_minus) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 -= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1-=v2; + return v1; } if(expr.id()==ID_mult) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 *= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1*=v2; + return v1; } if(expr.id()==ID_div) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 /= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1/=v2; + return v1; } - if(expr.id()==ID_symbol) //default value if not substituted in expr + // default value if not substituted in expr + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { ieee_floatt v; v.make_zero(); + #if 0 - std::cout << "substituting default value for " << expr << std::endl; + std::cerr << "substituting default value for " << expr << std::endl; #endif - return v; + + return v; } - if(expr.id()==ID_index) + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_float) { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow + mp_integer mp_index=simplify_const_int(index_expr.index()); + unsigned index=integer2unsigned(mp_index); // TODO: might overflow assert(index<(index_expr.array().operands().size())); return simplify_const_float(index_expr.array().operands()[index]); } - assert(false); //not implemented + assert(false); // not implemented } - assert(false); //not implemented + assert(false); // not implemented } +/*******************************************************************\ + +Function: simplify_const + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + constant_exprt simplify_const(const exprt &expr) { // std::cerr << "const: " << expr << std::endl; - if(expr.id()==ID_constant) return to_constant_expr(expr); - //TODO: handle "address_of" constants - if(expr.id()==ID_index) + if(expr.id()==ID_constant) + return to_constant_expr(expr); + // TODO: handle "address_of" constants + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_signedbv) { - mp_integer res = simplify_const_int(index_expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); + mp_integer res=simplify_const_int(index_expr); + const signedbv_typet &type=to_signedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(array_type.id()==ID_unsignedbv) { - mp_integer res = simplify_const_int(index_expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); + mp_integer res=simplify_const_int(index_expr); + const unsignedbv_typet &type=to_unsignedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(array_type.id()==ID_float) return to_constant_expr(simplify_const_float(index_expr).to_expr()); - assert(false); //not implemented + assert(false); // not implemented } // if(expr.id()==ID_typecast) return to_constant_expr(expr.op0()); - if(expr.type().id()==ID_signedbv) + if(expr.type().id()==ID_signedbv) { - mp_integer res = simplify_const_int(expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); + mp_integer res=simplify_const_int(expr); + const signedbv_typet &type=to_signedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(expr.type().id()==ID_unsignedbv) { - mp_integer res = simplify_const_int(expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); + mp_integer res=simplify_const_int(expr); + const unsignedbv_typet &type=to_unsignedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(expr.type().id()==ID_floatbv) { return to_constant_expr(simplify_const_float(expr).to_expr()); } - assert(false); //type not supported + assert(false); // type not supported } +/*******************************************************************\ + +Function: remove_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void remove_typecast(exprt& expr) { - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); ++it) + Forall_operands(it, expr) remove_typecast(*it); - if (expr.id() == ID_typecast) - expr = expr.op0(); + if(expr.id()==ID_typecast) + expr=expr.op0(); } /*******************************************************************\ -Function: pretty_print_termination_argument() +Function: pretty_print_termination_argument Inputs: @@ -378,49 +458,56 @@ Function: pretty_print_termination_argument() Purpose: print ranking argument expressions in a more readable format -\******************************************************************/ +\*******************************************************************/ -void pretty_print_termination_argument(std::ostream &out, const namespacet &ns, const exprt &_expr) +void pretty_print_termination_argument( + std::ostream &out, + const namespacet &ns, + const exprt &_expr) { - exprt expr = _expr; + exprt expr=_expr; remove_typecast(expr); if(expr.id()==ID_and) { - // should be of the form /\_i g_i => R_i - for(exprt::operandst::const_iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) + // should be of the form /\_i g_i=> R_i + forall_operands(it, expr) { out << "\n"; - if(it == expr.operands().begin()) out << " "; - else out << "&& "; + if(it==expr.operands().begin()) + out << " "; + else + out << "&& "; if(it->id()==ID_implies) { - out << from_expr(ns,"",it->op0()) << " ==> "; + out << from_expr(ns, "", it->op0()) << "==> "; if(it->op1().id()==ID_gt) - out << from_expr(ns,"",it->op1().op0()); + out << from_expr(ns, "", it->op1().op0()); else if(it->op1().id()==ID_or) // needed for lexicographic ones { - for(exprt::operandst::const_iterator it_lex = it->op1().operands().begin(); - it_lex != it->op1().operands().end(); ++it_lex) + forall_operands(it_lex, it->op1()) { - if(it_lex->id() == ID_and) - { - if(it_lex == it->op1().operands().begin()) out << "("; - else out << "\n " << " " << ","; - out << from_expr(ns,"",it_lex->op0()); - } - else - { - out << "\n " << " " << "," - << from_expr(ns,"",*it_lex); - } + if(it_lex->id()==ID_and) + { + if(it_lex==it->op1().operands().begin()) + out << "("; + else + out << "\n " << " " << ", "; + out << from_expr(ns, "", it_lex->op0()); + } + else + { + out << "\n " << " " << ", " + << from_expr(ns, "", *it_lex); + } } out << ")"; } - else out << from_expr(ns,"",it->op1()); + else + out << from_expr(ns, "", it->op1()); } - else out << from_expr(ns,"",*it); + else + out << from_expr(ns, "", *it); } return; } @@ -430,12 +517,13 @@ void pretty_print_termination_argument(std::ostream &out, const namespacet &ns, { if(expr.op1().id()==ID_gt) { - out << from_expr(ns,"",expr.op0()) << " ==> " << from_expr(ns,"",expr.op1().op0()); - return; + out << from_expr(ns, "", expr.op0()) << "==> " + << from_expr(ns, "", expr.op1().op0()); + return; } } } - out << from_expr(ns,"",expr); + out << from_expr(ns, "", expr); } /*******************************************************************\ @@ -450,12 +538,13 @@ Function: merge_and \*******************************************************************/ -void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, - const namespacet &ns) +void merge_and( + exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns) { - result = expr1; - if(expr1!=expr2) result = and_exprt(expr1,expr2); - simplify(result,ns); + result=expr1; + if(expr1!=expr2) + result=and_exprt(expr1, expr2); + simplify(result, ns); } /*******************************************************************\ @@ -473,8 +562,8 @@ Function: make_zero constant_exprt make_zero(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(0),type); - + return from_integer(mp_integer(0), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -499,8 +588,8 @@ Function: make_one constant_exprt make_one(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(1),type); - + return from_integer(mp_integer(1), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -525,8 +614,8 @@ Function: make_minusone constant_exprt make_minusone(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(-1),type); - + return from_integer(mp_integer(-1), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -536,3 +625,69 @@ constant_exprt make_minusone(const typet &type) } assert(false); } + +/*******************************************************************\ + +Function: get_original_name + + Inputs: + + Outputs: + + Purpose: retrieve original variable name from ssa variable + +\*******************************************************************/ + +irep_idt get_original_name( + const symbol_exprt &symbol_expr) +{ + std::string s=id2string(symbol_expr.get_identifier()); + std::size_t pos1=s.find_last_of("#"); + if(pos1==std::string::npos || (s.substr(pos1+1, 12)=="return_value")) + return irep_idt(s); + return s.substr(0, pos1); +} + +/*******************************************************************\ + +Function: clean_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void clean_expr(exprt &expr) +{ + if(expr.id()==ID_symbol) + { + symbol_exprt &symbol_expr=to_symbol_expr(expr); + symbol_expr.set_identifier(get_original_name(symbol_expr)); + } + else if(expr.id()==ID_and) + { + Forall_operands(it, expr) + clean_expr(*it); + } + else if(expr.id()==ID_le) + { + if(expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast && + expr.op1().id()==ID_constant) + { + exprt lhs=expr.op0().op0().op0(); + clean_expr(lhs); + constant_exprt rhs=to_constant_expr(expr.op1()); + mp_integer c; + to_integer(rhs, c); + expr=binary_relation_exprt(lhs, ID_ge, from_integer(-c, lhs.type())); + } + else + { + clean_expr(expr.op0()); + } + } +} diff --git a/src/domains/util.h b/src/domains/util.h index 5c33fa2be..de0c316ef 100644 --- a/src/domains/util.h +++ b/src/domains/util.h @@ -1,5 +1,13 @@ -#ifndef CPROVER_DOMAIN_UTIL_H -#define CPROVER_DOMAIN_UTIL_H +/*******************************************************************\ + +Module: Domain utilities + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_UTIL_H +#define CPROVER_2LS_DOMAINS_UTIL_H #include #include @@ -12,12 +20,15 @@ void extend_expr_types(exprt &expr); constant_exprt simplify_const(const exprt &expr); ieee_floatt simplify_const_float(const exprt &expr); mp_integer simplify_const_int(const exprt &expr); -void pretty_print_termination_argument(std::ostream &out, - const namespacet &ns, const exprt &expr); -void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, - const namespacet &ns); +void pretty_print_termination_argument( + std::ostream &out, const namespacet &ns, const exprt &expr); +void merge_and( + exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns); constant_exprt make_zero(const typet &type); constant_exprt make_one(const typet &type); constant_exprt make_minusone(const typet &type); +irep_idt get_original_name(const symbol_exprt &); +void clean_expr(exprt &expr); + #endif diff --git a/src/functions/Makefile b/src/functions/Makefile deleted file mode 100644 index f73eb0b85..000000000 --- a/src/functions/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = call_graph.cpp summary.cpp index.cpp get_function.cpp path_util.cpp - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - - -CP_CXXFLAGS += $(SUMMARIZERFLAGS) - -INCLUDES= -I $(CBMC)/src - -CLEANFILES = - -all: $(OBJ) - -############################################################################### - diff --git a/src/functions/call_graph.cpp b/src/functions/call_graph.cpp deleted file mode 100644 index 3e214ff61..000000000 --- a/src/functions/call_graph.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include "call_graph.h" -#include "get_function.h" - -/*******************************************************************\ - -Function: call_grapht::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void call_grapht::build( - const indext &index, - const irep_idt &file, - const goto_modelt &model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=model.goto_functions.function_map.begin(); - new_fkt_it!=model.goto_functions.function_map.end(); - new_fkt_it++) - { - f_idt this_f_id; - this_f_id.file=file; - this_f_id.function_id=new_fkt_it->first; - - datat &data=file_map[file][new_fkt_it->first]; - - const goto_programt &body=new_fkt_it->second.body; - - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - const f_idt called_f_id=get_f_id(index, file, symbol.get_identifier()); - data.calls.insert(called_f_id); - file_map[called_f_id.file][called_f_id.function_id].called_by.insert(this_f_id); - } - } - } -} diff --git a/src/functions/call_graph.h b/src/functions/call_graph.h deleted file mode 100644 index 620898107..000000000 --- a/src/functions/call_graph.h +++ /dev/null @@ -1,66 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CALL_GRAPH_H -#define CPROVER_DELTACHECK_CALL_GRAPH_H - -#include "index.h" - -class call_grapht -{ -public: - // function names are not unique - struct f_idt - { - irep_idt file, function_id; - }; - - friend bool operator<(const f_idt &f1, const f_idt &f2) - { - if(f1.file calls; - std::set called_by; - }; - - // function ID to 'datat' map - typedef std::map function_mapt; - - // file to functions map - typedef std::map file_mapt; - file_mapt file_map; - - inline datat &operator[](const f_idt &f) - { - return file_map[f.file][f.function_id]; - } - - void build( - const indext &index, - const irep_idt &file, - const goto_modelt &); - -protected: - f_idt get_f_id( - const indext &index, - const irep_idt &file, - const irep_idt &function_id) - { - f_idt result; - result.file=index.get_file_for_function(file, function_id); - result.function_id=function_id; - return result; - } -}; - -#endif diff --git a/src/functions/get_function.cpp b/src/functions/get_function.cpp deleted file mode 100644 index 39bbb593f..000000000 --- a/src/functions/get_function.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "path_util.h" -#include "get_function.h" - -/*******************************************************************\ - -Function: get_functiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_functionst::goto_functiont * get_functiont::operator()(const irep_idt &id) -{ - // do we have it in our current file? - if(current_file_name!="") - { - const goto_functionst::function_mapt::iterator - f_it=goto_model.goto_functions.function_map.find(id); - - if(f_it!=goto_model.goto_functions.function_map.end()) - return &f_it->second; // found - } - - // find in index - indext::function_to_filet::const_iterator it= - index.function_to_file.find(id); - - if(it==index.function_to_file.end()) - return NULL; // not there - - // pick first file - assert(!it->second.empty()); - - irep_idt file_name=*(it->second.begin()); - - current_file_name=index.full_path(file_name); - - status() << "Reading \"" << id2string(current_file_name) << "\"" << eom; - - // read the file - goto_model.clear(); - - bool error=read_goto_binary( - id2string(current_file_name), - goto_model, - get_message_handler()); - - if(error) - return NULL; - - const goto_functionst::function_mapt::iterator - f_it=goto_model.goto_functions.function_map.find(id); - - assert(f_it!=goto_model.goto_functions.function_map.end()); - return &f_it->second; -} diff --git a/src/functions/get_function.h b/src/functions/get_function.h deleted file mode 100644 index 325f0f3bd..000000000 --- a/src/functions/get_function.h +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GET_FUNCTION_H -#define CPROVER_GET_FUNCTION_H - -#include -#include - -#include "index.h" - -class get_functiont:public messaget -{ -public: - explicit get_functiont(const indext &_index): - index(_index), ns(goto_model.symbol_table) - { - } - - goto_functionst::goto_functiont * operator()( - const irep_idt &function_id); - - inline irep_idt get_file_name() const - { - return current_file_name; - } - -protected: - const indext &index; - irep_idt current_file_name; - goto_modelt goto_model; - -public: - const namespacet ns; -}; - -#endif diff --git a/src/functions/index.cpp b/src/functions/index.cpp deleted file mode 100644 index 2de0dff1a..000000000 --- a/src/functions/index.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "index.h" -#include "path_util.h" - -#define INDEX_VERSION "1.0" - -/*******************************************************************\ - -Function: indext::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::build(const std::vector &files, - const std::string &_description) -{ - description=_description; - path_prefix=""; - - for(std::vector::const_iterator - f_it=files.begin(); - f_it!=files.end(); - f_it++) - index_goto_binary(*f_it); -} - -/*******************************************************************\ - -Function: indext::write - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::write(std::ostream &out) const -{ - out << "\n"; - - out << "\n"; - - out << ""; - xmlt::escape(description, out); - out << "\n"; - out << "\n"; - - for(file_to_functiont::const_iterator - it=file_to_function.begin(); - it!=file_to_function.end(); - it++) - { - out << "first), out); - out << "\">\n"; - - const std::set &functions=it->second; - - for(std::set::const_iterator - f_it=functions.begin(); f_it!=functions.end(); f_it++) - { - out << " \n"; - } - - out << "\n"; - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: indext::full_path - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string indext::full_path(const irep_idt &src) const -{ - return make_relative_path(path_prefix, id2string(src)); -} - -/*******************************************************************\ - -Function: indext::index_goto_binary - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::index_goto_binary(const irep_idt &file) -{ - std::string file_full_path=full_path(file); - - status() << "Reading `" << file_full_path << "'" << eom; - - goto_modelt goto_model; - - if(read_goto_binary(file_full_path, goto_model, get_message_handler())) - { - error() << "failed to read `" << file_full_path << "'" << eom; - return; - } - - // index the functions - for(goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.begin(); - f_it!=goto_model.goto_functions.function_map.end(); - f_it++) - { - if(f_it->second.body_available()) - { - function_to_file[f_it->first].insert(file); - file_to_function[file].insert(f_it->first); - } - } -} - -/*******************************************************************\ - -Function: indext::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::read(const std::string &in_file_name) -{ - file_name=in_file_name; - path_prefix=get_directory(in_file_name); - - // figure out if this is a goto-binary or an index - if(is_goto_binary(file_name)) - { - index_goto_binary(get_file_name(file_name)); - } - else - { - xmlt xml; - if(parse_xml(in_file_name, get_message_handler(), xml)) - { - error() << "failed to read index XML `" << in_file_name << "'" << eom; - return; - } - - if(xml.name!="DeltaCheckIndex") - { - error() << "index XML `" << in_file_name << "' is malformed" << eom; - return; - } - - description=xml.get_element("description"); - - for(xmlt::elementst::const_iterator - file_it=xml.elements.begin(); - file_it!=xml.elements.end(); - file_it++) - { - if(file_it->name!="file") continue; - - irep_idt file_name=file_it->get_attribute("name"); - - // create map entry - file_to_function[file_name]; - - for(xmlt::elementst::const_iterator - fkt_it=file_it->elements.begin(); - fkt_it!=file_it->elements.end(); - fkt_it++) - { - irep_idt function_id=fkt_it->get_attribute("id"); - function_to_file[function_id].insert(file_name); - file_to_function[file_name].insert(function_id); - } - } - } -} - -/*******************************************************************\ - -Function: indext::get_file_for_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -irep_idt indext::get_file_for_function( - const irep_idt &preferred_file, - const irep_idt &function_id) const -{ - function_to_filet::const_iterator - it=function_to_file.find(function_id); - - // found at all? - if(it==function_to_file.end()) - return preferred_file; // function not found - - // function is in preferred file? - if(it->second.find(preferred_file)!=it->second.end()) - return preferred_file; // ok as given - - assert(!it->second.empty()); - - return *it->second.begin(); // fix file -} diff --git a/src/functions/index.h b/src/functions/index.h deleted file mode 100644 index b27cac36d..000000000 --- a/src/functions/index.h +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_INDEX_H -#define CPROVER_DELTACHECK_INDEX_H - -#include -#include -#include -#include - -#include - -class indext:public messaget -{ -public: - // function names to file - typedef std::map > function_to_filet; - function_to_filet function_to_file; - - // file names to functions - typedef std::map > file_to_functiont; - file_to_functiont file_to_function; - - void read(const std::string &file); - - void write(std::ostream &) const; - - void build(const std::vector &files, - const std::string &description); - - void index_goto_binary(const irep_idt &file); - - std::string description, file_name, path_prefix; - - std::string full_path(const irep_idt &) const; - - inline std::string full_path(const file_to_functiont::const_iterator it) const - { - return full_path(it->first); - } - - irep_idt get_file_for_function( - const irep_idt &preferred_file, - const irep_idt &function_id) const; - -}; - -#endif diff --git a/src/functions/path_util.cpp b/src/functions/path_util.cpp deleted file mode 100644 index ff0f6cb1b..000000000 --- a/src/functions/path_util.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#ifdef _WIN32 -const char pathsep='\\'; -#else -const char pathsep='/'; -#endif - -/*******************************************************************\ - -Function: make_relative_path - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string make_relative_path( - const std::string &directory, - const std::string &src) -{ - // is src already absolute? - - #ifdef _WIN32 - if((src.size()>=2 && src[1]==':') || src[0]=='\\') - return src; - #else - if(src[0]=='/') - return src; - #endif - - // anything given? - if(directory.empty()) return src; - - // otherwise, stitch together - if(directory[directory.size()-1]==pathsep) - return directory+src; - else - return directory+std::string(1, pathsep)+src; -} - -/*******************************************************************\ - -Function: get_directory - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_directory(const std::string &src) -{ - std::size_t last_pos=src.rfind(pathsep); - - if(last_pos==std::string::npos) - return ""; // no directory given - - // cut off file name, but keep pathsep - return std::string(src, 0, last_pos+1); -} - -/*******************************************************************\ - -Function: get_file_name - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_file_name(const std::string &src) -{ - std::size_t last_pos=src.rfind(pathsep); - - if(last_pos==std::string::npos) - return src; // no directory given - - // cut off directory - return std::string(src, last_pos+1, std::string::npos); -} - diff --git a/src/functions/path_util.h b/src/functions/path_util.h deleted file mode 100644 index e55e6c772..000000000 --- a/src/functions/path_util.h +++ /dev/null @@ -1,18 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -std::string make_relative_path( - const std::string &directory, - const std::string &src); - -std::string get_directory(const std::string &src); - -std::string get_file_name(const std::string &src); - diff --git a/src/functions/summary.cpp b/src/functions/summary.cpp deleted file mode 100644 index 1726a1dd1..000000000 --- a/src/functions/summary.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/*******************************************************************\ - -Module: Function Summary - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "summary.h" - diff --git a/src/functions/summary.h b/src/functions/summary.h deleted file mode 100644 index c948f76fc..000000000 --- a/src/functions/summary.h +++ /dev/null @@ -1,31 +0,0 @@ -/*******************************************************************\ - -Module: Function Summary - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SUMMARY_H -#define DELTACHECK_SUMMARY_H - -#include "../solver/predicate.h" - -class summaryt -{ -public: - // signature - predicatet::state_var_listt entry_vars, exit_vars; - - // build from fixedpoint - void from_fixedpoint(class ssa_fixed_pointt &); - - // a summary has two parts: - // 1) pre-state (a predicate over entry_vars) - // 2) transformer (a predicate over entry_vars and exit_vars) - - predicatet pre_state; - predicatet transformer; -}; - -#endif diff --git a/src/html/Makefile b/src/html/Makefile deleted file mode 100644 index 51760b59b..000000000 --- a/src/html/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = logo.cpp html_escape.cpp syntax_highlighting.cpp - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -CLEANFILES = - -all: $(OBJ) - -############################################################################### - diff --git a/src/html/html_escape.cpp b/src/html/html_escape.cpp deleted file mode 100644 index 936a472ad..000000000 --- a/src/html/html_escape.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "html_escape.h" - -/*******************************************************************\ - -Function: html_escape - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string html_escape(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - for(unsigned i=0; i': result+=">"; break; - case '"': result+="""; break; - case '&': result+="&"; break; - - // ' does not seem to be universally supported, - // and Unicode seems to suggest to prefer ’ over ' - case '\'': result+="&8217;"; break; - - default: result+=src[i]; - } - - return result; -} - -/*******************************************************************\ - -Function: html_escape - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string html_escape(const dstring &src) -{ - return html_escape(as_string(src)); -} diff --git a/src/html/html_escape.h b/src/html/html_escape.h deleted file mode 100644 index 1be002cae..000000000 --- a/src/html/html_escape.h +++ /dev/null @@ -1,19 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_HTML_ESCAPE_H -#define DELTACHECK_HTML_ESCAPE_H - -#include - -#include - -std::string html_escape(const std::string &); -std::string html_escape(const dstring &); - -#endif diff --git a/src/html/logo.cpp b/src/html/logo.cpp deleted file mode 100644 index 5955ceedc..000000000 --- a/src/html/logo.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "logo.h" - -const char deltacheck_logo[]= - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPYAAAA4CAYAAADO8AUMAAAACXBIWXMAAASdAAAEnQF8NGuhAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAFKFJREFUeNrsXQt0VEWa/m+/EiTQzWMFBOygnplZHScd1BFFTKNHZMK4RGYBdwdNRx4+dhii43HOnNFNg+weZ8+MCSMMyCjpADo+UJrhiMo6pgO+dmFJWFgZUKEziLwUusmDPPtu1U3ddHXl1u3br9CB+s+pc5Pue6vvrarv//7/r7/qSpDFYsnJ4X7X1d4OQoQI0RYpi8EsUUX9XyZ/y1QRQBciJFuBTQHahIq5u7NzuByJuGiAS2bzfrPFEkJ/d6MSIUf1b1kAXIgQgqcsAjUGrxkB03Prrbcuvuuuu37gdrtjzgsEAnDo0KGj+/bt24/kHclk2mm2Wr9EX3Wg0kkALkSIkEyLLMtFW+oOyM+8GFDK16fPVbOgRgWz9I0I0A21tbWyEamoqMAMvQtdOx4VOyo2VCQ9v1yIECFpklNnmmtHTXtWzr11qVIwuJE4GVDftHDhwrNI4gIan/PEE0+0ILZeh651o3ItKpejkovrEsAWIqTHn80oW7/w1m53uDnq927Y1oAPXgJAEzK9b0Cg3r527VoHEt36GhoaYNrddx97rqrqOWSCv0s+jtA+thAhQjIUPBt2y7gCdHB8+PIe7w3zVrvZ71c+OSNUfPPYq64YM2bEnXfeuev99993xKvT7/fDwkWLdp0Nh1+TJOk4+ug0KidR+QaVc6i0odKNFIUAuJBLXixpAvJMdHCTgiPZMLlwElS+/LHm+f+x/kPHnQUzvcinLtq0aVNcUPt8Pnhw/vz3EEv7EajPYAufFPx3swpqwdpChKTI2AjMdnQoJ6UPOJ//9VoI1HdA44kQ7Kxv7Avuf7kdplzrAJfLpfs7Ho8H1m/Y8AoCdYCw80nC1mdRaYGeiLhiioupLiFCUvCxEagr0CGIfWUtUGO2xqB2F9rggeKrNetY+ebeuKBesGDBeQLqDwigj6HyNQF2EyrtKlNfgqB2iuErJC3Axr4zKvU8QKtyX/GDyvHVbevg2PH/hh9cM6rPOX87EYYde4K6oK6uqVmFQP0XCtQniPmNmVqZt8aAvsRAjS0lVbEWiSGsKDg8hVp6ET6TnHFgEz86oPrQPKHZ+qP6T2H1ay/B/cV/r3nu8nV1XPN7XXX1ZpPZfICA+mtyxOb3eVS6VFBfQlJAOjtEFOtAliKmOJOso5ooOA8q+RdB/y5BpZ56pqTFYhDUWBv6jJyL2RoDG7M1lnPN56CleTdcOdqusDQt2PfGrH37xGif4EDZhj9vPG8eav0cQRgHyI4TUOOL21I0vWt1vnPEUVoNpMHx0Y/K3n7s9M2olAxwIOOB6o4DwABpW1waOedggqm6CIBMi2F8GRXJAKhnkoaOK5itR4+Zp7D14n9b1Pv5+NHjYMHcSnjy99v7XDOl0AnbV/ZYUXhKa1bpT8AyzApWq7W5u1V6qPvbjp2Epc6nwZ8uIAAp57gSKnAbqM/ySXFrnOsjgyzcD6ZZiICjivoc31NdlgPaS7VdAwGvqiRphVrCKFYfubZRoy3UPqHBgM9dehGwtp9RWlLagU3mowN6/jQtaiT8xPGNihmuiizLsPyRZ+CZl08Bnayiyqe+RSC3nIA7ZtwBzZZWsNisYMqxgclmCcttkclth5sOQnqDZHYysOjnwoB5LM51M4lSoEGuAm5LP3W+PECAXUnaCshg9RqwcpykH0oYgJeQZ92rYYG5LyJga7F3UsCO52P7jIKa9a0pVEN3SwecPtwIP5szSfPala/9F6xYtQKaTC1gtloQoK0g2dDRYrZbhtpWQfoj32EN08eIVYLBO5UMphDFOH7i7wnpUZqbCahVpXevQdelkZzrodrXo+MmNVyE7RdMRyUmHbauiBcoY31rLKpvrZILppfLYRh4vV4E7Js1r934zl6YWnwvmG1mVHpAbbaYQDJJIElSUV7hyJ9nIFDmT+HaOmIu0QPLI8Adw7CqRVGTRB01jPIEjk8dEs2dALARqJ2UGZUCWwN0nGqB3y77TQ+1DcmFeT8q0Kxj5//JMHXyVJCsClMjUJswqPEqbHz0koSYbJIwGXwsuCsu4fFUSYG6HFILMO6F2MhwvoBr6oxdbtQE12PrSFcEfjihEEpKoi7TU/OLuKz909mPImAjUJujoKbM3fIsbL8wGcghJohzKc4vF1F9hOMyK9JQ5xaITu0JYKcCbMKMnlTZGrnW0H68GSp/+1xsdGSMA+6Z8l1tFX3YArffdBsLapoNs1EaNZSO9xI1wTPx/EuJ3+kScE0S2HgppSzLiIFkB0amzBQFrUwyzENze8b0Ozs2AY3qSHsX3Df9HzXTRnm+Nl7SOcP9DxqY7tHYZOotG6WGCXq4s4S1+ysjq5Ri1CCkP1LvScSCFEIBm9qeaGqkMwKd4XY4f/QcdHzbCt2tnSBHIhDBAI8QkCOA/1PxbNiy4xt4oPgq2LZzO+1aQ26TCSqe+lfNH8UJKXj+uo9d29wO3zY7Yfzo8bz7dWdxW3qzzMJQk0L6I8BET0/5M1B/XZb3fXYCWwV1V3v7VbbTkZmeyXNg6x/egBP7/wZvr30LlpX9Cka25kHXuXaIdEd6wT1neqkC41e3VceydVsXPPLTRZCfr+0W4USU+6d/T/O7la9/Cg/PfXQgAruGAdGFzBSzUwAL9cNv0c8ayNDv1Am4JsHYCNQTJk6cuPvg7s8cVVVVgDcSxDua4GN5eTl8se8QLJg2D9pPtkCkqxvunXoP+LYegnnTJ8Cftr0Ry9bNJuUaLQmFQlD28INw5PPtSpqpNmtfqWSraUi2+1k0W2HT0UgOdCr50jygBQyYrs403RPbJ8Es6o+CNLWtk2qTVK2odNRjGNh4E8Cje/bs+Q3eCZQnGPDPPr4MOpF5Pvm6OxDA2+CVrS8S3zuWrXnbHOE6mkzNsHr9Glg828X1tX85XzsJjGTDZauwjZevM1DU1TsBUjAgjqToE6ugphsWZ3LVMqUeYlNT1UFXq3FPZ8m9Og1aUXsvcB/Yyf3i+25IsW1LyXVBqk3kOO3BCo4LbWbaVa2nnnyfqLJS+7GCKtXUZ7HBs7zrRu5fUP6wsrcYTzATz5p8D6za+CFMvX4QvPbumyScJsdl62AwCL//4/NgzjFD0/lmcAz6Gux5fTcfxItFzjSPhqF5Q7WqyeYgStCAhVFKBpyDAF8i56k5wj5ILtFFC9TqPbiZ4mKsi1JKKXmIaV1FzHgH+ayBDKps7o8CiK6MCjH9kUjb2glIqkhxUO0Qotqj1EAdfoiuyPMyyt+VRExiL0QzQr1UcZN78gEdOEPFMvSGUcsHXz9SLrxpYtydQl033CJPn/djGV0jO26+QnZMGisPdY1StgXmSWlZqWwZZ5MHXTNYHvz94fJ1P75R2bVU3cGULt+ZVSU/++LvZMTQbEmXGVMEsW8USVe9dJ0VGqCWiQbXkmrq2moD9RcxVoCqyc9S59RrMHY1wyjqeVqDs56qS2t1XC1ovJmln6SC+t3N5LkrGTZ1UozJ6xdW6nXGRAHVvmc5ys4ep44ipl2dOuOSJ5XUPSzh+thIuiWTKWLOscBfw4e5rKuoaGRml86bA4EdtUoSSiQSUczwnDhsveHNDUpWmcliQba/Gb46eQxG5DVyWXtw3o081h6I4qTMX17jlkE0k82ToLLB8+lTSaFNrnLqc7WUUd/7NOIDvSEP5l7dkL07t6ir9h6D2BVhav65j/rMS8DHA4yLnF/HYcwSylqp4sRaXOR36jiBQDfVT/lJuAjlxCLB9azQBDbJw5YlSarFqZwWBLQ/vPpH0PO3MYCvGDwa5M4ukBG4u853QvHkaVzfuvzxx8A0FOeAmwFw2qi5Jxd8246tcM+U73F87QPwyNz5AxnMIQZADmKKNepcU8UMwExKEWVK8wZXncG4Ac1oFyq+oZeXXsaY5h6O8i3XUXR0mzRQyq6IAZ2b9L3eajM6azGUIKh95PddvJhGbOaZSZIx2DDocsbkweO/ekL3F55+8inobmpXouQdp1qVhR6aLY4UxNa//BnMVsTWOGXU0pM2ijNRcLYazlrTkv/94iSMHfNDPbBkuzRQA8bNCbDpPV+mGTJI/Z7bwHPEu98L6XMHDJzj1wn6sYo0kWW45Rp1GPGdsYIflkDAsZoCtRt09gGgp7vw4Yjy9jsMbosJ/nr6Sy5YFZXn8cAVeYi12zvhn4tnc+etly5f2svWkoWwtbK4o+d7nGPOWxyyftuXSiKMKmc/+WrvAAJ2iDIT6Y6XdYpfw8TMlDQSBvZwTEojypQFvTuL+yMQRwGVcGIZWsWl8cwzKYsm3UtKq0k/YWAXQpzNPWK2Rgp9eqxx2C3jQghvDgw+iyMHVq1fowCYB9qnn3waFvx8IZTeX8pl6x31O8FqtymgVtjapOaC9yAbs3bN3F8qC0FYwdsnPfDrWepcebavv3UyYNirMdh9kNhcry/D9xyG5JZW0qxPSzbnGoTi9J2DeiZfEvW6DFo5iYidKF0PUfplRi6yaGo1SVI0lwn5221DOmHpM0uh+qVqLmvjTDL2zZg0W5vziG9tia7cYvPB393xCkwpLNDcg1xlbQTubAe2i2P2ORigDpQsqgLyTPkJmMAlZDCGYWBJPqOsktmNJRPWSoAaV26jbWvi+SG9JrnNAls+eFuJanMdF79fl60V05vD1qpgRubtQY7Bfl9xmVG/5UKKmwPsbPBBjVoceOqETlTxxLnnRg3WHsgbL6bL6sjPwL04jGKAC2xQNzlAQHz04Qoom/9QwnfEsrWJw9aq4D3ItdJMe1j7MJz5+Gi2B85KKI2/pR+1ejoArW7lWwXRiCsO7Ew1YFZW6QSh0iVY4fTXJhYO4E+H9TewPUx7uo20Qx9gn/3kq979wDAA59w9Cz461A1gzdOd/jLK1pIGW6uC9yBfPKdQ8zvsf3925LQ3i0FNL10s1/Htso3NCghwPRSgH4PEUkN90HcboyVpvEc7ZH7qLx1WRyb6uYa4BQ2M4ixKlLEpjSvBbTfNRmfZYOy1Q2HZvz+TMlvr7blI70GuJZUvf+zGr+bNQlDTAy+gwdYBZtBn0zP4KVPbA8nleoc1gOeF9E3VeSGaA5ApaWSA6UmijgbGhE7nnD67U49fr301gY1YGz9kFQ5YbXzvCDww4zuw9cP34JODuwyxtsLWuxJja1VeefsN3U0Ps5S11Q3sgxxN7Y9jul4oKaKsjACktoBjBfSdTvKnwaQtpSygYD8oOdrkXZLC9enu50ZmbOm2r96bQLy3TJxV/vm2L+HqcTb4ReliOBcKwwtr1nAj4L0m9erVcPttU2DYiGEwcvQoGPF3IyFvyJCeeWwp/jbJRYUjuN+99cFnmLULUD3ZMJ9NT0WESMOHOZ3io1jARXzaeFMXS+L460b9xf4KJpVAbBTXRf53Q3JR8grKEgiBfsZeuiwDD/N/PIVnJ32rbrEcoOIoqnKIt/9bEcROj/KkDqJz2Wr7VmmNIy6wz3x81DPtZzVKRPruejS2ZORnd8kQOfmNwsg8cOPo+aZ3d4NlxHVgyh0MUq4VZBu61tSCw+xgZP9z77o/cb/DGzHMnzmxHAzO5yUQ3PBAYtNQRaRRXcQEK4kz8LzkHNrsVVcMhTWCWT6IrsbiiZtzz0HmnC0pgLeAUQ4unXZSd25lwd2QYPvSbauC2h3nnhMNVrk5CthLKRPV/Pdw2rCU3GdQQxnQrO0A/vSZqrx4/exkxpW6NbOHcRnK4priiBHtH+w67I2dU0anmqxgHnk98rV/xx+9y5aDeUg+SGackJILMjqifwyDOq4z19wOL23Z40H3mKr/5tH4H6+9xYsAZnL8FyfpzFrSefmkUwoNsEmjxm+WkEFRDdF1tZshmtzPgoH12co5/rqfea5ShmHw79QzA9IBfVeUFUDfhJryOOZ1mLSHlwFdgLTbTB3mo9vWRQXm8jlsls8ANZ7Zz4Jf616WQmxyimry1lN9VEnGio/qK5ZVWaV+hLC3utFCBfnMy/SzW6OPWSlj7tFDxk3vmJU4wK5Y/lKdd0c93Z94v7Nu6GpvhVMHd8Kq5Y/2YW3M1pOKZoBjrAsG2UfAYMcoyMkbjvRBDsK1OW32kiMvF15/dq4PmePJsPYS0lhGNXyDBmMEIfn3dhUxASveb7KBrGqdgI56P0sNnk+/lqiCAWEQoq8/UhWXD7TXentBP5HDqWHesu2qAi6fuT+1fbUUptarltR795O6azguk5YSDGiYy5UQf8trvecvpdgadPpNBbUToqvCeG3lZRR9vcb5ynvReMBmzS944fV1rq2BbVVdnV1wvqUFhrcPgc1vvhVLeR4P/Of/vA+O4Q4YbLejMgRyLxsEZrMx31rxDcyW0I+m3OVdNPvBuFlmqM5kMrg2MxHWEMSu1GE1fD51TigNQSZ1oHkg+iI6B0RfBujnmH0VGoBgmahGQ4mVQOwCFD8BTZgDlBB1H/R5duYcPxhPRbVTz6o+r4tpf/UYMGCyV1Imfohjnt+r4doEODGIfI5LpG4Iqd433TZ+A1Ya3WZuGnga7eckvxXUCBKq7cW+5dUOnASihGxjxyRExSBvjnRH8tuONsHBT/b35pDjvcyGjx0B1sttYM1FDJ1rA7PNEn2jh3F29AywhR5ChGSdmBI5ubn+mwa5IzJRAul526jLYlZ+4b3MzA4zWcFFZZkZq1oxMxCgCwWohQhJXRKOZpGtiq228Zddm9eWs+bgvgPKKzSv+v7V0GJtRWxtQ2ydo7w1s/dVPfqA9hNQN4ruECIkPWJJ8rrujqOtX5zuOvcLxNQf4V1TmjqbD1hzrVeCxTJYIrujSHwwq76en6SwChEi5EIyNmFtfJ0VO+/XTJiwNSLLlx0OBrFdHrKMyAHLyJxBllxrDpik6wljq0GLoGBmIUKyF9j4gOevBnV1dMxF4LWardb96P8mVE4RVsZbskQy8F5rIUKEZBDY+Fq8WVkeKkOJWd8GPVMGrah0CVALEXJhJKmskUh3t7KFsPovKp0E1OfJsRsVGZ8nRIiQ/hdLitdjUHdgdiYMHlGLYGshQgaYKc6Y5Gw9sgC1ECEXA+33vCJINIQQIUKECBGSKfl/AQYACOr3yNUYh1MAAAAASUVORK5CYII="; diff --git a/src/html/logo.h b/src/html/logo.h deleted file mode 100644 index bd5e0c3c5..000000000 --- a/src/html/logo.h +++ /dev/null @@ -1 +0,0 @@ -extern const char deltacheck_logo[]; diff --git a/src/html/syntax_highlighting.cpp b/src/html/syntax_highlighting.cpp deleted file mode 100644 index 5c6e078d0..000000000 --- a/src/html/syntax_highlighting.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*******************************************************************\ - -Module: Syntax Highlighting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -// may wish to try http://www.gnu.org/software/src-highlite/ - -#include -#include -#include -#include - -#include "../html/html_escape.h" -#include "syntax_highlighting.h" - -/*******************************************************************\ - -Function: is_keyword - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const char *keywords[]= -{ - "auto", "_Bool", "break", "case", "char", "_Complex", "const", "continue", - "default", "do", "double", "else", "enum", "extern", "float", "for", - "goto", "if", "inline", "int", "long", "register", "restrict", "return", - "short", "signed", "sizeof", "static", "struct", "switch", "typedef", - "union", "unsigned", "void", "volatile", "while", "__float128", - "__int128", "__int8", "__int16", "__int32", "__int64", "__ptr32", - "__ptr64", "__complex__", "__complex", "__real__" , "__real", "__imag__" , - "__imag", "offsetof", "__asm", "asm", "__asm__", "bool", "catch", "class", - "constexpr", "delete", "decltype", "explicit", "friend", "mutable", - "namespace", "new", "nullptr", "operator", "private", "protected", - "public", "static_assert", "template", "this", "thread_local", "throw", - "typeid", "typename", "using", "virtual", "wchar_t", "typeof", NULL -}; - -bool is_keyword(const std::string &token) -{ - for(unsigned i=0; keywords[i]!=NULL; i++) - { - if(strcmp(keywords[i], token.c_str())==0) - return true; - } - - return false; -} - -/*******************************************************************\ - - Class: tokenizert - - Purpose: - -\*******************************************************************/ - -const char *tokens[]= -{ "++", "+=", "--", "-=", "&&", "&=", "||", "|=", "/*", - "*/", "//", "%=", "/=", "<<", ">>", "<<=", ">>=", "==", - "!=", "<=", ">=", "::", "->", "##", ".*", "->*", NULL }; - -class tokenizert -{ -public: - explicit tokenizert(const std::string &_buf):buf(_buf) - { - } - - std::string get(); - std::string peek(); - std::string buf; - bool eol() const { return buf.empty(); } - - char get_char() - { - if(buf.empty()) return 0; - char result=buf[0]; - buf.erase(0, 1); - return result; - } - - static inline bool is_identifier_char(char ch) - { - return isalnum(ch) || ch=='_'; - } -}; - -/*******************************************************************\ - -Function: tokenizert::peek - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string tokenizert::peek() -{ - if(buf.empty()) return buf; - - char first=buf[0]; - - unsigned pos=1; - - if(is_identifier_char(first)) - { - // identifier or keyword or number - for(pos=1; pos"; - - if(comment) out << ""; - - std::string token; - - std::map var_count; - - while(!tokenizer.eol()) - { - if(comment) - { - std::string buf; - bool end_of_comment=false; - - while(!end_of_comment) - { - char ch=tokenizer.get_char(); - if(ch==0) break; - buf+=ch; - if(buf.size()>=2 && buf[buf.size()-2]=='*' && buf[buf.size()-1]=='/') - end_of_comment=true; - } - - out << html_escape(buf); - - if(end_of_comment) - { - out << ""; - comment=false; - } - } - else - { - token=tokenizer.get(); - assert(!token.empty()); - - if(isdigit(token[0])) // numeral - { - out << html_escape(token); - } - else if(isalpha(token[0])) - { - if(is_keyword(token)) - out << "" << html_escape(token) << ""; - else - { - if(identifier_tooltip) - out << ""; - else - out << ""; - - out << html_escape(token); - - out << ""; - } - } - else if(token=="/*") - { - comment=true; - out << "" << token; - } - else if(token=="//") - { - out << "" << token; - while(!(token=tokenizer.get()).empty()) - out << html_escape(token); - out << ""; - } - else if(token[0]=='"' || token[0]=='\'') - { - out << "" << html_escape(token) << ""; - } - else - { - // hack to distinguish lhs from rhs without parsing - #if 0 - if(token=="+=" || token=="=" || - token=="-=" || token=="<<=" || - token==">>=" || token=="&=" || - token=="^=" || token=="|=") - lhs=false; - else if(token==";") - lhs=true; - #endif - - out << html_escape(token); - } - } - } - - // close tags - if(comment) out << ""; - if(!strong_class.empty()) out << ""; - - out << "\n"; -} - diff --git a/src/html/syntax_highlighting.h b/src/html/syntax_highlighting.h deleted file mode 100644 index 159abcc28..000000000 --- a/src/html/syntax_highlighting.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Syntax Highlighting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SYNTAX_HIGHLIGHTING_H -#define CPROVER_SYNTAX_HIGHLIGHTING_H - -#include -#include - -class syntax_highlightingt -{ -public: - explicit syntax_highlightingt(std::ostream &_out): - line_no(0), identifier_tooltip(false), - out(_out), comment(false) { } - - std::string strong_class; - unsigned line_no; - std::string id_suffix; - - bool identifier_tooltip; - - void operator()(const std::string &line); - -protected: - std::ostream &out; - bool comment; -}; - -#endif diff --git a/src/html/to_c_string.perl b/src/html/to_c_string.perl deleted file mode 100755 index 9d1e7e220..000000000 --- a/src/html/to_c_string.perl +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -my @c = ; - -# print "static char* c_str = {\n"; -print "// generated file\n"; -foreach (@c) { - my @s = split(''); - print qq(\t"); - printf("\\x%02x", ord($_)) foreach @s; - print qq("\n); -} -# print "};\n"; - diff --git a/src/solver/Makefile b/src/solver/Makefile index bdbbed955..e473705db 100644 --- a/src/solver/Makefile +++ b/src/solver/Makefile @@ -1,18 +1,35 @@ +SRC = summarizer_base.cpp \ + summarizer_bw.cpp \ + summarizer_bw_term.cpp \ + summarizer_bw_cex.cpp \ + summarizer_bw_cex_ai.cpp \ + summarizer_bw_cex_complete.cpp \ + summarizer_bw_cex_concrete.cpp \ + summarizer_bw_cex_all.cpp \ + summarizer_bw_cex_wp.cpp \ + summarizer_fw_contexts.cpp \ + summarizer_fw.cpp \ + summarizer_fw_term.cpp \ + summary.cpp \ + summary_db.cpp \ + # empty last line + include ../config.inc +include $(CBMC)/src/config.inc +include $(CBMC)/src/common CBMC ?= ../.. -SRC = solver.cpp predicate.cpp fixed_point.cpp +CP_CXXFLAGS += $(TWOLSFLAGS) -include $(CBMC)/src/config.inc -include $(CBMC)/src/common +INCLUDES= -I $(CBMC)/src -I .. CP_CXXFLAGS += $(SUMMARIZERFLAGS) -INCLUDES= -I $(CBMC)/src - -CLEANFILES = +CLEANFILES = solver$(LIBEXT) -all: $(OBJ) +all: solver$(LIBEXT) ############################################################################### +solver$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/solver/fixed_point.cpp b/src/solver/fixed_point.cpp deleted file mode 100644 index ca5f09d23..000000000 --- a/src/solver/fixed_point.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/*******************************************************************\ - -Module: Forward Least Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" -#include "solver.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: fixed_pointt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::operator()() -{ - iteration_number=0; - - // Set up the state predicate, starting with 'false' - // (the empty set). - - state_predicate.state_vars=pre_state_vars; - state_predicate.make_false(); - - bool change; - - do - { - iteration_number++; - - #ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; - #endif - - change=iteration(); - } - while(change); - - #ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - output(std::cout); - #endif -} - -/*******************************************************************\ - -Function: fixed_pointt::iteration - - Inputs: - - Outputs: 'true' if there is a change in the state predicate - - Purpose: - -\*******************************************************************/ - -bool fixed_pointt::iteration() -{ - solvert solver(ns); - - // Feed transition relation into solver. - for(constraintst::const_iterator - it=transition_relation.begin(); - it!=transition_relation.end(); - it++) - solver << *it; - - // Feed current state predicate into solver. - state_predicate.set_to_true(solver); - - #ifdef DEBUG - std::cout << "Entry state:\n"; - output(std::cout); - #endif - - // solve - solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - // now get new post-state - predicatet post_state; - post_state.state_vars=post_state_vars; - - post_state.get(solver); - - #ifdef DEBUG - std::cout << "Post state:\n"; - post_state.output(std::cout); - #endif - - // Now 'OR' with previous state predicate. - // First rename post-state to pre-state. - post_state.rename(pre_state_vars); - - // Form disjunction of previous state predicate and the new one. - return state_predicate.disjunction(post_state); -} - -/*******************************************************************\ - -Function: fixed_pointt::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::output(std::ostream &out) const -{ - state_predicate.output(out); -} diff --git a/src/solver/fixed_point.h b/src/solver/fixed_point.h deleted file mode 100644 index 1c1e213ce..000000000 --- a/src/solver/fixed_point.h +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************************************\ - -Module: Forward Greatest Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_FIXED_POINT_H -#define DELTACHECK_FIXED_POINT_H - -#include "solver.h" -#include "predicate.h" - -class fixed_pointt -{ -public: - explicit fixed_pointt(const namespacet &_ns):ns(_ns) - { - } - - typedef std::list constraintst; - constraintst transition_relation; - - predicatet::state_var_listt pre_state_vars, post_state_vars; - - predicatet state_predicate; - - void output(std::ostream &) const; - - unsigned iteration_number; - - void operator()(); - -protected: - const namespacet &ns; - - // fixed-point iteration - void initialize(); - bool iteration(); -}; - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, - const std::list &src) -{ - for(std::list::const_iterator - c_it=src.begin(); c_it!=src.end(); c_it++) - dest << *c_it; - return dest; -} - -#endif diff --git a/src/solver/predicate.cpp b/src/solver/predicate.cpp deleted file mode 100644 index c831ca52a..000000000 --- a/src/solver/predicate.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Abstract State - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicate.h" - -/*******************************************************************\ - -Function: predicatet::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatet::make_false() -{ - uuf.resize(state_vars.size()); - - for(unsigned v1=0; v1 eq_count; - - for(unsigned v=0; v::const_iterator - e_it=eq_count.begin(); e_it!=eq_count.end(); e_it++) - { - if(e_it->second>=2) - { - for(unsigned v=0; vfirst==uuf.find(v)) - out << "Equal: " << from_expr(state_vars[v]) << "\n"; - out << "\n"; - } - } - - // print intervals - #if 0 - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); i_it!=integer_intervals.end(); i_it++) - { - if(i_it->lower_is_set || i_it->upper_is_set) - { - if(i_it->lower_is_set) - out << from_expr(i_it->lower) << " <= "; - - out << from_expr(vars[i_it-intervals.begin()]); - - if(i_it->upper_is_set) - out << " <= " << from_expr(i_it->lower); - - out << "\n"; - } - } - #endif -} - -/*******************************************************************\ - -Function: predicatet::disjunction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool predicatet::disjunction(const predicatet &other) -{ - bool change=false; - - // - // do equalities - // - - assert(other.state_vars.size()==state_vars.size()); - - unsigned_union_find new_uuf; - new_uuf.resize(uuf.size()); - - // can be done better than quadratic - for(unsigned v1=0; v1 -#include -#include - -#include "solver.h" - -struct predicatet -{ -public: - predicatet() - { - } - - // support set of the predicate - typedef std::vector state_var_listt; - state_var_listt state_vars; - - void output(std::ostream &) const; - void make_false(); - - // returns 'true' iff predicate is weakened - bool disjunction(const predicatet &); - - // rename supporting set of variables - void rename(const state_var_listt &new_state_vars); - - // read the predicate from a solver state - void get(const solvert &); - - // push the predicate to a solver as constraint - void set_to_true(decision_proceduret &) const; - - bool is_bottom() const - { - return false; - } - - bool is_top() const - { - for(unsigned i=0; i integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - integer_intervalst integer_intervals; - ieee_float_intervalst ieee_float_intervals; -}; - -static inline std::ostream & operator << ( - std::ostream &out, const predicatet &predicate) -{ - predicate.output(out); - return out; -} - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, const predicatet &predicate) -{ - predicate.set_to_true(dest); - return dest; -} - -#endif diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp deleted file mode 100644 index c9b579d18..000000000 --- a/src/solver/solver.cpp +++ /dev/null @@ -1,954 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Solver - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include -#include - -#include - -#include -#include -#include -#include - -#include "solver.h" - -/*******************************************************************\ - -Function: solvert::solvert - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -solvert::solvert(const namespacet &_ns):decision_proceduret(_ns) -{ - false_nr=add(false_exprt()); - true_nr=add(true_exprt()); -} - -/*******************************************************************\ - -Function: solvert::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned solvert::add(const exprt &expr) -{ - exprt tmp=expr; - simplify(tmp, ns); - return add_rec(tmp); -} - -/*******************************************************************\ - -Function: solvert::add_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned solvert::add_rec(const exprt &expr) -{ - // we do a mild bit of canonicalization - - if(expr.id()==ID_ge) - { - // rewrite x>=y to y<=x - exprt tmp=expr; - tmp.id(ID_le); - assert(tmp.operands().size()==2); - std::swap(tmp.op0(), tmp.op1()); - return add_rec(tmp); - } - else if(expr.id()==ID_gt) - { - // rewrite x>y to y::number_type a_nr, b_nr; - if(expr_numbering.get_number(tmp_a, a_nr)) return false; - if(expr_numbering.get_number(tmp_b, b_nr)) return false; - return is_equal(a_nr, b_nr); -} - -/*******************************************************************\ - -Function: solvert::add_operands - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void solvert::add_operands(unsigned nr) -{ - // expr_numbering is a vector, and thus not stable. - // We will call add recursively below. - const exprt expr=expr_numbering[nr]; - - const exprt::operandst &expr_op=expr.operands(); - std::vector dest; - - dest.resize(expr_op.size()); - - for(unsigned i=0; i !x==y - set_equal(not_exprt(equal_exprt(expr.op0(), expr.op1())), - expr); - } - else if(expr.id()==ID_equal) - { - add_operands(nr); - equal_list.push_back(nr); - } - else if(expr.id()==ID_address_of) - { - // NOT an uninterpreted function, but rather a constant. - } - else - { - if(expr.has_operands()) // make it uninterpreted - { - add_operands(nr); - uf_map[expr.id()].push_back(nr); - - #ifdef DEBUG - std::cout << "UF " << nr << " added: " << expr.id(); - forall_operands(it, expr) std::cout << " " << add(*it); - std::cout << "\n"; - #endif - } - } -} - -/*******************************************************************\ - -Function: solvert::dec_solve - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -decision_proceduret::resultt solvert::dec_solve() -{ - bool progress; - - do - { - progress=false; - - // rummage through things that are equal to 'true' - // and that we haven't processed yet - - for(unsigned i=0; i !x, !y - { - for(unsigned i=0; i a==true && b==true - { - for(std::vector::const_iterator - o_it=se.op.begin(); o_it!=se.op.end(); o_it++) - implies_equal(*o_it, true_nr, progress); - } - } - - for(solver_expr_listt::const_iterator - not_it=not_list.begin(); - not_it!=not_list.end(); - not_it++) - { - unsigned e_nr=*not_it; - const solver_exprt &se=expr_map[e_nr]; - - if(is_true(se.op[0])) // !true == false - { - implies_equal(false_nr, e_nr, progress); - } - else if(is_false(se.op[0])) // !false == true - { - implies_equal(true_nr, e_nr, progress); - } - - if(is_true(e_nr)) // !true == false - { - implies_equal(false_nr, se.op[0], progress); - } - else if(is_false(e_nr)) // !false == true - { - implies_equal(true_nr, se.op[0], progress); - } - } - - for(solver_expr_listt::const_iterator - equal_it=equal_list.begin(); - equal_it!=equal_list.end(); - equal_it++) - { - unsigned e_nr=*equal_it; - const solver_exprt &se=expr_map[e_nr]; - - unsigned op0=equalities.find(se.op[0]); - unsigned op1=equalities.find(se.op[1]); - - // Is it equal? - if(is_equal(op0, op1)) - implies_equal(true_nr, e_nr, progress); - - // Is there a disequality for this equality? - for(disequalitiest::const_iterator - d_it=disequalities.begin(); - d_it!=disequalities.end(); - d_it++) - { - const std::set &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - if(is_equal(d_it->first, op0) && - is_equal(*diseq_it, op1)) - { - implies_equal(false_nr, e_nr, progress); - } - else if(is_equal(d_it->first, op1) && - is_equal(*diseq_it, op0)) - { - implies_equal(false_nr, e_nr, progress); - } - } - } - } - - for(uf_mapt::const_iterator - uf_map_it=uf_map.begin(); - uf_map_it!=uf_map.end(); - uf_map_it++) - { - const solver_expr_listt &uf_list=uf_map_it->second; - - // boo, quadratic! - for(solver_expr_listt::const_iterator - uf_it1=uf_list.begin(); - uf_it1!=uf_list.end(); - uf_it1++) - { - solver_expr_listt::const_iterator next=uf_it1; - next++; - - for(solver_expr_listt::const_iterator - uf_it2=next; - uf_it2!=uf_list.end(); - uf_it2++) - { - unsigned e_nr1=*uf_it1, e_nr2=*uf_it2; - const solver_exprt &se1=expr_map[e_nr1], - &se2=expr_map[e_nr2]; - - // same number of arguments? - if(se1.op.size()!=se2.op.size()) continue; - - // already equal? - if(is_equal(e_nr1, e_nr2)) continue; - - bool all_equal=true; - - for(unsigned i=0; i &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - if(is_equal(d_it->first, *diseq_it)) - return D_UNSATISFIABLE; - } - } - - for(unsigned i=0; i c+1 <= x - else - --int_val; // x < c ==> x <= c-1 - } - - integer_intervalt new_interval; - - if(lower_upper==LOWER) - new_interval.set_lower(int_val); - else - new_interval.set_upper(int_val); - - integer_intervalt &interval=integer_intervals[add(what)]; - - if(interval.meet(new_interval)) - progress=true; - } - else if(type.id()==ID_floatbv) - { - ieee_floatt float_val(bound); - if(weak_strict!=WEAK) return; - - ieee_float_intervalt new_interval; - - if(lower_upper==LOWER) - new_interval.set_lower(float_val); - else - new_interval.set_upper(float_val); - - ieee_float_intervalt &interval=ieee_float_intervals[add(what)]; - - if(interval.meet(new_interval)) - progress=true; - } -} - -/*******************************************************************\ - -Function: solvert::is_a_constant - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool solvert::is_a_constant(const exprt &expr) const -{ - if(expr.is_constant()) return true; - - if(expr.id()==ID_address_of && - expr.operands().size()==1 && - expr.op0().id()==ID_symbol) - return true; - - return false; -} - -/*******************************************************************\ - -Function: solvert::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt solvert::get(const exprt &expr) const -{ - // is it already a constant? - if(expr.is_constant()) - return expr; - - // is it an equality? - if(expr.id()==ID_equal) - { - if(is_equal(to_equal_expr(expr).lhs(), - to_equal_expr(expr).rhs())) - return true_exprt(); - } - else if(expr.id()==ID_not || - expr.id()==ID_and || - expr.id()==ID_or || - expr.id()==ID_implies) - { - exprt tmp=expr; - Forall_operands(it, tmp) - *it=get(*it); // recursive call - return tmp; - } - - exprt tmp=expr; - simplify(tmp, ns); - - numbering::number_type nr; - - if(!expr_numbering.get_number(tmp, nr)) - { - // Equal to some constant? - for(unsigned i=0; i > equality_map; - - for(unsigned i=0; i >::const_iterator - e_it=equality_map.begin(); - e_it!=equality_map.end(); - e_it++) - { - const std::set &eq_set=e_it->second; - - if(eq_set.size()>=2) - { - for(std::set::const_iterator - eq_it=eq_set.begin(); eq_it!=eq_set.end(); eq_it++) - { - out << "Equal: " - << from_expr(ns, "", expr_numbering[*eq_it]) << "\n"; - } - - out << "\n"; - } - } - - // disequalities - - for(disequalitiest::const_iterator - d_it=disequalities.begin(); - d_it!=disequalities.end(); - d_it++) - { - const std::set &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - out << "Disequal: " - << from_expr(ns, "", expr_numbering[d_it->first]) - << " != " - << from_expr(ns, "", expr_numbering[*diseq_it]) - << "\n"; - } - } - - // intervals - - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); - i_it!=integer_intervals.end(); i_it++) - { - const integer_intervalt &interval=*i_it; - - if(interval.is_top()) continue; - - out << "Integer interval: "; - - if(interval.lower_set) - out << interval.lower << " <= "; - - out << from_expr(ns, "", expr_numbering[i_it-integer_intervals.begin()]); - - if(interval.upper_set) - out << " <= " << interval.upper; - - if(interval.is_bottom()) out << " (bottom)"; - - out << "\n"; - } - - for(ieee_float_intervalst::const_iterator - i_it=ieee_float_intervals.begin(); - i_it!=ieee_float_intervals.end(); i_it++) - { - const ieee_float_intervalt &interval=*i_it; - - if(interval.is_top()) continue; - - out << "Floating-point interval: "; - - if(interval.lower_set) - out << interval.lower << " <= "; - - out << from_expr(ns, "", expr_numbering[i_it-ieee_float_intervals.begin()]); - - if(interval.upper_set) - out << " <= " << interval.upper; - - if(interval.is_bottom()) out << " (bottom)"; - - out << "\n"; - } - -} - diff --git a/src/solver/solver.h b/src/solver/solver.h deleted file mode 100644 index de961fea6..000000000 --- a/src/solver/solver.h +++ /dev/null @@ -1,182 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Solver - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SOLVER_H -#define CPROVER_DELTACHECK_SOLVER_H - -#include - -#include -#include -#include - -#include - -class solvert:public decision_proceduret -{ -public: - // standard solver interface - - explicit solvert(const namespacet &_ns); - - virtual exprt get(const exprt &expr) const; - virtual void print_assignment(std::ostream &out) const; - virtual void set_to(const exprt &expr, bool value); - - virtual resultt dec_solve(); - - virtual std::string decision_procedure_text() const - { - return "DeltaCheck equality+UF solver"; - } - - inline void set_equal(const exprt &a, const exprt &b) - { - set_equal(add(a), add(b)); - } - - bool is_equal(const exprt &a, const exprt &b) const; - - inline void add_expression(const exprt &expr) - { - add(expr); - } - -protected: - // Used to determine whether an expression is suitable - // as value for a model. - bool is_a_constant(const exprt &expr) const; - - void set_to_rec(const exprt &expr, bool value); - - // simplify and add expression, return its handle - unsigned add(const exprt &expr); - - // recursively add a simplified expression, returns its handle - unsigned add_rec(const exprt &expr); - - // make 'a' and 'b' equal - inline void set_equal(unsigned a, unsigned b) - { - equalities.make_union(a, b); - } - - // make 'a' and 'b' equal, and track if - // this wasn't the case before - inline void implies_equal(unsigned a, unsigned b, bool &progress) - { - if(is_equal(a, b)) return; // no progres - equalities.make_union(a, b); - progress=true; // progress! - } - - // add a bound, and track if this is a new bound - enum weak_strictt { WEAK, STRICT }; - enum lower_uppert { LOWER, UPPER }; - - void bound(const constant_exprt &bound, - const exprt &what, - weak_strictt weak_strict, - lower_uppert lower_upper, - bool &progress); - - // a numbering for expressions - numbering expr_numbering; - - // equality logic - unsigned_union_find equalities; - - inline bool is_equal(unsigned a, unsigned b) const - { - return equalities.find(a)==equalities.find(b); - } - - // Disequalities; the smaller index is the key. - // This should really use roots from the equalities. - typedef std::map > disequalitiest; - disequalitiest disequalities; - - inline bool is_disequal(unsigned a, unsigned b) const - { - if(a>b) std::swap(a, b); - disequalitiest::const_iterator it=disequalities.find(a); - if(it==disequalities.end()) return false; - return it->second.find(b)!=it->second.end(); - } - - void implies_disequal(unsigned a, unsigned b, bool &progress) - { - if(a>b) std::swap(a, b); - if((disequalities[a].insert(b)).second) - progress=true; - } - - void set_disequal(unsigned a, unsigned b) - { - if(a>b) std::swap(a, b); - disequalities[a].insert(b); - } - - // further data per expression - struct solver_exprt - { - // the numbers of the operands - std::vector op; - - // the numbers of the expressions that contain this one - std::vector operand_of; - - bool predicate_processed; - - solver_exprt():predicate_processed(false) - { - } - }; - - typedef expanding_vector expr_mapt; - expr_mapt expr_map; - - // lists of expressions with particular IDs - typedef std::vector solver_expr_listt; - solver_expr_listt if_list, or_list, and_list, not_list, equal_list; - - // uninterpreted functions (and predicates), mapping - // expression id -> to the list of expressions of this kind - typedef std::map uf_mapt; - uf_mapt uf_map; - - // builds above solver_exprt for given expression - solver_exprt build_solver_expr(unsigned nr); - - // called to recurse over the operands of a new expression - void add_operands(unsigned nr); - - // called after new expresion with given number has been added - void new_expression(unsigned nr); - - // handy numbers of well-known constants - unsigned false_nr, true_nr; - - inline bool is_true(unsigned a) const - { - return is_equal(a, true_nr); - } - - inline bool is_false(unsigned a) const - { - return is_equal(a, false_nr); - } - - // interval domain - typedef expanding_vector integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - ieee_float_intervalst ieee_float_intervals; - integer_intervalst integer_intervals; -}; - -#endif diff --git a/src/summarizer/summarizer_base.cpp b/src/solver/summarizer_base.cpp similarity index 52% rename from src/summarizer/summarizer_base.cpp rename to src/solver/summarizer_base.cpp index 489d9d3ae..9a951bda9 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/solver/summarizer_base.cpp @@ -17,17 +17,17 @@ Author: Peter Schrammel #include "summarizer_base.h" #include "summary_db.h" -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" +#include +#include +#include -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" +#include +#include /*******************************************************************\ -Function: summarizer_baset::summarize() +Function: summarizer_baset::summarize Inputs: @@ -39,22 +39,23 @@ Function: summarizer_baset::summarize() void summarizer_baset::summarize() { - exprt precondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); + exprt precondition=true_exprt(); // initial calling context + for(functionst::const_iterator it=ssa_db.functions().begin(); it!=ssa_db.functions().end(); it++) { status() << "\nSummarizing function " << it->first << eom; - if(!summary_db.exists(it->first) || - summary_db.get(it->first).mark_recompute) - compute_summary_rec(it->first,precondition,false); - else status() << "Summary for function " << it->first << - " exists already" << eom; + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) + compute_summary_rec(it->first, precondition, false); + else + status() << "Summary for function " << it->first + << " exists already" << eom; } } /*******************************************************************\ -Function: summarizer_baset::summarize() +Function: summarizer_baset::summarize Inputs: @@ -66,22 +67,25 @@ Function: summarizer_baset::summarize() void summarizer_baset::summarize(const function_namet &function_name) { - exprt precondition = true_exprt(); //initial calling context + exprt precondition=true_exprt(); // initial calling context status() << "\nSummarizing function " << function_name << eom; - if(!summary_db.exists(function_name) || - summary_db.get(function_name).mark_recompute) + if(!summary_db.exists(function_name) || + summary_db.get(function_name).mark_recompute) { - compute_summary_rec(function_name,precondition, - options.get_bool_option("context-sensitive")); + compute_summary_rec( + function_name, + precondition, + options.get_bool_option("context-sensitive")); } - else status() << "Summary for function " << function_name << - " exists already" << eom; + else + status() << "Summary for function " << function_name + << " exists already" << eom; } /*******************************************************************\ -Function: summarizer_baset::check_call_reachable() +Function: summarizer_baset::check_call_reachable Inputs: @@ -89,25 +93,25 @@ Function: summarizer_baset::check_call_reachable() Purpose: returns false if function call is not reachable -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_call_reachable( const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt& precondition, bool forward) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); debug() << "Checking reachability of call to " << fname << eom; - bool reachable = false; + bool reachable=false; // reachability check - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -117,29 +121,37 @@ bool summarizer_baset::check_call_reachable( solver << precondition; solver << ssa_inliner.get_summaries(SSA); - symbol_exprt guard = SSA.guard_symbol(n_it->location); - ssa_unwinder.get(function_name).unwinder_rename(guard,*n_it,false); + symbol_exprt guard=SSA.guard_symbol(n_it->location); + ssa_unwinder.get(function_name).unwinder_rename(guard, *n_it, false); solver << guard; #if 0 std::cout << "guard: " << from_expr(SSA.ns, "", guard) << std::endl; - std::cout << "enable: " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) << std::endl; - std::cout << "precondition: " << from_expr(SSA.ns, "", precondition) << std::endl; - std::cout << "summaries: " << from_expr(SSA.ns, "", ssa_inliner.get_summaries(SSA)) << std::endl; + std::cout << "enable: " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << std::endl; + std::cout << "precondition: " << from_expr(SSA.ns, "", precondition) + << std::endl; + std::cout << "summaries: " + << from_expr(SSA.ns, "", ssa_inliner.get_summaries(SSA)) + << std::endl; #endif - if(!forward) + if(!forward) solver << SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); switch(solver()) { - case decision_proceduret::D_SATISFIABLE: { - reachable = true; + case decision_proceduret::D_SATISFIABLE: + { + reachable=true; debug() << "Call is reachable" << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { debug() << "Call is not reachable" << eom; - break; } + break; + } default: assert(false); break; } @@ -150,7 +162,7 @@ bool summarizer_baset::check_call_reachable( /*******************************************************************\ -Function: summarizer_baset::compute_calling_context () +Function: summarizer_baset::compute_calling_context Inputs: @@ -159,23 +171,23 @@ Function: summarizer_baset::compute_calling_context () Purpose: computes callee preconditions from the calling context for a single function call -\******************************************************************/ +\*******************************************************************/ exprt summarizer_baset::compute_calling_context( - const function_namet &function_name, + const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt &precondition, bool forward) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); status() << "Computing calling context for function " << fname << eom; // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -188,40 +200,44 @@ exprt summarizer_baset::compute_calling_context( analyzer.set_message_handler(get_message_handler()); template_generator_callingcontextt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); + options, ssa_db, ssa_unwinder.get(function_name)); template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,n_it,f_it,forward); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, forward); // collect globals at call site - std::map + std::map cs_globals_in; - if(forward) SSA.get_globals(n_it->location,cs_globals_in[f_it]); - else SSA.get_globals(n_it->location,cs_globals_in[f_it],false); + if(forward) + SSA.get_globals(n_it->location, cs_globals_in[f_it]); + else + SSA.get_globals(n_it->location, cs_globals_in[f_it], false); - exprt cond = precondition; - if(!forward) cond = and_exprt(cond, - SSA.guard_symbol(--SSA.goto_function.body.instructions.end())); + exprt cond=precondition; + if(!forward) + cond=and_exprt( + cond, SSA.guard_symbol(--SSA.goto_function.body.instructions.end())); // analyze - analyzer(solver,SSA,cond,template_generator); + analyzer(solver, SSA, cond, template_generator); // set preconditions - local_SSAt &fSSA = ssa_db.get(fname); + local_SSAt &fSSA=ssa_db.get(fname); preconditiont precondition_call; - analyzer.get_result(precondition_call, - template_generator.callingcontext_vars()); - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_in[f_it],fSSA.globals_in, - precondition_call); + analyzer.get_result( + precondition_call, + template_generator.callingcontext_vars()); + ssa_inliner.rename_to_callee( + f_it, fSSA.params, cs_globals_in[f_it], fSSA.globals_in, precondition_call); - debug() << (forward ? "Forward " : "Backward ") << "calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", precondition_call) << eom; + debug() << (forward ? "Forward " : "Backward ") << "calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", precondition_call) << eom; - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); solver.pop_context(); @@ -230,34 +246,28 @@ exprt summarizer_baset::compute_calling_context( /*******************************************************************\ -Function: summarizer_baset::get_assertions() +Function: summarizer_baset::get_assertions Inputs: Outputs: - Purpose: + Purpose: -\******************************************************************/ +\*******************************************************************/ -void summarizer_baset::get_assertions(const local_SSAt &SSA, - exprt::operandst &assertions) +void summarizer_baset::get_assertions( + const local_SSAt &SSA, + exprt::operandst &assertions) { - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator - a_it = n_it->assertions.begin(); - a_it != n_it->assertions.end(); a_it++) - { - assertions.push_back(*a_it); - } - } + for(const auto &node : SSA.nodes) + for(const auto &a : node.assertions) + assertions.push_back(a); } /*******************************************************************\ -Function: summarizer_baset::check_precondition() +Function: summarizer_baset::check_precondition Inputs: @@ -265,77 +275,78 @@ Function: summarizer_baset::check_precondition() Purpose: returns false if the summary needs to be recomputed -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_precondition( const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt &precondition, bool context_sensitive) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); status() << "Checking precondition of " << fname << eom; - bool precondition_holds = false; + bool precondition_holds=false; exprt assertion; - if(summary_db.exists(fname)) + if(summary_db.exists(fname)) { - summaryt summary = summary_db.get(fname); - if(summary.mark_recompute) + summaryt summary=summary_db.get(fname); + if(summary.mark_recompute) return false; if(!context_sensitive || - summary.fw_precondition.is_true()) //precondition trivially holds + summary.fw_precondition.is_true()) // precondition trivially holds { - status() << "Precondition trivially holds, replacing by summary." + status() << "Precondition trivially holds, replacing by summary." << eom; summaries_used++; - precondition_holds = true; + precondition_holds=true; } else { - assertion = summary.fw_precondition; + assertion=summary.fw_precondition; - //getting globals at call site - local_SSAt::var_sett cs_globals_in; - SSA.get_globals(n_it->location,cs_globals_in); + // getting globals at call site + local_SSAt::var_sett cs_globals_in; + SSA.get_globals(n_it->location, cs_globals_in); - ssa_inliner.rename_to_caller(f_it,summary.params, - cs_globals_in,summary.globals_in,assertion); + ssa_inliner.rename_to_caller( + f_it, summary.params, cs_globals_in, summary.globals_in, assertion); - debug() << "precondition assertion: " - << from_expr(SSA.ns,"",assertion) << eom; + debug() << "precondition assertion: " + << from_expr(SSA.ns, "", assertion) << eom; - precondition_holds = false; + precondition_holds=false; } } else if(!ssa_db.exists(fname)) { status() << "Function " << fname << " not found" << eom; - precondition_holds = true; + precondition_holds=true; } - else if(fname == function_name) + else if(fname==function_name) { status() << "Havoc recursive function call to " << fname << eom; - precondition_holds = true; + precondition_holds=true; } - else + else { status() << "Function " << fname << " not analyzed yet" << eom; - return false; //function not seen yet + return false; // function not seen yet } - if(precondition_holds) return true; + if(precondition_holds) + return true; assert(!assertion.is_nil()); // precondition check // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -346,23 +357,27 @@ bool summarizer_baset::check_precondition( solver << precondition; solver << ssa_inliner.get_summaries(SSA); - //add precondition + // add precondition solver << not_exprt(assertion); switch(solver()) { - case decision_proceduret::D_SATISFIABLE: { - precondition_holds = false; + case decision_proceduret::D_SATISFIABLE: + { + precondition_holds=false; status() << "Precondition does not hold, need to recompute summary." << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { - precondition_holds = true; + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { + precondition_holds=true; status() << "Precondition holds, replacing by summary." << eom; summaries_used++; - - break; } + + break; + } default: assert(false); break; } @@ -373,7 +388,7 @@ bool summarizer_baset::check_precondition( /*******************************************************************\ -Function: summarizer_baset::check_end_reachable() +Function: summarizer_baset::check_end_reachable Inputs: @@ -381,14 +396,14 @@ Function: summarizer_baset::check_end_reachable() Purpose: returns false if the end of the function is not reachable -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_end_reachable( - const function_namet &function_name, - local_SSAt &SSA, - const exprt &cond) + const function_namet &function_name, + local_SSAt &SSA, + const exprt &cond) { - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -398,13 +413,16 @@ bool summarizer_baset::check_end_reachable( solver << ssa_inliner.get_summaries(SSA); solver << cond; + exprt::operandst assertions; + // do not add assertions + // because a failing assertion does not prove termination assertions.push_back( not_exprt(SSA.guard_symbol(--SSA.goto_function.body.instructions.end()))); -// get_assertions(SSA,assertions); //a failing assertion does not prove termination, let's ignore them - solver << not_exprt(conjunction(assertions)); //we want to reach any of them - bool result = (solver()==decision_proceduret::D_SATISFIABLE); + solver << not_exprt(conjunction(assertions)); // we want to reach any of them + + bool result=(solver()==decision_proceduret::D_SATISFIABLE); solver.pop_context(); diff --git a/src/solver/summarizer_base.h b/src/solver/summarizer_base.h new file mode 100644 index 000000000..d086d65bb --- /dev/null +++ b/src/solver/summarizer_base.h @@ -0,0 +1,113 @@ +/*******************************************************************\ + +Module: Summarizer Base + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BASE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BASE_H + +#include +#include +#include + +#include +#include +#include +#include + +class summarizer_baset:public messaget +{ +public: + explicit summarizer_baset( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + options(_options), + summary_db(_summary_db), + ssa_db(_ssa_db), + ssa_unwinder(_ssa_unwinder), + ssa_inliner(_ssa_inliner), + solver_instances(0), + solver_calls(0), + summaries_used(0), + termargs_computed(0) + { + } + + typedef summaryt::predicatet preconditiont; + typedef irep_idt function_namet; + typedef local_SSAt function_bodyt; + typedef std::map preconditionst; + typedef ssa_dbt::functionst functionst; + typedef functionst::value_type functiont; + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + unsigned get_number_of_summaries_used() { return summaries_used; } + unsigned get_number_of_termargs_computed() { return termargs_computed; } + + protected: + optionst &options; + summary_dbt &summary_db; + ssa_dbt &ssa_db; + ssa_unwindert &ssa_unwinder; + ssa_inlinert &ssa_inliner; + + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) + { + assert(false); + } + + bool check_call_reachable( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt& precondition, + bool forward); + + virtual exprt compute_calling_context( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool forward); + + virtual bool check_precondition( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator node, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool context_sensitive); + + void get_assertions( + const local_SSAt &SSA, + exprt::operandst &assertions); + + bool check_end_reachable( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &cond); + + // statistics + unsigned solver_instances; + unsigned solver_calls; + unsigned summaries_used; + unsigned termargs_computed; +}; + + +#endif diff --git a/src/solver/summarizer_bw.cpp b/src/solver/summarizer_bw.cpp new file mode 100644 index 000000000..e52b1df43 --- /dev/null +++ b/src/solver/summarizer_bw.cpp @@ -0,0 +1,541 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "summarizer_bw.h" +#include "summary_db.h" + +/*******************************************************************\ + +Function: summarizer_bwt::summarize + + Inputs: + + Outputs: + + Purpose: analyze only functions reachable in a previous forward analysis + +\*******************************************************************/ + +void summarizer_bwt::summarize() +{ + status() << "\nBackward analysis..." << eom; + + exprt postcondition=true_exprt(); // initial calling context + for(const auto &f : ssa_db.functions()) + { + status() << "\nSummarizing function " << f.first << eom; + if(summary_db.exists(f.first) && + summary_db.get(f.first).bw_precondition.is_nil()) + compute_summary_rec(f.first, postcondition, false); + else + status() << "Skipping function " << f.first << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::summarize + + Inputs: + + Outputs: + + Purpose: summarize from given entry point + +\*******************************************************************/ + +void summarizer_bwt::summarize(const function_namet &function_name) +{ + status() << "\nBackward analysis..." << eom; + + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + if(summary_db.exists(function_name)) + { + compute_summary_rec(function_name, postcondition, true); + } + else + status() << "Skipping function " << function_name << eom; +} + + +/*******************************************************************\ + +Function: summarizer_bwt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + const summaryt &old_summary=summary_db.get(function_name); + + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + old_summary, + postcondition, + context_sensitive, + options.get_bool_option("sufficient")); + + status() << "Analyzing function " << function_name << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.bw_postcondition=postcondition; + + if(!options.get_bool_option("havoc")) + { + do_summary(function_name, SSA, old_summary, summary, context_sensitive); + } + + // store summary in db + summary_db.put(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::do_summary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::do_summary( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive) +{ + bool sufficient=options.get_bool_option("sufficient"); + status() << "Computing preconditions" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + template_generator_summaryt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, false); + + exprt::operandst c; + c.push_back(old_summary.fw_precondition); + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst postcond; + ssa_inliner.get_summaries(SSA, false, postcond, c); // backward summaries + collect_postconditions(function_name, SSA, summary, postcond, sufficient); + if(!sufficient) + { + c.push_back(conjunction(postcond)); + } + else // sufficient + { + c.push_back(not_exprt(conjunction(postcond))); + } + + if(!template_generator.out_vars().empty()) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, conjunction(c), template_generator); + analyzer.get_result( + summary.bw_transformer, template_generator.inout_vars()); + analyzer.get_result(summary.bw_invariant, template_generator.loop_vars()); + analyzer.get_result(summary.bw_precondition, template_generator.out_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } +#if 1 + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + else + { + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << conjunction(c); + exprt result=true_exprt(); + if(solver()==decision_proceduret::D_UNSATISFIABLE) + result=false_exprt(); + solver.pop_context(); + summary.bw_transformer=result; + summary.bw_invariant=result; + summary.bw_precondition=result; + } +#endif + + if(sufficient) + { + summary.bw_transformer=not_exprt(summary.bw_transformer); + summary.bw_invariant=not_exprt(summary.bw_invariant); + summary.bw_precondition=not_exprt(summary.bw_precondition); + } + + if(context_sensitive && !summary.bw_postcondition.is_true()) + { + summary.bw_transformer= + implies_exprt(summary.bw_postcondition, summary.bw_transformer); + summary.bw_invariant= + implies_exprt(summary.bw_postcondition, summary.bw_invariant); + summary.bw_precondition= + implies_exprt(summary.bw_postcondition, summary.bw_precondition); + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!sufficient && + !check_call_reachable( + function_name, SSA, n_it, f_it, postcondition, false)) + { + continue; + } + + if(!check_postcondition( + function_name, SSA, n_it, f_it, postcondition, context_sensitive)) + { + exprt postcondition_call=true_exprt(); + if(context_sensitive) + postcondition_call= + compute_calling_context2( + function_name, + SSA, + old_summary, + n_it, + f_it, + postcondition, + sufficient); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, postcondition_call, context_sensitive); + summaries_used++; + } + } + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::collect_postconditions + + Inputs: + + Outputs: + + Purpose: collects postconditions where precondition inference starts from + +\*******************************************************************/ + +void summarizer_bwt::collect_postconditions( + const function_namet &function_name, + const local_SSAt &SSA, + const summaryt &summary, + exprt::operandst &postconditions, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); a_it++) + { + postconditions.push_back(*a_it); + } + } + + exprt guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + if(!sufficient) + postconditions.push_back(and_exprt(guard, summary.bw_postcondition)); + else + postconditions.push_back(implies_exprt(guard, summary.bw_postcondition)); +} + +/*******************************************************************\ + +Function: summarizer_bwt::check_postcondition + + Inputs: + + Outputs: + + Purpose: returns false if the summary needs to be recomputed + +\*******************************************************************/ + +bool summarizer_bwt::check_postcondition( + const function_namet &function_name, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool context_sensitive) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Checking precondition of " << fname << eom; + + bool precondition_holds=false; + exprt assertion; + + if(!summary_db.exists(fname)) + return true; // nothing to do + + summaryt summary=summary_db.get(fname); + + if(summary.bw_precondition.is_nil()) + return false; // there is work to do + + if(!context_sensitive || + summary.fw_precondition.is_true()) // precondition trivially holds + { + status() << "Precondition trivially holds, replacing by summary." + << eom; + summaries_used++; + precondition_holds=true; + } + else + { + assertion=summary.bw_precondition; + + // getting globals at call site + local_SSAt::var_sett cs_globals_in; + SSA.get_globals(n_it->location, cs_globals_in); + + ssa_inliner.rename_to_caller( + f_it, summary.params, cs_globals_in, summary.globals_in, assertion); + + debug() << "precondition assertion: " << + from_expr(SSA.ns, "", assertion) << eom; + + precondition_holds=false; + } + + if(precondition_holds) + return true; + + assert(!assertion.is_nil()); + + // postcondition check + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + solver << SSA; + + solver.new_context(); + solver << SSA.get_enabling_exprs(); + + solver << precondition; + solver << ssa_inliner.get_summaries(SSA); + + // add postcondition + solver << not_exprt(assertion); + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + { + precondition_holds=false; + + status() << "Precondition does not hold, need to recompute summary." << eom; + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { + precondition_holds=true; + + status() << "Precondition holds, replacing by summary." << eom; + summaries_used++; + + break; + } + default: assert(false); break; + } + + return precondition_holds; +} + +/*******************************************************************\ + +Function: summarizer_bwt::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bwt::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + + template_generator_callingcontextt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, false); + + // collect globals at call site + std::map + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + c.push_back(old_summary.fw_precondition); + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst postcond; + ssa_inliner.get_summaries(SSA, false, postcond, c); // backward summaries + old_summary.bw_postcondition=postcondition; // that's a bit awkward + collect_postconditions(function_name, SSA, old_summary, postcond, sufficient); + if(!sufficient) + { + c.push_back(conjunction(postcond)); + } + else // sufficient + { + c.push_back(not_exprt(conjunction(postcond))); + } + + analyzer(solver, SSA, conjunction(c), template_generator); + + // set preconditions + local_SSAt &fSSA=ssa_db.get(fname); + + exprt postcondition_call; + analyzer.get_result( + postcondition_call, + template_generator.callingcontext_vars()); + + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + +#if 1 + // TODO: this should actually be handled by ssa_analyzer + // using a "guard-reachabiliity-only" analysis if template is empty + if(sufficient && + !postcondition_call.is_true()) + { + postcondition_call=not_exprt(postcondition_call); + } +#endif + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + + return postcondition_call; +} diff --git a/src/solver/summarizer_bw.h b/src/solver/summarizer_bw.h new file mode 100644 index 000000000..540f8fedd --- /dev/null +++ b/src/solver/summarizer_bw.h @@ -0,0 +1,86 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_base.h" + +class summarizer_bwt:public summarizer_baset +{ +public: + explicit summarizer_bwt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_baset( + _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive); + + virtual bool check_postcondition( + const function_namet &function_name, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator node, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool context_sensitive); + + virtual void collect_postconditions( + const function_namet &function_name, + const local_SSAt &SSA, + const summaryt &summary, + exprt::operandst &postconditions, + bool sufficient); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif diff --git a/src/solver/summarizer_bw_cex.cpp b/src/solver/summarizer_bw_cex.cpp new file mode 100644 index 000000000..33501d783 --- /dev/null +++ b/src/solver/summarizer_bw_cex.cpp @@ -0,0 +1,274 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize() +{ + assert(false); // unused +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec(function_name, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_baset::check() +{ + assert(false); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loophead_selects + + Inputs: + + Outputs: + + Purpose: returns the select guards at the loop heads + in order to check whether a countermodel is spurious + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loophead_selects( + const irep_idt &function_name, + const local_SSAt &SSA, + prop_convt &solver, + exprt::operandst &loophead_selects) +{ + // TODO: this should be provided by unwindable_local_SSA + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + symbol_exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_unwinder.get(function_name).unwinder_rename(lsguard, *n_it, true); + loophead_selects.push_back(not_exprt(lsguard)); + solver.set_frozen(solver.convert(lsguard)); + } + literalt loophead_selects_literal= + solver.convert(conjunction(loophead_selects)); + if(!loophead_selects_literal.is_constant()) + solver.set_frozen(loophead_selects_literal); + +#if 0 + std::cout << "loophead_selects: " + << from_expr(SSA.ns, "", conjunction(loophead_selects)) + << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + loops in order to check whether we can unroll further + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + prop_convt &solver, + exprt::operandst &loop_continues) +{ + // TODO: this should be provided by unwindable_local_SSA + ssa_unwinder.get(function_name).loop_continuation_conditions(loop_continues); + if(loop_continues.size()==0) + { + // TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + symbol_exprt guard=SSA.guard_symbol(n_it->location); + symbol_exprt cond=SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard, cond)); + } + } + +#if 0 + std::cout << "loophead_continues: " + << from_expr(SSA.ns, "", disjunction(loop_continues)) << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + given loop in order to check whether we can unroll further + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues) +{ + // TODO: need to ask ssa_inliner regarding inlined functions + + // TODO: this should be provided by unwindable_local_SSA + + ssa_unwinder.get(function_name) + .loop_continuation_conditions(loop_id, loop_continues); + if(loop_continues.empty()) + { + // TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + if(n_it->loophead->location!=loop_id) + continue; + symbol_exprt guard=SSA.guard_symbol(n_it->location); + symbol_exprt cond=SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard, cond)); + break; + } + } + +#if 0 + std::cout << "loophead_continues: " + << from_expr(SSA.ns, "", disjunction(loop_continues)) << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::is_fully_unwound + + Inputs: + + Outputs: + + Purpose: checks whether the loops have been fully unwound + +\*******************************************************************/ + +bool summarizer_bw_cex_baset::is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver) +{ + solver.new_context(); + solver << + and_exprt(conjunction(loophead_selects), disjunction(loop_continues)); + + solver_calls++; // statistics + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + return false; + break; + + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + solver << conjunction(loophead_selects); + return true; + break; + + case decision_proceduret::D_ERROR: + default: + throw "error from decision procedure"; + } +} diff --git a/src/solver/summarizer_bw_cex.h b/src/solver/summarizer_bw_cex.h new file mode 100644 index 000000000..8e3f70e1b --- /dev/null +++ b/src/solver/summarizer_bw_cex.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis Base + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw.h" + +class summarizer_bw_cex_baset:public summarizer_bwt +{ +public: + typedef ssa_refiner_selectivet::reasont reasont; + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + virtual void get_reason(reasont &_reason) { _reason.merge(reason); } + +protected: + function_namet entry_function; + function_namet error_function; + exprt error_assertion; + reasont reason; + + summarizer_bw_cex_baset( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner), + entry_function(_entry_function), + error_function(_error_function) + { + } + + void get_loophead_selects( + const irep_idt &function_name, + const local_SSAt &, + prop_convt &, + exprt::operandst &loophead_selects); + + void get_loop_continues( + const irep_idt &function_name, + const local_SSAt &, + prop_convt &, + exprt::operandst &loop_continues); + + void get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues); + + bool is_fully_unwound( + const exprt::operandst& loop_continues, + const exprt::operandst& loophead_selects, + incremental_solvert&); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H diff --git a/src/solver/summarizer_bw_cex_ai.cpp b/src/solver/summarizer_bw_cex_ai.cpp new file mode 100644 index 000000000..8607afa5c --- /dev/null +++ b/src/solver/summarizer_bw_cex_ai.cpp @@ -0,0 +1,529 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_ai.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../domains/disjunctive_analyzer.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec( + function_name, summaryt::entry_call_site, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (abstract)..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_ait::check() +{ + property_checkert::resultt result=property_checkert::FAIL; + if(!summary_db.exists(entry_function)) + { + result=property_checkert::UNKNOWN; + } + else + { + const summaryt &summary=summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + result=property_checkert::UNKNOWN; + } + + // we are only complete if we are in the entry function + if(result==property_checkert::UNKNOWN && + entry_function==error_function) + { + incremental_solvert &solver=ssa_db.get_solver(entry_function); + const local_SSAt &ssa=ssa_db.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects( + entry_function, ssa, *solver.solver, loophead_selects); + get_loop_continues(entry_function, ssa, *solver.solver, loop_continues); + // check whether loops have been fully unwound + bool fully_unwound= + is_fully_unwound(loop_continues, loophead_selects, solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result=property_checkert::PASS; + } + + return result; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + summaryt summary; + if(summary_db.exists(function_name)) + summary=summary_db.get(function_name); + else + { + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.nondets=SSA.nondets; + } + + // insert assertion + exprt end_guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition=implies_exprt(end_guard, _postcondition); + if(function_name==error_function) + { + postcondition=and_exprt(postcondition, not_exprt(error_assertion)); + } + + summary.bw_postcondition=_postcondition; + +#if 0 + debug() << "Postcondition: " << + from_expr(SSA.ns, "", postcondition) << eom; +#endif + + if(_postcondition.is_false()) + { + summary.error_summaries[call_site]=false_exprt(); + } + else + { + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + summary, + postcondition, + context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary( + function_name, + call_site, + SSA, + summary, + summary, + postcondition, + context_sensitive); + + if(function_name==error_function) + summary.has_assertion=true; + } + + summary_db.set(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + debug() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // TODO: maybe allow setting this separately on the command line + optionst _options=options; + _options.set_option("intervals", true); + _options.set_option("binsearch-solver", true); + + // TODO: use a template generator without invariants + template_generator_summaryt template_generator( + _options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, false); + + exprt::operandst c; + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + // backward summaries + assertion_flag= + ssa_inliner.get_summaries( + SSA, call_site, false, assert_postcond, noassert_postcond, c); + + assert_postcond.push_back(postcondition); // context + + // add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond), summary_vars); + for(const auto &var : summary_vars) + { + if(var.id()==ID_nondet_symbol) + summary.nondets.insert(var); + } + + // assumptions must hold + for(const auto &node : SSA.nodes) + for(const auto &assumption : node.assumptions) + c.push_back(assumption); + +#if 0 + std::cout << from_expr(SSA.ns, "", cc) << std::endl; +#endif + + // TODO: pushing loophead_selects into the solver + + summary.error_summaries[call_site]; + if(!template_generator.empty()) + { + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + + exprt cc=simplify_expr(conjunction(c), SSA.ns); + + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + disjunctive_analyzer( + solver, + SSA, + cc, + template_generator, + cc, + summary.error_summaries[call_site], + template_generator.inout_vars()); + +#if 0 + std::cout << "SUM: " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + + summary.error_summaries[call_site]= + simplify_expr(summary.error_summaries[call_site], SSA.ns); + +#if 0 + std::cout << "SUM (post simplification): " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + + // statistics + solver_instances+=disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls+=disjunctive_analyzer.get_number_of_solver_calls(); + } + else + { + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + // c.push_back(not_exprt(conjunction(assert_postcond))); + // c.push_back(not_exprt(disjunction(noassert_postcond))); + + exprt cc=simplify_expr(conjunction(c), SSA.ns); + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << cc; + exprt result=true_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) + result=false_exprt(); + solver.pop_context(); + summary.error_summaries[call_site]=result; + +#if 0 + std::cout << "SUM: " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + } + + summary.error_summaries[call_site]= + simplify_expr((summary.error_summaries[call_site]), SSA.ns); // not_exprt + + summary.has_assertion=assertion_flag; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt postcondition_call=true_exprt(); + postcondition_call=compute_calling_context2( + function_name, SSA, old_summary, n_it, f_it, postcondition, sufficient); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec( + fname, + summaryt::call_sitet(n_it->location), + postcondition_call, + context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_ait::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + + // TODO: maybe allow setting this separately on the command line + optionst _options=options; + _options.set_option("intervals", true); + _options.set_option("binsearch-solver", true); + + // TODO: use a template generator without invariants + template_generator_callingcontextt template_generator( + _options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, false); + + // collect globals at call site + std::map< + local_SSAt::nodet::function_callst::const_iterator, + local_SSAt::var_sett> + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries(SSA, summaryt::call_sitet(n_it->location), false, + assert_postcond, noassert_postcond, c); // backward summaries + assert_postcond.push_back(postcondition); // context + + + // TODO: pushing loophead_selects into the solver + + // set preconditions + local_SSAt &fSSA=ssa_db.get(fname); + + exprt postcondition_call; + + if(!template_generator.empty()) + { + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + + disjunctive_analyzer(solver, SSA, conjunction(c), template_generator, + conjunction(c), postcondition_call, + template_generator.callingcontext_vars()); + + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + } + else + { + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << conjunction(c); + + postcondition_call=false_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) + postcondition_call=true_exprt(); + solver.pop_context(); + } + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_instances+=disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls+=disjunctive_analyzer.get_number_of_solver_calls(); + + return postcondition_call; +} + diff --git a/src/solver/summarizer_bw_cex_ai.h b/src/solver/summarizer_bw_cex_ai.h new file mode 100644 index 000000000..af779376f --- /dev/null +++ b/src/solver/summarizer_bw_cex_ai.h @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: Summarizer for Backwards Error Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_ait:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_ait( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function) + { + } + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H diff --git a/src/solver/summarizer_bw_cex_all.cpp b/src/solver/summarizer_bw_cex_all.cpp new file mode 100644 index 000000000..4b1052fcc --- /dev/null +++ b/src/solver/summarizer_bw_cex_all.cpp @@ -0,0 +1,75 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_all.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_allt::summarize() + + Inputs: + + Outputs: + + Purpose: calls multiple strategies + +\*******************************************************************/ + +void summarizer_bw_cex_allt::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (all)..." << eom; + error_assertion=_error_assertion; + + summarizer_bw_cex_concrete.summarize(error_assertion); + result=summarizer_bw_cex_concrete.check(); + + if(result==property_checkert::UNKNOWN) + { + summarizer_bw_cex_ai.summarize(error_assertion); + result=summarizer_bw_cex_ai.check(); + + if(result==property_checkert::UNKNOWN) + { + summarizer_bw_cex_complete.summarize(error_assertion); + result=summarizer_bw_cex_complete.check(); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_allt::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_allt::check() +{ + return result; +} diff --git a/src/solver/summarizer_bw_cex_all.h b/src/solver/summarizer_bw_cex_all.h new file mode 100644 index 000000000..c12afcf8b --- /dev/null +++ b/src/solver/summarizer_bw_cex_all.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis All + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H + +#include +#include +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex.h" +#include "summarizer_bw_cex_concrete.h" +#include "summarizer_bw_cex_ai.h" +#include "summarizer_bw_cex_complete.h" + +class summarizer_bw_cex_allt:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_allt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_concrete( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_ai( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_complete( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _solver, + _entry_function, + _error_function), + result(property_checkert::UNKNOWN) + { + } + + virtual void summarize(const exprt &_error_assertion); + + virtual void set_message_handler(message_handlert &handler) + { + summarizer_bw_cex_concrete.set_message_handler(handler); + summarizer_bw_cex_ai.set_message_handler(handler); + summarizer_bw_cex_complete.set_message_handler(handler); + messaget::set_message_handler(handler); + } + + virtual property_checkert::resultt check(); + + protected: + summarizer_bw_cex_concretet summarizer_bw_cex_concrete; + summarizer_bw_cex_ait summarizer_bw_cex_ai; + summarizer_bw_cex_completet summarizer_bw_cex_complete; + property_checkert::resultt result; +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H diff --git a/src/solver/summarizer_bw_cex_complete.cpp b/src/solver/summarizer_bw_cex_complete.cpp new file mode 100644 index 000000000..7df4f3c23 --- /dev/null +++ b/src/solver/summarizer_bw_cex_complete.cpp @@ -0,0 +1,735 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include +#include +#include + +#include +#include +#include + +#include "summarizer_bw_cex_complete.h" + +#define REFINE_ALL + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize +( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec(entry_function, dependency_set, -1); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (complete)..." << eom; + error_assertion=_error_assertion; + ssa_inliner.rename(error_assertion, -1, false); + /* + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; + */ + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_completet::inline_summaries +( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) +{ + local_SSAt &SSA=ssa_db.get(function_name); +#if 0 + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(function_name); + ssa_local_unwinder.compute_loop_continuation_conditions(); +#endif + // add enabling expressions + exprt enable_exprs=SSA.get_enabling_exprs(); + ssa_inliner.rename(enable_exprs, counter); + + solver << enable_exprs; + + // assumptions must hold + for(local_SSAt::nodest::const_iterator + n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); ++n_it) + for(local_SSAt::nodet::assumptionst::const_iterator + a_it=n_it->assumptions.begin(); a_it!=n_it->assumptions.end(); ++a_it) + { + exprt assumption=*a_it; + ssa_inliner.rename(assumption, counter); + solver << assumption; + } + +#ifdef REFINE_ALL + // TODO: let's just put all loops into the reason + for(const auto node : SSA.nodes) + if(node.loophead!=SSA.nodes.end()) + reason[function_name].loops.insert(node.loophead->location); +#endif + + ssa_dependency_grapht &ssa_depgraph=ssa_db.get_depgraph(function_name); + + struct worknodet + { + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index=0; + start_node.dependency_set=dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()) + { +#ifdef DEBUG + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + worknodet &worknode=worklist.front(); + const ssa_dependency_grapht::depnodet &depnode= + ssa_depgraph.depnodes_map[worknode.node_index]; + +#ifdef DEBUG + std::cout << "working node: " << function_name << ": " + << worknode.node_index << "\n"; + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n\n\n"; +#endif + + // return if the top most node is reached + if(worknode.node_index==ssa_depgraph.top_node_index) + return worknode.dependency_set; + + // modify worknode_dependency_set if the node is an assertion + if(depnode.is_assertion==true) + { +#ifdef DEBUG + std::cout << "\t\t an assertion node\n"; +#endif + for(find_symbols_sett::const_iterator d_it=depnode.used_symbols.begin(); + d_it!=depnode.used_symbols.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + } + + // if this is a function call + if(depnode.is_function_call==true) + { +#ifdef DEBUG + std::cout << "fcall: working node: " << function_name << ": " + << worknode.node_index << "\n"; +#endif + irep_idt fname= + to_symbol_expr( + (to_function_application_expr(depnode.node_info)).function()) + .get_identifier(); + + find_symbols_sett renamed_dependencies; + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename(renamed_id, depnode.rename_counter, false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + +#ifdef REFINE_ALL + // TODO: just put all function calls into reason + reason[function_name].functions.insert(depnode.location); +#endif + + // recurse + worknode.dependency_set= + compute_summary_rec( + fname, worknode.dependency_set, depnode.rename_counter); + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // attach the '@' symbol if not already there + ssa_inliner.rename(renamed_id, depnode.rename_counter, true); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()) + { + exprt worknode_info=depnode.node_info; + + bool is_error_assertion=false; + if(depnode.is_assertion) + { +#ifdef DEBUG + std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) + << std::endl; + std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) + << std::endl; +#endif + assert(error_assertion.id()==ID_not); + if(error_assertion.op0().id()!=ID_and) + is_error_assertion=(worknode_info==error_assertion.op0()); + else + { + forall_operands(a_it, error_assertion.op0()) + { + if(worknode_info==*a_it) + { + is_error_assertion=true; + break; + } + } + } + } + + if(worknode.node_index!=0) + { + if(!(depnode.is_function_call)) + { + if(!depnode.is_assertion || is_error_assertion) + { +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + ssa_inliner.rename(worknode_info, counter); +#if 0 + std::cout << "Solver <-- renamed assertion: " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + + if(depnode.is_assertion) // keep for later + renamed_error_assertion.push_back(worknode_info); + else + solver << worknode_info; + + if(depnode.is_loop) + { + // loop head selects + exprt lsguard=depnode.guard; + ssa_inliner.rename(lsguard, counter); + loophead_selects.push_back(lsguard); + add_reason_to_check( + lsguard, function_name, false, depnode.location); + + // loop continuations + exprt::operandst local_loop_continues; + get_loop_continues( + function_name, SSA, depnode.location, local_loop_continues); + for(size_t i=0; inode_index==pred_node_index) + { + dependencies_merged=true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); + a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!= + worknode.dependency_set.end()) + { + if((w_it->dependency_set).find(*a_it)== + (w_it->dependency_set).end()) + { + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged==false) + { + worknodet new_worknode; + new_worknode.node_index=pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!=worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#if 0 + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#if 0 + std::cout << function_name << ": covered : "; + for(int l=0; l &waitlisted_worknode_successors= + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i=0; iconvert(not_exprt(conjunction(renamed_error_assertion)))); + solver.solver->set_assumptions(formula); +#endif + + solver_calls++; // for statistics + if(solver()==decision_proceduret::D_SATISFIABLE) + { + // pop_context() not necessary + return property_checkert::FAIL; + } +#ifndef REFINE_ALL + else + { + const namespacet &ns=ssa_db.get(entry_function).ns; + // get reasons for spuriousness + for(unsigned i=0; iis_in_conflict(formula[i])) + { + debug() << "is_in_conflict: " + << from_expr(ns, "", formula_expr[i]) << eom; + const reason_to_checkt &r=reasons_to_check[i]; + if(r.is_function) + reason[r.function_name].functions.insert(r.info); + else + reason[r.function_name].loops.insert(r.info); + } + } + bvt assumptions; + solver.solver->set_assumptions(assumptions); + for(unsigned i=0; i " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it=dependency_set.begin(); + d_it!=dependency_set.end(); d_it++) + { + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::add_reason_to_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt & info) +{ + literalt l=solver.solver->convert(expr); + if(l.is_false()) + { + literalt dummy= + solver.solver->convert(symbol_exprt("goto_symex::\\dummy", bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); + } + else if(!l.is_true()) + { + formula.push_back(l); + formula_expr.push_back(expr); + reasons_to_check.push_back(reason_to_checkt()); + reason_to_checkt &r=reasons_to_check.back(); + r.function_name=function_name; + r.info=info; + r.is_function=is_function; + } +} diff --git a/src/solver/summarizer_bw_cex_complete.h b/src/solver/summarizer_bw_cex_complete.h new file mode 100644 index 000000000..098395872 --- /dev/null +++ b/src/solver/summarizer_bw_cex_complete.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_completet:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_completet( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + solver(_solver) + { + } + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + incremental_solvert &solver; + bvt formula; // for UNSAT core + exprt::operandst formula_expr; // for debugging + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + exprt::operandst renamed_error_assertion; + + struct reason_to_checkt + { + function_namet function_name; + bool is_function; + local_SSAt::locationt info; + }; + std::vector reasons_to_check; + void add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt &info); + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H diff --git a/src/solver/summarizer_bw_cex_concrete.cpp b/src/solver/summarizer_bw_cex_concrete.cpp new file mode 100644 index 000000000..0e08832bf --- /dev/null +++ b/src/solver/summarizer_bw_cex_concrete.cpp @@ -0,0 +1,657 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +// #define OPT_11 // simplify before pushing to solver +#define OPT_12 // collect, conjunct, simplify and then push to the solver + +// #define OPT_2 // a fresh solver each time + +// TODO: a bug in the fresh solver case; does not compute +// calling contexts (see struct tests in regression) + +// #define DEBUG + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_concrete.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec( + function_name, summaryt::entry_call_site, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (concrete)..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_concretet::check() +{ + property_checkert::resultt result=property_checkert::UNKNOWN; + if(summary_db.exists(entry_function)) + { + const summaryt &summary=summary_db.get(entry_function); + if(!summary.error_summaries.empty() && + !summary.error_summaries.begin()->second.is_nil()) + { + if(summary.error_summaries.begin()->second.is_false()) + result=property_checkert::PASS; + else + result=property_checkert::FAIL; + } + } + + // we are only complete if everything was inlined + if(result==property_checkert::UNKNOWN && + options.get_bool_option("inline")) + { + incremental_solvert &solver=ssa_db.get_solver(entry_function); + const local_SSAt &ssa=ssa_db.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects( + entry_function, ssa, *solver.solver, loophead_selects); + get_loop_continues(entry_function, ssa, *solver.solver, loop_continues); + // check whether loops have been fully unwound + bool fully_unwound= + is_fully_unwound(loop_continues, loophead_selects, solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result=property_checkert::PASS; + } + + return result; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + // TODO: let's just put all loops into the reason + for(const auto &node : SSA.nodes) + if(node.loophead!=SSA.nodes.end()) + reason[function_name].loops.insert(node.loophead->location); + + summaryt summary; + if(summary_db.exists(function_name)) + summary=summary_db.get(function_name); + else + { + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.nondets=SSA.nondets; + } + + // insert assertion + exprt end_guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition=implies_exprt(end_guard, _postcondition); + if(function_name==error_function) + { + postcondition=and_exprt(postcondition, not_exprt(error_assertion)); + } + + summary.bw_postcondition=_postcondition; + +#if 0 + debug() << "Postcondition: " << from_expr(SSA.ns, "", postcondition) << eom; +#endif + + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + summary, + postcondition, + context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary( + function_name, + call_site, + SSA, + summary, + summary, + postcondition, + context_sensitive); + + if(function_name==error_function) + summary.has_assertion=true; + + summary_db.set(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + debug() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert *fresh_solver= + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + incremental_solvert &solver=(*fresh_solver); + SSA.unmark_nodes(); + exprt::operandst store; +#else + incremental_solvert &solver=ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + // ENHANCE: we could reuse the solver state, but it's difficult + // (the function maybe called several times) + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + // backward summaries + assertion_flag=ssa_inliner.get_summaries( + SSA, call_site, false, assert_postcond, noassert_postcond, c); + assert_postcond.push_back(postcondition); // context + + // add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond), summary_vars); + for(std::set::const_iterator it=summary_vars.begin(); + it!=summary_vars.end(); ++it) + if(it->id()==ID_nondet_symbol) + summary.nondets.insert(*it); + +#ifdef DEBUG + std::cout << "Assert Summary: " + << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; + std::cout << "Noassert Summary: " + << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; +#endif + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#ifdef DEBUG + debug() << "Backward summaries: " << + from_expr(SSA.ns, "", simplify_expr(conjunction(c), SSA.ns)) << eom; +#endif + +#ifdef OPT_12 + store << SSA; +#else +#ifdef OPT_2 + store << SSA; +#else + solver << SSA; +#endif +#endif + +#ifndef OPT_2 + solver.new_context(); +#endif + + // assumptions must hold + for(const auto &node : SSA.nodes) + { + for(const auto &a : node.assumptions) + { +#ifdef OPT_11 + solver << simplify_expr(a, SSA.ns); +#else +#ifdef OPT_12 + store.push_back(a); +#else +#ifdef OPT_2 + store.push_back(a); +#else + solver << a; +#endif +#endif +#endif + } + } + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else +#ifdef OPT_2 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif +#endif + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else +#ifdef OPT_2 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif +#endif + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else +#ifdef OPT_2 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif +#endif + +#ifdef OPT_12 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in do_summary:" + << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif +#ifdef OPT_2 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in do_summary:" + << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) + << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + // statistics + solver_calls++; + + // solve + if(solver()==decision_proceduret::D_UNSATISFIABLE) + { + // TODO: this is likely to be incomplete + summary.error_summaries[call_site]=true_exprt(); + summary.has_assertion=assertion_flag; +#ifndef OPT_2 + solver.pop_context(); +#endif + + return; + } + + // build error summary and add to summary + exprt::operandst var_values; + + for(const auto &var : SSA.params) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.globals_in) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.globals_out) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.nondets) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + summary.error_summaries[call_site]=not_exprt(conjunction(var_values)); + summary.has_assertion=assertion_flag; + +#ifndef OPT_2 + solver.pop_context(); +#endif + +#ifdef OPT_2 + delete fresh_solver; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt postcondition_call=true_exprt(); + postcondition_call=compute_calling_context2( + function_name, SSA, old_summary, n_it, f_it, postcondition, sufficient); + + // TODO: just put all function calls into reason + reason[function_name].functions.insert(n_it->location); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, summaryt::call_sitet(n_it->location), + postcondition_call, context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_concretet::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert *fresh_solver= + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + incremental_solvert &solver=(*fresh_solver); +#else + incremental_solvert &solver=ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + // collect globals at call site + std::map< + local_SSAt::nodet::function_callst::const_iterator, + local_SSAt::var_sett> + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries( + SSA, + summaryt::call_sitet(n_it->location), + false, + assert_postcond, + noassert_postcond, + c); + assert_postcond.push_back(postcondition); // context + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#ifdef OPT_12 + store << SSA; +#else + solver << SSA; +#endif + + solver.new_context(); + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif + +#ifdef OPT_12 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" + << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + + // build postcondition + exprt postcondition_call; + + if(solver()!=decision_proceduret::D_SATISFIABLE) + { + postcondition_call=true_exprt(); // TODO: this is likely to be incomplete + solver.pop_context(); + return postcondition_call; + } + + bool result=solver()==decision_proceduret::D_SATISFIABLE; + assert(result); + + exprt::operandst postcond_values; + for(local_SSAt::var_sett::const_iterator it=cs_globals_out[f_it].begin(); + it!=cs_globals_out[f_it].end(); it++) + { + exprt postc_value=solver.get(*it); + postcond_values.push_back(equal_exprt(*it, postc_value)); + } + postcondition_call=conjunction(postcond_values); + + solver.pop_context(); + + // get callee SSA and rename + local_SSAt &fSSA=ssa_db.get(fname); + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_calls++; + +#ifdef OPT_2 + delete fresh_solver; +#endif + + return not_exprt(postcondition_call); +} diff --git a/src/solver/summarizer_bw_cex_concrete.h b/src/solver/summarizer_bw_cex_concrete.h new file mode 100644 index 000000000..b13d9509c --- /dev/null +++ b/src/solver/summarizer_bw_cex_concrete.h @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_concretet:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_concretet( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H diff --git a/src/solver/summarizer_bw_cex_wp.cpp b/src/solver/summarizer_bw_cex_wp.cpp new file mode 100644 index 000000000..5f4d43e0f --- /dev/null +++ b/src/solver/summarizer_bw_cex_wp.cpp @@ -0,0 +1,768 @@ +/*******************************************************************\ + +Module: Slicing-based WP Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +// #define DEBUG + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include +#include +#include + +#include +#include +#include + +#include "summarizer_bw_cex_wp.h" + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec( + entry_function, dependency_set, -1, summaryt::entry_call_site); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (WP)..." << eom; + error_assertion=_error_assertion; +#ifdef DEBUG + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; +#endif + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_wpt::inline_summaries( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary) +{ + exprt::operandst slice; + + local_SSAt &SSA=ssa_db.get(function_name); + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + exprt c=conjunction(loophead_selects); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name + << ": (conjunction of loophead_selects):" + << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + slice.push_back(c); + ssa_inliner.rename(c, counter); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name + << ": (conjunction of loophead_selects):" + << "\t renamed info ~ " + << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + solver << c; + + ssa_dependency_grapht &ssa_depgraph=ssa_db.get_depgraph(function_name); + + struct worknodet + { + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index=0; + start_node.dependency_set=dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()) + { +#ifdef DEBUG + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + worknodet &worknode=worklist.front(); + +#ifdef DEBUG + std::cout << "working node: " << function_name + << ": " << worknode.node_index << "\n"; + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n\n\n"; +#endif + + // return if the top most node is reached + if(worknode.node_index==ssa_depgraph.top_node_index) + { + find_symbols_sett vars=worknode.dependency_set; + vars.insert(dependency_set.begin(), dependency_set.end()); + error_summary=simplify_summary(SSA.ns, conjunction(slice), vars); + return worknode.dependency_set; + } + + // modify worknode_dependency_set if the node is an assertion + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + { +#ifdef DEBUG + std::cout << "\t\t an assertion node\n"; +#endif + for(find_symbols_sett::const_iterator d_it= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); + d_it!= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); + d_it++) + { + worknode.dependency_set.insert(*d_it); + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + } + + // if this is a function call + if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call==true) + { +#ifdef DEBUG + std::cout << "fcall: working node: " << function_name << ": " + << worknode.node_index << "\n"; +#endif + irep_idt fname= + to_symbol_expr( + to_function_application_expr( + ssa_depgraph.depnodes_map[worknode.node_index].node_info) + .function()).get_identifier(); + + find_symbols_sett renamed_dependencies; + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename( + renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols( + ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + worknode.dependency_set= + compute_summary_rec( + fname, + worknode.dependency_set, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + summaryt::call_sitet( + ssa_depgraph.depnodes_map[worknode.node_index].location)); + slice.push_back(ssa_depgraph.depnodes_map[worknode.node_index].node_info); + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename( + renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols( + ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()) + { + exprt worknode_info= + ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + worknode_info=not_exprt(worknode_info); + + if(worknode.node_index!=0) + { + if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)) + { + if((ssa_depgraph.depnodes_map[worknode.node_index] + .is_assertion==false) || + (worknode_info==error_assertion)) + { +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + + slice.push_back(worknode_info); + ssa_inliner.rename(worknode_info, counter); + +#ifdef DEBUG + std::cout << "Solver <-- renamed assertion: " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + solver << worknode_info; + } + } + else + { + exprt guard_binding= + ssa_depgraph.depnodes_map[worknode.node_index].guard; +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) + << "\n"; +#endif + + ssa_inliner.rename(guard_binding, counter); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + ssa_db.get(function_name).ns, "", guard_binding) + << "\n"; +#endif + solver << guard_binding; + slice.push_back(guard_binding); + } + } + } + + // if not a function call and the dependency set is non-empty + if((ssa_depgraph.depnodes_map[worknode.node_index] + .is_function_call==false) && + (!worknode.dependency_set.empty())) + { + exprt worknode_info= + ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + worknode_info=not_exprt(worknode_info); + + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==false) || + (worknode_info==error_assertion)) + { + worknode.dependency_set= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols; + } + } + + for(ssa_dependency_grapht::annotated_predecessorst::const_iterator + p_it=ssa_depgraph.depnodes_map[worknode.node_index] + .predecessors.begin(); + p_it!=ssa_depgraph.depnodes_map[worknode.node_index].predecessors.end(); + p_it++) + { + ssa_dependency_grapht::annotated_predecessort pred=*p_it; + int pred_node_index=pred.predecessor_node_index; + find_symbols_sett pred_annotation=pred.annotation; + + bool dependencies_merged=false; + for(worklistt::iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + if(w_it->node_index==pred_node_index) + { + dependencies_merged=true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); + a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!= + worknode.dependency_set.end()) + { + if((w_it->dependency_set).find(*a_it)== + (w_it->dependency_set).end()) + { + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged==false) + { + worknodet new_worknode; + new_worknode.node_index=pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!=worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#ifdef DEBUG + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#ifdef DEBUG + std::cout << function_name << ": covered : "; + for(int l=0; l &waitlisted_worknode_successors= + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i=0; i " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it=dependency_set.begin(); + d_it!=dependency_set.end(); d_it++) + { + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::simplify_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr) +{ + if(expr.id()==ID_equal) + { + replace_map[expr.op0()]=expr.op1(); + return; + } + forall_operands(it, expr) + simplify_summary_build_map(replace_map, *it); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::simplify_summary_replace() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool summarizer_bw_cex_wpt::simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr) +{ + if(expr.id()==ID_function_application) + { + bool result=true; + exprt::operandst &args=to_function_application_expr(expr).arguments(); + for(size_t i=0; i +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_wpt:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_wpt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + solver(_solver) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + incremental_solvert &solver; + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + const summaryt::call_sitet &call_site); + + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); + + exprt simplify_summary( + const namespacet &ns, + exprt summary, + const find_symbols_sett &vars); + + void simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr); + + bool simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr); + + void simplify_summary_cleanup( + const find_symbols_sett &vars, exprt &expr); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_WP_H diff --git a/src/solver/summarizer_bw_term.cpp b/src/solver/summarizer_bw_term.cpp new file mode 100644 index 000000000..3543ab25e --- /dev/null +++ b/src/solver/summarizer_bw_term.cpp @@ -0,0 +1,497 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis with Termination + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "summarizer_bw_term.h" +#include "summarizer_fw_term.h" +#include "summary_db.h" + +#define MAX_PRECONDITION_DISJUNCTS 5 +#define MAX_BOOTSTRAP_ATTEMPTS 20 + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + const summaryt &old_summary=summary_db.get(function_name); + + // recursively compute summaries for function calls + inline_summaries( + function_name, SSA, old_summary, postcondition, context_sensitive, false); + + status() << "Analyzing function " << function_name << eom; + + bool has_loops=false; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) + { + has_loops=true; + break; + } + } + + debug() << "function " << + (has_loops ? "has loops" : "does not have loops") << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.bw_postcondition=postcondition; + + do_nontermination(function_name, SSA, old_summary, summary); + if(!options.get_bool_option("havoc") && + summary.terminates!=NO) + { + if(!has_loops) + { + do_summary(function_name, SSA, old_summary, summary, context_sensitive); + } + else + { + do_summary_term( + function_name, SSA, old_summary, summary, context_sensitive); + } + } + + // store summary in db + summary_db.put(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::do_nontermination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + cond.push_back(old_summary.fw_invariant); + cond.push_back(old_summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + ssa_inliner.get_summaries(SSA, false, cond, cond); // backward summaries + + if(!check_end_reachable(function_name, SSA, conjunction(cond))) + { + status() << "Function never terminates" << eom; + summary.bw_transformer=false_exprt(); + summary.bw_precondition=false_exprt(); + summary.terminates=NO; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::do_summary_term + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::do_summary_term( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive) +{ + status() << "Computing preconditions for termination" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // templates for ranking functions + template_generator_rankingt template_generator1( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator1.set_message_handler(get_message_handler()); + template_generator1(solver.next_domain_number(), SSA, true); + + // templates for backward summary + template_generator_summaryt template_generator2( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator2.set_message_handler(get_message_handler()); + template_generator2(solver.next_domain_number(), SSA, false); + + exprt::operandst bindings; + exprt::operandst postcond; + // backward summaries + ssa_inliner.get_summaries(SSA, false, postcond, bindings); + collect_postconditions(function_name, SSA, summary, postcond, true); + + // prepare solver + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << old_summary.fw_precondition; + solver << old_summary.fw_invariant; + solver << ssa_inliner.get_summaries(SSA); // forward summaries + solver << conjunction(bindings); // bindings for backward summaries + +#if 0 + // compute preconditions individually + // TODO: this should be done more transparently + for(unsigned i=0; iget(*it))); + } + precondition=conjunction(c); + debug() << "bootstrap model for precondition: " + << from_expr(SSA.ns, "", precondition) << eom; + solver.pop_context(); + } + else // whole precondition space covered + { + solver.pop_context(); + break; + } + + termination_argument= + compute_termination_argument( + SSA, precondition, solver, template_generator1); + + if(summarizer_fw_termt::check_termination_argument( + termination_argument)==YES) + { + return true; + } + + solver.new_context(); + checked_candidates.push_back(precondition); + solver << not_exprt(disjunction(checked_candidates)); // next one, please! + } + + return false; +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_termination_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_termt::compute_termination_argument( + local_SSAt &SSA, + const exprt &precondition, + incremental_solvert &solver, + template_generator_rankingt &template_generator) +{ + // compute ranking functions + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, precondition, template_generator); + exprt termination_argument; + analyzer.get_result(termination_argument, template_generator.all_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + termargs_computed++; + + return termination_argument; +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_precondition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_termt::compute_precondition( + local_SSAt &SSA, + summaryt &summary, + const exprt::operandst &postconditions, + incremental_solvert &solver, + template_generator_summaryt &template_generator, + bool context_sensitive) +{ + exprt postcond=not_exprt(conjunction(postconditions)); + + // compute backward summary + exprt bw_transformer, bw_invariant, bw_precondition; + if(!template_generator.out_vars().empty()) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, postcond, template_generator); + analyzer.get_result(bw_transformer, template_generator.inout_vars()); + analyzer.get_result(bw_invariant, template_generator.loop_vars()); + analyzer.get_result(bw_precondition, template_generator.out_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } +#if 1 + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + else + { + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << postcond; + exprt result=true_exprt(); + if(solver()==decision_proceduret::D_UNSATISFIABLE) + result=false_exprt(); + solver.pop_context(); + bw_transformer=result; + bw_invariant=result; + bw_precondition=result; + } +#endif + + bw_transformer=not_exprt(bw_transformer); + bw_invariant=not_exprt(bw_invariant); + bw_precondition=not_exprt(bw_precondition); + + if(context_sensitive && !summary.bw_postcondition.is_true()) + { + bw_transformer=implies_exprt(summary.bw_postcondition, bw_transformer); + bw_invariant=implies_exprt(summary.bw_postcondition, bw_invariant); + bw_precondition=implies_exprt(summary.bw_postcondition, bw_precondition); + } + + // join // TODO: should go into summaryt + if(summary.bw_transformer.is_nil()) + { + summary.bw_transformer=bw_transformer; + summary.bw_invariant=bw_invariant; + summary.bw_precondition=bw_precondition; + } + else + { + summary.bw_transformer=or_exprt(summary.bw_transformer, bw_transformer); + summary.bw_invariant=or_exprt(summary.bw_invariant, bw_invariant); + summary.bw_precondition=or_exprt(summary.bw_precondition, bw_precondition); + } + + return bw_precondition; +} + + diff --git a/src/solver/summarizer_bw_term.h b/src/solver/summarizer_bw_term.h new file mode 100644 index 000000000..190243b03 --- /dev/null +++ b/src/solver/summarizer_bw_term.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis with Termination + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_TERM_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_TERM_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "summarizer_bw.h" + +class summarizer_bw_termt:public summarizer_bwt +{ +public: + explicit summarizer_bw_termt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_bwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive); + + void do_summary_term( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive); + + void do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary); + + bool bootstrap_preconditions( + local_SSAt &SSA, + summaryt &summary, + incremental_solvert &solver, + template_generator_rankingt &template_generator1, + template_generator_summaryt &template_generator2, + exprt &termination_argument); + + exprt compute_termination_argument( + local_SSAt &SSA, + const exprt &precondition, + incremental_solvert &solver, + template_generator_rankingt &template_generator); + + exprt compute_precondition( + local_SSAt &SSA, + summaryt &summary, + const exprt::operandst &postconditions, + incremental_solvert &solver, + template_generator_summaryt &template_generator, + bool context_sensitive); +}; + +#endif diff --git a/src/solver/summarizer_fw.cpp b/src/solver/summarizer_fw.cpp new file mode 100644 index 000000000..1048a2b0f --- /dev/null +++ b/src/solver/summarizer_fw.cpp @@ -0,0 +1,234 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_fw.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +// #define SHOW_WHOLE_RESULT + +/*******************************************************************\ + +Function: summarizer_fwt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); // TODO: make const + + // recursively compute summaries for function calls + inline_summaries(function_name, SSA, precondition, context_sensitive); + + status() << "Analyzing function " << function_name << eom; + +#if 0 + { + std::ostringstream out; + out << "Function body for " << function_name + << " to be analyzed: " << std::endl; + for(const auto &node : SSA.nodes) + { + if(!node.empty()) + node.output(out, SSA.ns); + } + out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << "\n"; + debug() << out.str() << eom; + } +#endif + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.fw_precondition=precondition; + + if(!options.get_bool_option("havoc")) + { + do_summary(function_name, SSA, summary, true_exprt(), context_sensitive); + } + + +#if 0 + if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary.output(out, SSA.ns); + status() << out.str() << eom; + } +#endif + + // store summary in db + summary_db.put(function_name, summary); + + if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_fwt::do_summary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::do_summary( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary, + exprt cond, + bool context_sensitive) +{ + status() << "Computing summary" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + + template_generator_summaryt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, true); + + exprt::operandst conds; + conds.reserve(5); + conds.push_back(cond); + conds.push_back(summary.fw_precondition); + conds.push_back(ssa_inliner.get_summaries(SSA)); + +#ifdef REUSE_INVARIANTS + if(summary_db.exists(function_name)) // reuse existing invariants + { + const exprt &old_inv=summary_db.get(function_name).fw_invariant; + exprt inv=ssa_unwinder.get(function_name).rename_invariant(old_inv); + conds.push_back(inv); + +#if 0 + std::ostringstream out; + out << "(original inv)" << from_expr(SSA.ns, "", old_inv) << "\n"; + debug() << out.str() << eom; + out << "(renamed inv)" << from_expr(SSA.ns, "", inv) << "\n"; + debug() << out.str() << eom; +#endif + } +#endif + + cond=conjunction(conds); + + analyzer(solver, SSA, cond, template_generator); + analyzer.get_result(summary.fw_transformer, template_generator.inout_vars()); + analyzer.get_result(summary.fw_invariant, template_generator.loop_vars()); + +#ifdef SHOW_WHOLE_RESULT + // to see all the custom template values + exprt whole_result; + analyzer.get_result(whole_result, template_generator.all_vars()); + debug() << "whole result: " << from_expr(SSA.ns, "", whole_result) << eom; +#endif + + if(context_sensitive && !summary.fw_precondition.is_true()) + { + summary.fw_transformer= + implies_exprt(summary.fw_precondition, summary.fw_transformer); + summary.fw_invariant= + implies_exprt(summary.fw_precondition, summary.fw_invariant); + } + + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); +} + +/*******************************************************************\ + +Function: summarizer_fwt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, const exprt &precondition, + bool context_sensitive) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + { + continue; + } + + if(!check_precondition( + function_name, SSA, n_it, f_it, precondition, context_sensitive)) + { + exprt precondition_call=true_exprt(); + if(context_sensitive) + precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, precondition_call, context_sensitive); + summaries_used++; + } + } + } +} + + diff --git a/src/solver/summarizer_fw.h b/src/solver/summarizer_fw.h new file mode 100644 index 000000000..f21255c55 --- /dev/null +++ b/src/solver/summarizer_fw.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_FW_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_FW_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_base.h" + +class summarizer_fwt:public summarizer_baset +{ +public: + explicit summarizer_fwt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_baset( + _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &precondition, + bool context_sensitive); + + void do_summary( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary, + exprt cond, // additional constraints + bool forward); +}; + +#endif diff --git a/src/solver/summarizer_fw_contexts.cpp b/src/solver/summarizer_fw_contexts.cpp new file mode 100644 index 000000000..69c7e3f09 --- /dev/null +++ b/src/solver/summarizer_fw_contexts.cpp @@ -0,0 +1,176 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis with Calling Context output + +Author: Peter Schrammel + +\*******************************************************************/ + +#include // for xml output + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "summarizer_fw_contexts.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +/*******************************************************************\ + +Function: summarizer_fw_contextst::summarize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::summarize() +{ + exprt precondition=true_exprt(); // initial calling context + for(functionst::const_iterator it=ssa_db.functions().begin(); + it!=ssa_db.functions().end(); it++) + { + if(excluded_functions.find(it->first)!=excluded_functions.end()) + continue; + status() << "\nSummarizing function " << it->first << eom; + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) + compute_summary_rec(it->first, precondition, false); + else + status() << "Summary for function " << it->first + << " exists already" << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_fw_contextst::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, const exprt &precondition, + bool context_sensitive) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + { + continue; + } + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(excluded_functions.find(fname)!=excluded_functions.end()) + { + exprt precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + // output calling context + switch(ui) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml_cc("calling-context"); + xml_cc.set_attribute("function", id2string(fname)); + xml_cc.set_attribute( + "goto_location", i2string(n_it->location->location_number)); + + // location + const source_locationt &source_location= + n_it->location->source_location; + xmlt xml_location; + if(source_location.is_not_nil() && source_location.get_file()!="") + xml_location=xml(source_location); + if(xml_location.name!="") + xml_cc.new_element().swap(xml_location); + + // argument ranges + xmlt xml_args("argument-ranges"); + assert(precondition_call.operands().size()%2==0); + for(unsigned i=0; i +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_fw.h" + + +class summarizer_fw_contextst:public summarizer_fwt +{ +public: + explicit summarizer_fw_contextst( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_fwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner), + ui(ui_message_handlert::PLAIN) + { + if(_options.get_bool_option("xml-ui")) + ui=ui_message_handlert::XML_UI; + + optionst::value_listt _excluded_functions= + _options.get_list_option("do-not-analyze-functions"); + excluded_functions.insert( + _excluded_functions.begin(), _excluded_functions.end()); + } + + virtual void summarize(); + + protected: + language_uit::uit ui; // use gui format + std::set excluded_functions; + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &precondition, + bool context_sensitive); +}; + +#endif diff --git a/src/solver/summarizer_fw_term.cpp b/src/solver/summarizer_fw_term.cpp new file mode 100644 index 000000000..63f96ae92 --- /dev/null +++ b/src/solver/summarizer_fw_term.cpp @@ -0,0 +1,372 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "summarizer_fw_term.h" +#include "summary_db.h" + +/*******************************************************************\ + +Function: summarizer_fw_termt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) +{ + if(options.get_bool_option("competition-mode") && + summary_db.exists(ID__start) && + summary_db.get(ID__start).terminates==NO) + { + return; + } + + local_SSAt &SSA=ssa_db.get(function_name); + + // recursively compute summaries for function calls + threevalt calls_terminate=YES; + bool has_function_calls=false; + inline_summaries( + function_name, + SSA, + precondition, + context_sensitive, + calls_terminate, + has_function_calls); + + status() << "Analyzing function " << function_name << eom; + + { + std::ostringstream out; + out << "Function body for " << function_name << + " to be analyzed: " << std::endl; + for(local_SSAt::nodest::iterator n=SSA.nodes.begin(); + n!=SSA.nodes.end(); n++) + { + if(!n->empty()) + n->output(out, SSA.ns); + } + out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << "\n"; + debug() << out.str() << eom; + } + + bool has_loops=false; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) + { + has_loops=true; + break; + } + } + + debug() << "function " + << (has_function_calls ? "has" : "does not have") << " function calls" + << eom; + debug() << "function calls terminate: " + << threeval2string(calls_terminate) << eom; + debug() << "function " + << (has_loops ? "has loops" : "does not have loops") << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.fw_precondition=precondition; + summary.terminates=UNKNOWN; + + // compute summary + if(!options.get_bool_option("havoc")) + { + // We are not allowed to assume the assertions here, + // otherwise we might cut off all terminating executions + // and classify the program as non-terminating. + do_summary(function_name, SSA, summary, true_exprt(), context_sensitive); + } + + // check termination + status() << "Computing termination argument for " << function_name << eom; + // check non-termination if we haven't analyzed this function yet, + // otherwise the termination status is UNKNOWN anyways + if(!summary_db.exists(function_name)) + { + do_nontermination(function_name, SSA, summary); + } + if(summary.terminates==UNKNOWN) + { + bool has_terminating_function_calls= + has_function_calls && calls_terminate==YES; + + if(!has_loops && !has_function_calls) + { + status() << "Function trivially terminates" << eom; + summary.terminates=YES; + } + else if(!has_loops && has_function_calls && calls_terminate==YES) + { + status() << "Function terminates" << eom; + summary.terminates=YES; + } + else if(has_function_calls && calls_terminate!=YES) + { + summary.terminates=calls_terminate; + } + else if(has_loops && + (!has_function_calls || has_terminating_function_calls)) + { + do_termination(function_name, SSA, summary); + } + } + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary.output(out, SSA.ns); + status() << out.str() << eom; + } + + // store summary in db + summary_db.put(function_name, summary); +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, exprt precondition, + bool context_sensitive, + threevalt &calls_terminate, + bool &has_function_calls) +{ + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt::operandst c; + c.push_back(precondition); + get_assertions(SSA, c); // assertions as assumptions + precondition=conjunction(c); + + if(!options.get_bool_option("competition-mode") && + !check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + continue; + + has_function_calls=true; + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(!check_precondition( + function_name, SSA, n_it, f_it, precondition, context_sensitive)) + { + exprt precondition_call=true_exprt(); + if(context_sensitive) + precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, precondition_call, context_sensitive); + summaries_used++; + } + + // get information about callee termination + if(summary_db.exists(fname) && summary_db.get(fname).terminates!=YES) + { + // cannot propagate NO + // because call reachability might be over-approximating + calls_terminate=UNKNOWN; + break; + } + } + } +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::do_nontermination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + if(!summary.fw_invariant.is_nil()) + cond.push_back(summary.fw_invariant); + if(!summary.fw_precondition.is_nil()) + cond.push_back(summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + + if(!check_end_reachable(function_name, SSA, conjunction(cond))) + { + status() << "Function never terminates normally" << eom; + + if(summary.fw_precondition.is_true()) + summary.fw_transformer=false_exprt(); + else + summary.fw_transformer= + implies_exprt(summary.fw_precondition, false_exprt()); + + summary.terminates=NO; + } +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::do_termination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::do_termination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + if(!summary.fw_invariant.is_nil()) + cond.push_back(summary.fw_invariant); + if(!summary.fw_precondition.is_nil()) + cond.push_back(summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + + status() << "Synthesizing ranking function to prove termination" << eom; + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + template_generator_rankingt template_generator1( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator1.set_message_handler(get_message_handler()); + template_generator1(solver.next_domain_number(), SSA, true); + + if(template_generator1.all_vars().empty()) + return; // nothing to do + + get_assertions(SSA, cond); // add assertions as assumptions + + // compute ranking functions + ssa_analyzert analyzer1; + analyzer1.set_message_handler(get_message_handler()); + analyzer1(solver, SSA, conjunction(cond), template_generator1); + analyzer1.get_result( + summary.termination_argument, template_generator1.all_vars()); + + // extract information whether a ranking function was found for all loops + summary.terminates=check_termination_argument(summary.termination_argument); + if(!summary.fw_precondition.is_true()) + summary.termination_argument= + implies_exprt(summary.fw_precondition, summary.termination_argument); + + // statistics + solver_instances+=analyzer1.get_number_of_solver_instances(); + solver_calls+=analyzer1.get_number_of_solver_calls(); + termargs_computed++; +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::check_termination_argument + + Inputs: + + Outputs: + + Purpose: checks whether a termination argument implies termination + +\*******************************************************************/ + +threevalt summarizer_fw_termt::check_termination_argument(exprt expr) +{ + if(expr.is_false()) + return YES; + + // should be of the form /\_i g_i=> R_i + if(expr.id()==ID_and) + { + threevalt result=YES; + for(exprt::operandst::iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) + { + if(it->is_true()) + result=UNKNOWN; + if(it->id()==ID_implies) + { + if(it->op1().is_true()) + result=UNKNOWN; + } + } + return result; + } + else + { + if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + return UNKNOWN; + } + else + return !expr.is_true() ? YES : UNKNOWN; + } + return YES; +} + diff --git a/src/solver/summarizer_fw_term.h b/src/solver/summarizer_fw_term.h new file mode 100644 index 000000000..bc8ad45aa --- /dev/null +++ b/src/solver/summarizer_fw_term.h @@ -0,0 +1,63 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_FW_TERM_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_FW_TERM_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_fw.h" + +class summarizer_fw_termt:public summarizer_fwt +{ +public: + explicit summarizer_fw_termt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_fwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + + static threevalt check_termination_argument(exprt expr); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + exprt precondition, + bool context_sensitive, + threevalt &calls_terminate, + bool &has_function_calls); + + void do_termination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary); + + void do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary); +}; + +#endif diff --git a/src/solver/summary.cpp b/src/solver/summary.cpp new file mode 100644 index 000000000..547002037 --- /dev/null +++ b/src/solver/summary.cpp @@ -0,0 +1,209 @@ +/*******************************************************************\ + +Module: Summary + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include + +#include "summary.h" + +// #define PRETTY_PRINT + +const summaryt::call_sitet summaryt::entry_call_site; + +/*******************************************************************\ + +Function: summaryt::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::output(std::ostream &out, const namespacet &ns) const +{ + out << "params: "; + for(summaryt::var_listt::const_iterator it=params.begin(); + it!=params.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "globals_in: "; + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "globals_out: "; + for(summaryt::var_sett::const_iterator it=globals_out.begin(); + it!=globals_out.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "forward precondition: " + << (fw_precondition.is_nil() ? "not computed" : + from_expr(ns, "", fw_precondition)) << std::endl; + out << "forward transformer: " + << (fw_transformer.is_nil() ? "not computed" : + from_expr(ns, "", fw_transformer)) << std::endl; + out << "forward invariant: " + << (fw_invariant.is_nil() ? "not computed" : + from_expr(ns, "", fw_invariant)) << std::endl; + out << "backward precondition: " + << (bw_precondition.is_nil() ? "not computed" : + from_expr(ns, "", bw_precondition)) << std::endl; + out << "backward postcondition: " + << (bw_postcondition.is_nil() ? "not computed" : + from_expr(ns, "", bw_postcondition)) << std::endl; + out << "backward transformer: " + << (bw_transformer.is_nil() ? "not computed" : + from_expr(ns, "", bw_transformer)) << std::endl; + out << "backward invariant: " + << (bw_invariant.is_nil() ? "not computed" : + from_expr(ns, "", bw_invariant)) << std::endl; + out << "termination argument: "; + if(termination_argument.is_nil()) + out << "not computed"; + else +#if PRETTY_PRINT + pretty_print_termination_argument(out, ns, termination_argument); +#else + out << from_expr(ns, "", termination_argument) << std::endl; +#endif + out << std::endl; + out << "terminates: " << threeval2string(terminates) << std::endl; + for(error_summariest::const_iterator + it=error_summaries.begin(); + it!=error_summaries.end(); it++) + { + out << "error summary for "; + if(it->first==entry_call_site) + out << "entry point"; + else + out << "location " << it->first.location_number; + out << ": " << std::endl + << " " << from_expr(ns, "", it->second) << std::endl; + } +} + +/*******************************************************************\ + +Function: summaryt::combine_and + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::combine_and(exprt &olde, const exprt &newe) +{ + if(olde.is_nil()) + { + olde=newe; + } + else + { + if(newe.is_nil()) + return; + + olde=and_exprt(olde, newe); + } +} + +/*******************************************************************\ + +Function: summaryt::combine_or + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::combine_or(exprt &olde, const exprt &newe) +{ + if(olde.is_nil()) + { + olde=newe; + } + else + { + if(newe.is_nil()) + return; + olde=or_exprt(olde, newe); + } +} + +/*******************************************************************\ + +Function: summaryt::join + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::join(const summaryt &new_summary) +{ + assert(params==new_summary.params); + assert(globals_in==new_summary.globals_in); + assert(globals_out==new_summary.globals_out); + combine_or(fw_precondition, new_summary.fw_precondition); + combine_and(fw_transformer, new_summary.fw_transformer); + combine_and(fw_invariant, new_summary.fw_invariant); + combine_and(bw_precondition, new_summary.bw_precondition); + combine_or(bw_postcondition, new_summary.bw_postcondition); + combine_and(bw_transformer, new_summary.bw_transformer); + combine_and(bw_invariant, new_summary.bw_invariant); + combine_and(termination_argument, new_summary.termination_argument); + switch(new_summary.terminates) + { + case YES: + break; + case NO: terminates=NO; + break; + case UNKNOWN: + if(terminates!=NO) + terminates=UNKNOWN; + break; + default: assert(false); + } +} + +/*******************************************************************\ + +Function: threeval2string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string threeval2string(threevalt v) +{ + switch(v) + { + case YES: return "yes"; + case NO: return "no"; + case UNKNOWN: return "unknown"; + } + assert(false); +} diff --git a/src/summarizer/summary.h b/src/solver/summary.h similarity index 51% rename from src/summarizer/summary.h rename to src/solver/summary.h index a2c9bfa2d..c7e68a6c6 100644 --- a/src/summarizer/summary.h +++ b/src/solver/summary.h @@ -6,15 +6,18 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_DELTACHECK_SUMMARY_H -#define CPROVER_DELTACHECK_SUMMARY_H +#ifndef CPROVER_2LS_SOLVER_SUMMARY_H +#define CPROVER_2LS_SOLVER_SUMMARY_H +#include #include #include #include -typedef enum{YES, NO, UNKNOWN} threevalt; +#include + +typedef enum {YES, NO, UNKNOWN} threevalt; class summaryt { @@ -23,36 +26,67 @@ class summaryt typedef std::list var_listt; typedef std::set var_sett; - - summaryt() : - fw_precondition(nil_exprt()), - fw_transformer(nil_exprt()), - fw_invariant(nil_exprt()), - bw_precondition(nil_exprt()), - bw_postcondition(nil_exprt()), - bw_transformer(nil_exprt()), + typedef std::set expr_sett; + + summaryt() : + fw_precondition(nil_exprt()), + fw_transformer(nil_exprt()), + fw_invariant(nil_exprt()), + bw_precondition(nil_exprt()), + bw_postcondition(nil_exprt()), + bw_transformer(nil_exprt()), bw_invariant(nil_exprt()), - termination_argument(nil_exprt()), + termination_argument(nil_exprt()), terminates(UNKNOWN), mark_recompute(false) {} var_listt params; var_sett globals_in, globals_out; - - + expr_sett nondets; predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently predicatet fw_transformer; // forward summary (over-approx) predicatet fw_invariant; // forward invariant (over-approx) - predicatet bw_precondition; // accumulated preconditions (over- or under-approx) - predicatet bw_postcondition; // accumulated postconditions (over- or under-approx) + predicatet bw_precondition; // accumulated preconditions (over/under-approx) + predicatet bw_postcondition; // accumulated postconditions (over/under-approx) predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) predicatet termination_argument; threevalt terminates; - bool mark_recompute; //to force recomputation of the summary + // -------------- + // the following is for generating interprocedural counterexample + + bool has_assertion; + + std::list nonpassed_assertions; + + struct call_sitet + { // TODO: we also need unwinding information here + call_sitet():location_number(UINT_MAX) {} + explicit call_sitet(local_SSAt::locationt loc): + location_number(loc->location_number) + { + } + unsigned location_number; + + bool operator<(const call_sitet &other) const + { + return (location_number error_summariest; + error_summariest error_summaries; + // -------------- + + bool mark_recompute; // to force recomputation of the summary // (used for invariant reuse in k-induction) void output(std::ostream &out, const namespacet &ns) const; @@ -60,13 +94,10 @@ class summaryt void join(const summaryt &new_summary); protected: - void combine_or(exprt &olde, const exprt &newe); void combine_and(exprt &olde, const exprt &newe); - }; std::string threeval2string(threevalt v); - #endif diff --git a/src/summarizer/summary_db.cpp b/src/solver/summary_db.cpp similarity index 64% rename from src/summarizer/summary_db.cpp rename to src/solver/summary_db.cpp index 4e7dde39e..a04e8d809 100644 --- a/src/summarizer/summary_db.cpp +++ b/src/solver/summary_db.cpp @@ -12,21 +12,46 @@ Author: Daniel Kroening, kroening@kroening.com #include "summary_db.h" -void summary_dbt::put(const function_namet &function_name, - const summaryt &summary) -{ - if(store.find(function_name)==store.end() || +/*******************************************************************\ + +Function: summary_dbt::put + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::put( + const function_namet &function_name, + const summaryt &summary) +{ + if(store.find(function_name)==store.end() || store[function_name].mark_recompute) - store[function_name] = summary; + store[function_name]=summary; else store[function_name].join(summary); } +/*******************************************************************\ + +Function: summary_dbt::mark_recompute_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void summary_dbt::mark_recompute_all() { - for(std::map::iterator it = store.begin(); - it != store.end(); it++) - it->second.mark_recompute = true; + for(std::map::iterator it=store.begin(); + it!=store.end(); it++) + it->second.mark_recompute=true; } /*******************************************************************\ @@ -62,7 +87,7 @@ void summary_dbt::read(const std::string &id) { current=id; - summary=jsont::json_object(); + summary.make_object(); parse_json(file_name(id), get_message_handler(), summary); } diff --git a/src/summarizer/summary_db.h b/src/solver/summary_db.h similarity index 74% rename from src/summarizer/summary_db.h rename to src/solver/summary_db.h index bd2dcaa27..04145d8a5 100644 --- a/src/summarizer/summary_db.h +++ b/src/solver/summary_db.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_SUMMARY_DB_H -#define CPROVER_SUMMARIZER_SUMMARY_DB_H +#ifndef CPROVER_2LS_SOLVER_SUMMARY_DB_H +#define CPROVER_2LS_SOLVER_SUMMARY_DB_H #include "summary.h" #include @@ -23,9 +23,11 @@ class summary_dbt:public messaget void write(); void clear() { store.clear(); } - summaryt get(const function_namet &function_name) const + summaryt get(const function_namet &function_name) const { return store.at(function_name); } - bool exists(const function_namet &function_name) const + void set(const function_namet &function_name, const summaryt &summary) + { store[function_name]=summary; } + bool exists(const function_namet &function_name) const { return store.find(function_name)!=store.end(); } void put(const function_namet &function_name, const summaryt &summary); diff --git a/src/ssa/Makefile b/src/ssa/Makefile index b05aa8749..b997fff90 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,23 +1,38 @@ -include ../config.inc -CBMC ?= ../.. - SRC = local_ssa.cpp \ - ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ - guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ - ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ - ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ - unwindable_local_ssa.cpp split_loopheads.cpp + ssa_domain.cpp \ + translate_union_member.cpp \ + malloc_ssa.cpp \ + guard_map.cpp \ + ssa_object.cpp \ + assignments.cpp \ + ssa_dereference.cpp \ + ssa_value_set.cpp \ + address_canonizer.cpp \ + simplify_ssa.cpp \ + ssa_build_goto_trace.cpp \ + ssa_inliner.cpp \ + ssa_unwinder.cpp \ + unwindable_local_ssa.cpp \ + ssa_const_propagator.cpp \ + ssa_dependency_graph.cpp \ + ssa_refiner_monolithic.cpp \ + ssa_refiner_selective.cpp \ + # empty last line +include ../config.inc include $(CBMC)/src/config.inc include $(CBMC)/src/common +CBMC ?= ../.. -CP_CXXFLAGS += $(SUMMARIZERFLAGS) +CP_CXXFLAGS += $(TWOLSFLAGS) -INCLUDES= -I $(CBMC)/src +INCLUDES= -I $(CBMC)/src -I .. -CLEANFILES = +CLEANFILES = ssa$(LIBEXT) -all: $(OBJ) +all: ssa$(LIBEXT) ############################################################################### +ssa$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/ssa/address_canonizer.cpp b/src/ssa/address_canonizer.cpp index 4b64b4378..2ffab3ff1 100644 --- a/src/ssa/address_canonizer.cpp +++ b/src/ssa/address_canonizer.cpp @@ -46,17 +46,18 @@ exprt address_canonizer( { // get offset exprt offset=member_offset_expr(to_member_expr(object), ns); - + // &x.m ---> (&x)+offset - + address_of_exprt address_of_expr(to_member_expr(object).struct_op()); exprt rec_result=address_canonizer(address_of_expr, ns); // rec. call pointer_typet byte_pointer(unsigned_char_type()); typecast_exprt typecast_expr(rec_result, byte_pointer); plus_exprt sum(typecast_expr, offset); - if(sum.type()!=address.type()) sum.make_typecast(address.type()); - + if(sum.type()!=address.type()) + sum.make_typecast(address.type()); + return sum; } else if(object.id()==ID_index) @@ -69,8 +70,9 @@ exprt address_canonizer( pointer_type.subtype()=object.type(); typecast_exprt typecast_expr(rec_result, pointer_type); plus_exprt sum(typecast_expr, to_index_expr(object).index()); - if(sum.type()!=address.type()) sum.make_typecast(address.type()); - + if(sum.type()!=address.type()) + sum.make_typecast(address.type()); + return sum; } else @@ -105,7 +107,7 @@ exprt address_canonizer( else if(address.id()==ID_typecast) { typecast_exprt tmp=to_typecast_expr(address); - + // cast from another pointer? if(tmp.op().type().id()==ID_pointer) { diff --git a/src/ssa/address_canonizer.h b/src/ssa/address_canonizer.h index f917adc79..72281d792 100644 --- a/src/ssa/address_canonizer.h +++ b/src/ssa/address_canonizer.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_ADDRESS_CANONIZER_H -#define CPROVER_ADDRESS_CANONIZER_H +#ifndef CPROVER_2LS_SSA_ADDRESS_CANONIZER_H +#define CPROVER_2LS_SSA_ADDRESS_CANONIZER_H #include #include diff --git a/src/ssa/assignments.cpp b/src/ssa/assignments.cpp index bcae6e998..2a5bc1c6b 100644 --- a/src/ssa/assignments.cpp +++ b/src/ssa/assignments.cpp @@ -31,7 +31,7 @@ void assignmentst::build_assignment_map( { // make sure we have the location in the map assignment_map[it]; - + // now fill it if(it->is_assign()) { @@ -46,12 +46,13 @@ void assignmentst::build_assignment_map( } else if(it->is_function_call()) { - const code_function_callt &code_function_call=to_code_function_call(it->code); + const code_function_callt &code_function_call= + to_code_function_call(it->code); // functions may alter state almost arbitrarily: // * any global-scoped variables // * any dirty locals - + for(objectst::const_iterator o_it=ssa_objects.dirty_locals.begin(); o_it!=ssa_objects.dirty_locals.end(); o_it++) @@ -65,7 +66,8 @@ void assignmentst::build_assignment_map( // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) { - exprt lhs_deref=dereference(code_function_call.lhs(), ssa_value_ai[it], "", ns); + exprt lhs_deref= + dereference(code_function_call.lhs(), ssa_value_ai[it], "", ns); assign(lhs_deref, it, ns); } } @@ -95,15 +97,15 @@ void assignmentst::assign( if(is_symbol_struct_member(lhs, ns)) { const typet &lhs_type=ns.follow(lhs.type()); - + if(lhs_type.id()==ID_struct) { // Are we assigning an entire struct? // If so, need to split into pieces, recursively. - + const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -112,13 +114,13 @@ void assignmentst::assign( member_exprt new_lhs(lhs, it->get_name(), it->type()); assign(new_lhs, loc, ns); // recursive call } - + return; // done } - + // object? ssa_objectt ssa_object(lhs, ns); - + if(ssa_object) { assign(ssa_object, loc, ns); @@ -201,12 +203,13 @@ void assignmentst::output( { out << "**** " << i_it->location_number << " " << i_it->source_location << "\n"; - + assignment_mapt::const_iterator m_it=assignment_map.find(i_it); - if(m_it==assignment_map.end()) throw "location not found"; - + if(m_it==assignment_map.end()) + throw "location not found"; + const objectst &objects=m_it->second; - + for(objectst::const_iterator o_it=objects.begin(); o_it!=objects.end(); @@ -214,7 +217,7 @@ void assignmentst::output( { out << o_it->get_identifier() << "\n"; } - + out << "\n"; } } diff --git a/src/ssa/assignments.h b/src/ssa/assignments.h index e21462cfe..28b36d608 100644 --- a/src/ssa/assignments.h +++ b/src/ssa/assignments.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_ASSIGNMENTS_H -#define CPROVER_ASSIGNMENTS_H +#ifndef CPROVER_2LS_SSA_ASSIGNMENTS_H +#define CPROVER_2LS_SSA_ASSIGNMENTS_H #include @@ -26,14 +26,15 @@ class assignmentst typedef std::map assignment_mapt; assignment_mapt assignment_map; - + bool assigns(locationt loc, const ssa_objectt &object) const { assignment_mapt::const_iterator it=assignment_map.find(loc); - if(it==assignment_map.end()) return false; + if(it==assignment_map.end()) + return false; return it->second.find(object)!=it->second.end(); } - + inline const objectst &get(locationt loc) const { assignment_mapt::const_iterator it=assignment_map.find(loc); @@ -51,22 +52,22 @@ class assignmentst { build_assignment_map(_goto_program, _ns); } - + void output( const namespacet &ns, const goto_programt &_goto_program, std::ostream &); - + protected: void build_assignment_map(const goto_programt &, const namespacet &); void assign( const exprt &lhs, locationt, const namespacet &ns); - + void assign( const ssa_objectt &lhs, locationt, - const namespacet &ns); + const namespacet &ns); }; #endif diff --git a/src/ssa/guard_domain.cpp b/src/ssa/guard_domain.cpp deleted file mode 100644 index d7c8b8106..000000000 --- a/src/ssa/guard_domain.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/*******************************************************************\ - -Module: Definition Domain - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include "guard_domain.h" - -/*******************************************************************\ - -Function: guard_domaint::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void guard_domaint::output( - std::ostream &out, - const ai_baset &, - const namespacet &ns) const -{ - if(unreachable) - { - out << "UNREACHABLE\n"; - return; - } - - for(guardst::const_iterator - g_it=guards.begin(); - g_it!=guards.end(); - g_it++) - { - if(g_it!=guards.begin()) out << " "; - switch(g_it->kind) - { - case guardt::NONE: assert(false); break; - case guardt::BRANCH_TAKEN: out << "bt"; break; - case guardt::BRANCH_NOT_TAKEN: out << "bnt"; break; - case guardt::MERGED: break; - } - - out << g_it->loc->location_number; - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: guard_domaint::transform - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void guard_domaint::transform( - locationt from, - locationt to, - ai_baset &, - const namespacet &ns) -{ - if(unreachable) return; - - if(from->is_goto()) - { - if(from->get_target()==to) - { - // taken - if(!from->guard.is_true()) - guards.push_back(guardt(from, true)); - } - else - { - // not taken - guards.push_back(guardt(from, false)); - } - } - else if(from->is_assume()) - { - guards.push_back(guardt(from)); - } - else if(from->is_function_call()) - { - // Functions might not return, but we will assume that - // for now. - //guards.push_back(guardt(from)); - } -} - -/*******************************************************************\ - -Function: guard_domaint::merge - - Inputs: - - Outputs: return true if "this" has changed - - Purpose: - -\*******************************************************************/ - -bool guard_domaint::merge( - const guard_domaint &b, - locationt from, - locationt to) -{ - // Merging a blank state doesn't change anything. - if(b.unreachable) return false; - - // update 'from' - incoming[from]=b.guards; - - if(unreachable) - { - // copy guards of 'b' - unreachable=false; - guards=b.guards; - return true; - } - - guardst new_guards; - - // Just one incoming edge? - if(incoming.size()==1) - { - new_guards=incoming.begin()->second; - } - else if(incoming.size()==2) - { - // This is the 'OR' between the two conjunctions. - // If this is something simple, we use it. - const guardst &g1=incoming.begin()->second; - const guardst &g2=incoming.rbegin()->second; - - if(prefix_match(g1, g2) && - g1.back().is_branch() && g2.back().is_branch() && - g1.back().loc==g2.back().loc) - { - // We have PREFIX bt loc and PREFIX bnt loc. - // The 'OR' is PREFIX. - new_guards=g1; - new_guards.resize(new_guards.size()-1); - } - else - { - // introduce merge guard - new_guards.push_back(guardt(to)); - } - } - else - { - // Otherwise, we introduce a brand-new merge guard. - new_guards.push_back(guardt(to)); - } - - if(new_guards==guards) - return false; - - guards.swap(new_guards); - - return true; -} diff --git a/src/ssa/guard_domain.h b/src/ssa/guard_domain.h deleted file mode 100644 index 5cd2a6811..000000000 --- a/src/ssa/guard_domain.h +++ /dev/null @@ -1,113 +0,0 @@ -/*******************************************************************\ - -Module: Discover the Guards of Basic Blocks - -Author: Daniel Kroening, kroening@kroening.com - -SEEMS OBSOLETE - -\*******************************************************************/ - -#ifndef CPROVER_GUARD_DOMAIN_H -#define CPROVER_GUARD_DOMAIN_H - -#include - -class guard_domaint:public ai_domain_baset -{ -public: - // the constructor builds 'bottom' - inline guard_domaint():unreachable(true) - { - } - - // A guard may be one of the following: - // 1) a location of a branch, possibly negated for the else-case - // 2) a location of a merged guard - struct guardt - { - public: - locationt loc; - - enum { NONE, BRANCH_TAKEN, BRANCH_NOT_TAKEN, MERGED } kind; - - guardt():kind(NONE) - { - } - - explicit guardt(locationt _loc):loc(_loc), kind(MERGED) - { - } - - guardt(locationt branch, bool truth): - loc(branch), kind(truth?BRANCH_TAKEN:BRANCH_NOT_TAKEN) - { - } - - inline bool is_branch() const - { - return kind==BRANCH_TAKEN || kind==BRANCH_NOT_TAKEN; - } - }; - - inline friend bool operator==(const guardt &a, const guardt &b) - { - return a.kind==b.kind && - a.loc->location_number==b.loc->location_number; - } - - inline friend bool operator!=(const guardt &a, const guardt &b) - { - return !(a==b); - } - - // We may be under some set of guards. - typedef std::vector guardst; - guardst guards; - - bool unreachable; - - // Keep the guards for all incoming edges. - typedef std::map incomingt; - incomingt incoming; - - // returns true iff 'a' and 'b' match in all but the last place - static bool prefix_match(const guardst &a, const guardst &b) - { - if(a.size()!=b.size() || a.empty()) return false; - for(unsigned i=0; i -{ -protected: - virtual void initialize(const goto_programt &goto_program) - { - ait::initialize(goto_program); - - // make entry instruction reachable - if(!goto_program.instructions.empty()) - operator[](goto_program.instructions.begin()).unreachable=false; - } -}; - -#endif diff --git a/src/ssa/guard_map.cpp b/src/ssa/guard_map.cpp index d039438c9..6b5de89ab 100644 --- a/src/ssa/guard_map.cpp +++ b/src/ssa/guard_map.cpp @@ -38,7 +38,7 @@ void guard_mapt::output( in_it++) out << " " << in_it->guard_source->location_number << " (" << in_it->kind << ")"; - + out << "\n"; } } @@ -63,11 +63,11 @@ void guard_mapt::build(const goto_programt &src) { locationt next=it; next++; - + if(it->is_goto()) { map[it->get_target()->location_number].add_in(it, TAKEN); - + if(!it->guard.is_true()) map[next->location_number].add_in(it, NOT_TAKEN); else @@ -88,7 +88,7 @@ void guard_mapt::build(const goto_programt &src) // Also make the function entry location have a guard if(!src.instructions.empty()) map[src.instructions.begin()->location_number].has_guard=true; - + // now assign the guard sources accordingly locationt g; @@ -96,7 +96,7 @@ void guard_mapt::build(const goto_programt &src) forall_goto_program_instructions(it, src) { entryt &entry=map[it->location_number]; - + if(entry.has_guard) { entry.guard_source=it; // self-pointer @@ -105,19 +105,19 @@ void guard_mapt::build(const goto_programt &src) else entry.guard_source=g; // previous } - + // Locations with guards get the successor edge // in the CFG. locationt previous; - + forall_goto_program_instructions(it, src) { // skip first, which has no predecessor if(it!=src.instructions.begin()) { entryt &entry=map[it->location_number]; - + // no need if previous is a goto if(entry.has_guard && !previous->is_goto() && @@ -125,10 +125,10 @@ void guard_mapt::build(const goto_programt &src) !previous->is_function_call()) entry.add_in(previous, SUCCESSOR); } - + previous=it; } - + // now do guard sources of edges for(mapt::iterator m_it=map.begin(); m_it!=map.end(); m_it++) diff --git a/src/ssa/guard_map.h b/src/ssa/guard_map.h index 2e7ca7cce..e59a725cb 100644 --- a/src/ssa/guard_map.h +++ b/src/ssa/guard_map.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_GUARD_MAP_H -#define CPROVER_GUARD_MAP_H +#ifndef CPROVER_2LS_SSA_GUARD_MAP_H +#define CPROVER_2LS_SSA_GUARD_MAP_H #include #include @@ -22,11 +22,11 @@ class guard_mapt { build(src); } - - typedef goto_programt::const_targett locationt; + + typedef goto_programt::const_targett locationt; enum kindt { SUCCESSOR, TAKEN, NOT_TAKEN, ASSUME, FUNCTION_CALL } kind; - + friend std::ostream & operator << (std::ostream &out, kindt kind) { switch(kind) @@ -44,7 +44,7 @@ class guard_mapt { locationt from, guard_source; kindt kind; - + bool is_branch_not_taken() const { return kind==NOT_TAKEN; @@ -82,10 +82,10 @@ class guard_mapt public: inline entryt():has_guard(false) { } bool has_guard; - + // if location has a guard of its own this is a self-pointer locationt guard_source; - + // if it has a guard of its own: incomingt incoming; @@ -95,7 +95,7 @@ class guard_mapt incoming.push_back(edget(l, k)); } }; - + // Query me! I return the entry for any program location. inline const entryt &operator[](const locationt location) const { @@ -103,15 +103,15 @@ class guard_mapt assert(it!=map.end()); return it->second; } - + void output( const goto_programt &goto_program, std::ostream &) const; - + protected: void build(const goto_programt &src); - - //use location number as key to make iteration deterministic + + // use location number as key to make iteration deterministic typedef std::map mapt; mapt map; }; diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index c71bbf21a..e997d27d3 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -19,9 +19,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include +#include -#include + +#include #include "local_ssa.h" #include "malloc_ssa.h" @@ -44,21 +45,22 @@ void local_SSAt::build_SSA() { // perform SSA data-flow analysis ssa_analysis(goto_function, ns); - + forall_goto_program_instructions(i_it, goto_function.body) { - nodest::iterator loophead_node = nodes.end(); + nodest::iterator loophead_node=nodes.end(); if(i_it->is_backwards_goto()) { - loophead_node = find_node(i_it->get_target()); + loophead_node=find_node(i_it->get_target()); } - nodes.push_back(nodet(i_it,loophead_node)); + nodes.push_back(nodet(i_it, loophead_node)); build_transfer(i_it); build_phi_nodes(i_it); build_cond(i_it); build_guard(i_it); build_assertions(i_it); + build_assumptions(i_it); build_function_call(i_it); } @@ -83,31 +85,94 @@ Function: local_SSAt::get_entry_exit_vars void local_SSAt::get_entry_exit_vars() { - //get parameters - const code_typet::parameterst ¶meter_types = + goto_programt::const_targett first=goto_function.body.instructions.begin(); + + // get parameters + const code_typet::parameterst ¶meter_types= goto_function.type.parameters(); for(code_typet::parameterst::const_iterator - it=parameter_types.begin(); it!=parameter_types.end(); it++) + it=parameter_types.begin(); it!=parameter_types.end(); it++) { const code_typet::parametert ¶meter=*it; const irep_idt &identifier=parameter.get_identifier(); const symbolt *symbol; - if(ns.lookup(identifier,symbol)) continue; + if(ns.lookup(identifier, symbol)) + continue; - params.push_back(symbol->symbol_expr()); + if(ns.follow(symbol->type).id()==ID_struct) + { + exprt param=read_rhs(symbol->symbol_expr(), first); +#if 0 + std::cout << "param: " + << from_expr(ns, "", param) << std::endl; +#endif + forall_operands(it, param) + params.push_back(to_symbol_expr(*it)); + } + else + params.push_back(symbol->symbol_expr()); } - //get globals in - goto_programt::const_targett first = goto_function.body.instructions.begin(); - get_globals(first,globals_in,true,false); //filters out #return_value + // get globals in + get_globals(first, globals_in, true, false); // filters out #return_value - //get globals out (includes return value) - goto_programt::const_targett - last = goto_function.body.instructions.end(); last--; - get_globals(last,globals_out,true,true,last->function); + // get globals out (includes return value) + goto_programt::const_targett + last=goto_function.body.instructions.end(); last--; + get_globals(last, globals_out, true, true, last->function); + + // get nondeterministic variables + get_nondet_vars(); } +/*******************************************************************\ + +Function: local_SSAt::get_nondet_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::get_nondet_vars(const exprt &expr) +{ + if(expr.id()==ID_nondet_symbol) + nondets.insert(expr); + else + forall_operands(it, expr) + get_nondet_vars(*it); +} + +void local_SSAt::get_nondet_vars() +{ + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + { + for(nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + get_nondet_vars(*e_it); + + for(nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + get_nondet_vars(*c_it); + + for(nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + get_nondet_vars(*a_it); + } +} + + /*******************************************************************\ Function: local_SSAt::get_globals @@ -120,45 +185,54 @@ Function: local_SSAt::get_globals \*******************************************************************/ -void local_SSAt::get_globals(locationt loc, std::set &globals, - bool rhs_value, bool with_returns, - const irep_idt &returns_for_function) const +void local_SSAt::get_globals( + locationt loc, + std::set &globals, + bool rhs_value, + bool with_returns, + const irep_idt &returns_for_function) const { { - const std::set &ssa_globals = assignments.ssa_objects.globals; - for(std::set::const_iterator it = ssa_globals.begin(); - it != ssa_globals.end(); it++) + const std::set &ssa_globals=assignments.ssa_objects.globals; + for(std::set::const_iterator it=ssa_globals.begin(); + it!=ssa_globals.end(); it++) { #if 0 - std::cout << "global: " - << from_expr(ns, "", read_lhs(it->get_expr(),loc)) << std::endl; + std::cout << "global: " + << from_expr(ns, "", read_lhs(it->get_expr(), loc)) + << std::endl; #endif - if(!with_returns && - id2string(it->get_identifier()).find( - "#return_value") != std::string::npos) - continue; + bool is_return=id2string(it->get_identifier()).find( + "#return_value")!=std::string::npos; + if(!with_returns && is_return) + continue; - //filter out return values of other functions + // filter out return values of other functions if(with_returns && returns_for_function!="" && - id2string(it->get_identifier()).find( - "#return_value") != std::string::npos && + is_return && id2string(it->get_identifier()).find( id2string(returns_for_function)+"#return_value")==std::string::npos) - continue; + continue; if(rhs_value) { - const exprt &expr = read_rhs(it->get_expr(),loc); + // workaround for the problem that + // rhs() for a return value is always the "input" return value + #if 0 // --loc may be invalid + const exprt &expr=is_return ? + read_lhs(it->get_expr(), --loc) : read_rhs(it->get_expr(), loc); + #endif + const exprt &expr=read_rhs(it->get_expr(), loc); globals.insert(to_symbol_expr(expr)); } else { - const exprt &expr = read_lhs(it->get_expr(),loc); + const exprt &expr=read_lhs(it->get_expr(), loc); globals.insert(to_symbol_expr(expr)); } } } -} +} /*******************************************************************\ @@ -175,20 +249,22 @@ Function: local_SSAt::collect_custom_templates void local_SSAt::collect_custom_templates() { - for(local_SSAt::nodest::iterator n_it=nodes.begin(); + for(local_SSAt::nodest::iterator n_it=nodes.begin(); n_it!=nodes.end(); n_it++) { - if(n_it->loophead != nodes.end()) //we've found a loop + if(n_it->loophead!=nodes.end()) // we've found a loop { - //search for templates in the loop - for(local_SSAt::nodest::iterator nn_it=n_it->loophead; - nn_it!=n_it; nn_it++) + // search for templates in the loop + for(local_SSAt::nodest::iterator nn_it=n_it->loophead; + nn_it!=n_it; nn_it++) { - if(nn_it->templates.empty()) continue; - n_it->loophead->templates.insert(n_it->loophead->templates.end(), - nn_it->templates.begin(), - nn_it->templates.end()); - nn_it->templates.clear(); + if(nn_it->templates.empty()) + continue; + + n_it->loophead->templates.insert(n_it->loophead->templates.end(), + nn_it->templates.begin(), + nn_it->templates.end()); + nn_it->templates.clear(); } } } @@ -208,10 +284,11 @@ Function: local_SSAt::find_node local_SSAt::nodest::iterator local_SSAt::find_node(locationt loc) { - nodest::iterator n_it = nodes.begin(); - for(; n_it != nodes.end(); n_it++) + nodest::iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) { - if(n_it->location == loc) break; + if(n_it->location==loc) + break; } return n_it; } @@ -230,10 +307,11 @@ Function: local_SSAt::find_node local_SSAt::nodest::const_iterator local_SSAt::find_node(locationt loc) const { - nodest::const_iterator n_it = nodes.begin(); - for(; n_it != nodes.end(); n_it++) + nodest::const_iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) { - if(n_it->location == loc) break; + if(n_it->location==loc) + break; } return n_it; } @@ -250,18 +328,43 @@ Function: local_SSAt::find_nodes \*******************************************************************/ -void local_SSAt::find_nodes(locationt loc, - std::list &_nodes) const +void local_SSAt::find_nodes( + locationt loc, + std::list &_nodes) const { - nodest::const_iterator n_it = nodes.begin(); - for(; n_it != nodes.end(); n_it++) + nodest::const_iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) { - if(n_it->location == loc) _nodes.push_back(n_it); + if(n_it->location==loc) + _nodes.push_back(n_it); } } /*******************************************************************\ +Function: local_SSAt::find_location_by_number + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +local_SSAt::locationt local_SSAt::find_location_by_number( + unsigned location_number) const +{ + for(const auto &node : nodes) + { + if(node.location->location_number==location_number) + return node.location; + } + assert(false); +} + +/*******************************************************************\ + Function: local_SSAt::edge_guard Inputs: @@ -305,25 +408,27 @@ Function: local_SSAt::build_phi_nodes void local_SSAt::build_phi_nodes(locationt loc) { const ssa_domaint::phi_nodest &phi_nodes=ssa_analysis[loc].phi_nodes; - nodet &node= *(--nodes.end()); + nodet &node=*(--nodes.end()); for(objectst::const_iterator - o_it=ssa_objects.objects.begin(); - o_it!=ssa_objects.objects.end(); o_it++) + o_it=ssa_objects.objects.begin(); + o_it!=ssa_objects.objects.end(); ++o_it) { // phi-node for this object here? ssa_domaint::phi_nodest::const_iterator p_it= phi_nodes.find(o_it->get_identifier()); - - if(p_it==phi_nodes.end()) continue; // none - - #ifdef DEBUG - std::cout << "PHI " << o_it->get_identifier() << "\n"; - #endif - - //ignore custom template variables + + if(p_it==phi_nodes.end()) // none + continue; + +#ifdef DEBUG + std::cout << "PHI " << o_it->get_identifier() << "\n"; +#endif + + // ignore custom template variables if(id2string(o_it->get_identifier()). - find(TEMPLATE_PREFIX)!=std::string::npos) continue; + find(TEMPLATE_PREFIX)!=std::string::npos) + continue; // Yes. Get the source -> def map. const ssa_domaint::loc_def_mapt &incoming=p_it->second; @@ -334,50 +439,50 @@ void local_SSAt::build_phi_nodes(locationt loc) // and do forwards-edges first, which gives them // _lower_ priority in the ITE. Inputs are always // forward edges. - + for(ssa_domaint::loc_def_mapt::const_iterator incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + incoming_it!=incoming.end(); + incoming_it++) if(incoming_it->second.is_input() || - incoming_it->first < loc->location_number) - { - // it's a forward edge - exprt incoming_value=name(*o_it, incoming_it->second); - //TODO: investigate: here is some nondeterminism - // whether g2 (=g1&c1 (maybe)) or (g1&c1) is used, - // not sure whether this has consequences - // (further than the SSA looking different each time you generate it) - exprt incoming_guard=edge_guard(get_location(incoming_it->first),loc); - - if(rhs.is_nil()) // first - rhs=incoming_value; - else - rhs=if_exprt(incoming_guard, incoming_value, rhs); - } - + incoming_it->firstlocation_number) + { + // it's a forward edge + exprt incoming_value=name(*o_it, incoming_it->second); + // TODO: investigate: here is some nondeterminism + // whether g2 (=g1&c1 (maybe)) or (g1&c1) is used, + // not sure whether this has consequences + // (further than the SSA looking different each time you generate it) + exprt incoming_guard=edge_guard(get_location(incoming_it->first), loc); + + if(rhs.is_nil()) // first + rhs=incoming_value; + else + rhs=if_exprt(incoming_guard, incoming_value, rhs); + } + // now do backwards for(ssa_domaint::loc_def_mapt::const_iterator incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + incoming_it!=incoming.end(); + incoming_it++) if(!incoming_it->second.is_input() && - incoming_it->first >= loc->location_number) - { - // it's a backwards edge - const locationt &iloc = get_location(incoming_it->first); - exprt incoming_value=name(*o_it, LOOP_BACK, iloc); - exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); - - if(rhs.is_nil()) // first - rhs=incoming_value; - else - rhs=if_exprt(incoming_select, incoming_value, rhs); - } + incoming_it->first>=loc->location_number) + { + // it's a backwards edge + const locationt &iloc=get_location(incoming_it->first); + exprt incoming_value=name(*o_it, LOOP_BACK, iloc); + exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); + + if(rhs.is_nil()) // first + rhs=incoming_value; + else + rhs=if_exprt(incoming_select, incoming_value, rhs); + } symbol_exprt lhs=name(*o_it, PHI, loc); - + equal_exprt equality(lhs, rhs); node.equalities.push_back(equality); } @@ -420,23 +525,23 @@ void local_SSAt::build_transfer(locationt loc) { const code_assignt &code_assign=to_code_assign(loc->code); - // template declarations - if(code_assign.lhs().id()==ID_symbol && + // template declarations + if(code_assign.lhs().id()==ID_symbol && id2string(code_assign.lhs().get(ID_identifier)). - find("return_value_" TEMPLATE_NEWVAR) != std::string::npos) + find("return_value_" TEMPLATE_NEWVAR)!=std::string::npos) { - //propagate equalities through replace map - exprt lhs = code_assign.lhs(); - template_newvars[lhs] = template_newvars[template_last_newvar]; - template_last_newvar = lhs; + // propagate equalities through replace map + exprt lhs=code_assign.lhs(); + template_newvars[lhs]=template_newvars[template_last_newvar]; + template_last_newvar=lhs; return; } - if(code_assign.lhs().id()==ID_symbol && + if(code_assign.lhs().id()==ID_symbol && id2string(code_assign.lhs().get(ID_identifier)). - find(TEMPLATE_PREFIX)!=std::string::npos) return; - if(code_assign.rhs().id()==ID_symbol && + find(TEMPLATE_PREFIX)!=std::string::npos) return; + if(code_assign.rhs().id()==ID_symbol && id2string(code_assign.rhs().get(ID_identifier)). - find(TEMPLATE_PREFIX)!=std::string::npos) return; + find(TEMPLATE_PREFIX)!=std::string::npos) return; exprt deref_lhs=dereference(code_assign.lhs(), loc); exprt deref_rhs=dereference(code_assign.rhs(), loc); @@ -444,7 +549,7 @@ void local_SSAt::build_transfer(locationt loc) assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); } } - + /*******************************************************************\ Function: local_SSAt::build_function_call @@ -465,69 +570,85 @@ void local_SSAt::build_function_call(locationt loc) to_code_function_call(loc->code); const exprt &lhs=code_function_call.lhs(); - + if(lhs.is_not_nil()) { exprt deref_lhs=dereference(lhs, loc); - + // generate a symbol for rhs irep_idt identifier="ssa::return_value"+i2string(loc->location_number); symbol_exprt rhs(identifier, code_function_call.lhs().type()); - + assign_rec(deref_lhs, rhs, true_exprt(), loc); } - nodest::iterator n_it = --nodes.end(); + nodest::iterator n_it=--nodes.end(); - //template declarations + // template declarations if(code_function_call.function().id()==ID_symbol && - has_prefix(TEMPLATE_DECL, - id2string(code_function_call.function().get(ID_identifier)))) + has_prefix( + TEMPLATE_DECL, + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); n_it->templates.push_back(code_function_call.arguments()[0]); // replace "new" vars - replace_expr(template_newvars,n_it->templates.back()); + replace_expr(template_newvars, n_it->templates.back()); #if 0 - std::cout << "found template declaration: " - << from_expr(ns,"",code_function_call.arguments()[0]) - << std::endl; + std::cout << "found template declaration: " + << from_expr(ns, "", code_function_call.arguments()[0]) + << std::endl; #endif template_newvars.clear(); return; } - //turn function call into expression + // turn function call into expression function_application_exprt f; - f.function() = code_function_call.function(); - f.type() = code_function_call.lhs().type(); - f.arguments() = code_function_call.arguments(); + f.function()=code_function_call.function(); + f.type()=code_function_call.lhs().type(); + f.arguments()=code_function_call.arguments(); - //access to "new" value in template declarations + // access to "new" value in template declarations if(code_function_call.function().id()==ID_symbol && - has_prefix(TEMPLATE_NEWVAR, - id2string(code_function_call.function().get(ID_identifier)))) + has_prefix( + TEMPLATE_NEWVAR, + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); - template_last_newvar = f; - template_newvars[template_last_newvar] = template_last_newvar; + template_last_newvar=f; + template_newvars[template_last_newvar]=template_last_newvar; return; } - f = to_function_application_expr(read_rhs(f, loc)); - assert(f.function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f.function()).get_identifier(); - //add equalities for arguments + f=to_function_application_expr(read_rhs(f, loc)); + assert(f.function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f.function()).get_identifier(); + // add equalities for arguments unsigned i=0; - for(exprt::operandst::iterator it = f.arguments().begin(); - it != f.arguments().end(); it++, i++) - { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i),it->type()); - n_it->equalities.push_back(equal_exprt(*it,arg)); - *it = arg; + for(exprt::operandst::iterator it=f.arguments().begin(); + it!=f.arguments().end(); ++it, ++i) + { + symbol_exprt arg( + id2string(fname)+"#"+i2string(loc->location_number)+ + "#arg"+i2string(i), it->type()); + const typet &argtype=ns.follow(it->type()); + if(argtype.id()==ID_struct) + { + exprt lhs=read_rhs(arg, loc); + for(size_t j=0; jequalities.push_back( + equal_exprt(lhs.operands()[j], it->operands()[j])); + } + } + else + { + n_it->equalities.push_back(equal_exprt(arg, *it)); + } + *it=arg; } n_it->function_calls.push_back( @@ -550,7 +671,7 @@ Function: local_SSAt::build_cond void local_SSAt::build_cond(locationt loc) { // anything to be built? - if(loc->is_goto() || loc->is_assume()) + if(loc->is_goto() || loc->is_assume()) { // produce a symbol for the renamed branching condition equal_exprt equality(cond_symbol(loc), read_rhs(loc->guard, loc)); @@ -578,25 +699,26 @@ Function: local_SSAt::build_guard void local_SSAt::build_guard(locationt loc) { const guard_mapt::entryt &entry=guard_map[loc]; - + // anything to be built? - if(!entry.has_guard) return; - + if(!entry.has_guard) + return; + exprt::operandst sources; // the very first 'loc' trivially gets 'true' as source if(loc==goto_function.body.instructions.begin()) sources.push_back(true_exprt()); - + for(guard_mapt::incomingt::const_iterator - i_it=entry.incoming.begin(); + i_it=entry.incoming.begin(); i_it!=entry.incoming.end(); i_it++) { const guard_mapt::edget &edge=*i_it; - + exprt source; - + // might be backwards branch taken edge if(edge.is_branch_taken() && edge.from->is_backwards_goto()) @@ -613,11 +735,11 @@ void local_SSAt::build_guard(locationt loc) else { // the other cases are basically similar - + symbol_exprt gs=name(guard_symbol(), OUT, edge.guard_source); exprt cond; - + if(edge.is_branch_taken() || edge.is_assume() || edge.is_function_call()) @@ -631,13 +753,13 @@ void local_SSAt::build_guard(locationt loc) source=and_exprt(gs, cond); } - + sources.push_back(source); } - + // the below produces 'false' if there is no source exprt rhs=disjunction(sources); - + equal_exprt equality(guard_symbol(loc), rhs); (--nodes.end())->equalities.push_back(equality); } @@ -659,13 +781,34 @@ void local_SSAt::build_assertions(locationt loc) if(loc->is_assert()) { exprt c=read_rhs(loc->guard, loc); - exprt g=guard_symbol(loc); + exprt g=guard_symbol(loc); (--nodes.end())->assertions.push_back(implies_exprt(g, c)); } } /*******************************************************************\ +Function: local_SSAt::build_assumptions + + Inputs: + + Outputs: + + Purpose: collect assumptions (required for backwards analysis) + +\*******************************************************************/ + +void local_SSAt::build_assumptions(locationt loc) +{ + if(loc->is_assume()) + { + exprt c=read_rhs(loc->guard, loc); + (--nodes.end())->assumptions.push_back(c); + } +} + +/*******************************************************************\ + Function: local_SSAt::assertions_to_constraints Inputs: @@ -679,13 +822,13 @@ Function: local_SSAt::assertions_to_constraints void local_SSAt::assertions_to_constraints() { for(nodest::iterator - n_it=nodes.begin(); + n_it=nodes.begin(); n_it!=nodes.end(); n_it++) { n_it->constraints.insert(n_it->constraints.end(), - n_it->assertions.begin(),n_it->assertions.end()); - } + n_it->assertions.begin(), n_it->assertions.end()); + } } /*******************************************************************\ @@ -777,18 +920,16 @@ exprt local_SSAt::read_lhs( #ifdef DEBUG std::cout << "read_lhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; #endif - + ssa_objectt object(tmp1, ns); // is this an object we track? if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { - #ifdef DEBUG std::cout << from_expr(ns, "", tmp1) << "is_object" << '\n'; #endif - // yes, it is if(assignments.assigns(loc, object)) return name(object, OUT, loc); @@ -832,23 +973,20 @@ exprt local_SSAt::read_node_in( ssa_domaint::phi_nodest::const_iterator p_it= phi_nodes.find(identifier); - + bool has_phi=false; - + if(p_it!=phi_nodes.end()) { const ssa_domaint::loc_def_mapt &incoming=p_it->second; - for(ssa_domaint::loc_def_mapt::const_iterator - incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + for(const auto &incoming_it : incoming) { - if(incoming_it->first > loc->location_number) + if(incoming_it.first>loc->location_number) has_phi=true; } } - + if(has_phi) return name(object, LOOP_BACK, loc); else @@ -868,11 +1006,12 @@ Function: local_SSAt::get_def_loc \*******************************************************************/ local_SSAt::locationt local_SSAt::get_def_loc( - const symbol_exprt &expr, + const symbol_exprt &expr, locationt loc) const { - ssa_objectt object(expr,ns); - if(!object) assert(false); + ssa_objectt object(expr, ns); + if(!object) + assert(false); if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { @@ -882,14 +1021,13 @@ local_SSAt::locationt local_SSAt::get_def_loc( ssa_domaint::def_mapt::const_iterator d_it= ssa_domain.def_map.find(identifier); - if(d_it==ssa_domain.def_map.end()) //input + if(d_it==ssa_domain.def_map.end()) // input return goto_function.body.instructions.begin(); else - return d_it->second.def.loc; //last definition + return d_it->second.def.loc; // last definition } - else //input + else // input return goto_function.body.instructions.begin(); - } /*******************************************************************\ @@ -908,26 +1046,26 @@ exprt local_SSAt::read_rhs(const exprt &expr, locationt loc) const { exprt tmp1=expr; adjust_float_expressions(tmp1, ns); - + unsigned counter=0; replace_side_effects_rec(tmp1, loc, counter); - #ifdef DEBUG +#ifdef DEBUG std::cout << "read_rhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; - #endif - +#endif + exprt tmp2=dereference(tmp1, loc); - #ifdef DEBUG +#ifdef DEBUG std::cout << "read_rhs tmp2: " << from_expr(ns, "", tmp2) << '\n'; - #endif - +#endif + exprt result=read_rhs_rec(tmp2, loc); - - #ifdef DEBUG + +#ifdef DEBUG std::cout << "read_rhs result: " << from_expr(ns, "", result) << '\n'; - #endif - +#endif + return result; } @@ -997,7 +1135,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { // ignore - //throw "unexpected side effect in read_rhs_rec"; + // throw "unexpected side effect in read_rhs_rec"; } else if(expr.id()==ID_address_of) { @@ -1016,11 +1154,11 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const read_rhs(index_expr.index(), loc), expr.type()); } - + ssa_objectt object(expr, ns); - + // is it an object identifier? - + if(!object) { exprt tmp=expr; // copy @@ -1028,7 +1166,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const *it=read_rhs(*it, loc); return tmp; } - + // Argument is a struct-typed ssa object? // May need to split up into members. const typet &type=ns.follow(expr.type()); @@ -1037,21 +1175,21 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { // build struct constructor struct_exprt result(expr.type()); - + const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + result.operands().resize(components.size()); - + for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { result.operands()[it-components.begin()]= read_rhs(member_exprt(expr, it->get_name(), it->type()), loc); } - + return result; } @@ -1095,21 +1233,21 @@ void local_SSAt::replace_side_effects_rec( { // turn into nondet_symbol counter++; - exprt s = nondet_symbol("ssa::nondet",expr.type(),loc,counter); + exprt s=nondet_symbol("ssa::nondet", expr.type(), loc, counter); expr.swap(s); } else if(statement==ID_malloc) { assert(false); /* counter++; - std::string tmp_suffix= + std::string tmp_suffix= i2string(loc->location_number)+ "."+i2string(counter)+suffix; - expr=malloc_ssa(side_effect_expr, tmp_suffix, ns);*/ + expr=malloc_ssa(side_effect_expr, tmp_suffix, ns);*/ } else { - //throw "unexpected side effect: "+id2string(statement); + // throw "unexpected side effect: "+id2string(statement); // ignore } } @@ -1135,24 +1273,24 @@ symbol_exprt local_SSAt::name( symbol_exprt new_symbol_expr(object.get_expr().type()); const irep_idt &id=object.get_identifier(); unsigned cnt=loc->location_number; - + irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi": - kind==LOOP_BACK?"lb": - kind==LOOP_SELECT?"ls": - "")+ - i2string(cnt)+ - (kind==LOOP_SELECT?std::string(""):suffix); + (kind==PHI?"phi": + kind==LOOP_BACK?"lb": + kind==LOOP_SELECT?"ls": + "")+ + i2string(cnt)+ + (kind==LOOP_SELECT?std::string(""):suffix); #ifdef DEBUG std::cout << "name " << kind << ": " << new_id << '\n'; #endif new_symbol_expr.set_identifier(new_id); - + if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); - + return new_symbol_expr; } @@ -1196,7 +1334,7 @@ symbol_exprt local_SSAt::name_input(const ssa_objectt &object) const { symbol_exprt new_symbol_expr(object.get_expr().type()); // copy const irep_idt old_id=object.get_identifier(); - irep_idt new_id=id2string(old_id)+suffix; //+"#in" + irep_idt new_id=id2string(old_id)+suffix; // +"#in" new_symbol_expr.set_identifier(new_id); if(object.get_expr().source_location().is_not_nil()) @@ -1217,8 +1355,11 @@ Function: local_SSAt::nondet_symbol \*******************************************************************/ -exprt local_SSAt::nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const +exprt local_SSAt::nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const { exprt s(ID_nondet_symbol, type); const irep_idt identifier= @@ -1257,9 +1398,9 @@ void local_SSAt::assign_rec( const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { @@ -1272,7 +1413,7 @@ void local_SSAt::assign_rec( } ssa_objectt lhs_object(lhs, ns); - + const std::set &assigned= assignments.get(loc); @@ -1281,7 +1422,7 @@ void local_SSAt::assign_rec( exprt ssa_rhs=read_rhs(rhs, loc); const symbol_exprt ssa_symbol=name(lhs_object, OUT, loc); - + equal_exprt equality(ssa_symbol, ssa_rhs); (--nodes.end())->equalities.push_back(equality); } @@ -1302,7 +1443,8 @@ void local_SSAt::assign_rec( if(compound_type.id()==ID_union) { - union_exprt new_rhs(member_expr.get_component_name(), rhs, compound.type()); + union_exprt new_rhs( + member_expr.get_component_name(), rhs, compound.type()); assign_rec(member_expr.struct_op(), new_rhs, guard, loc); } else if(compound_type.id()==ID_struct) @@ -1335,7 +1477,11 @@ void local_SSAt::assign_rec( { const if_exprt &if_expr=to_if_expr(lhs); assign_rec(if_expr.true_case(), rhs, and_exprt(guard, if_expr.cond()), loc); - assign_rec(if_expr.false_case(), rhs, and_exprt(guard, not_exprt(if_expr.cond())), loc); + assign_rec( + if_expr.false_case(), + rhs, + and_exprt(guard, not_exprt(if_expr.cond())), + loc); } else if(lhs.id()==ID_byte_extract_little_endian || lhs.id()==ID_byte_extract_big_endian) @@ -1350,7 +1496,7 @@ void local_SSAt::assign_rec( assign_rec(new_lhs, new_rhs, guard, loc); } else - throw "UNKNOWN LHS: "+lhs.id_string(); + throw "UNKNOWN LHS: "+lhs.id_string(); // NOLINT(readability/throw) } /*******************************************************************\ @@ -1367,11 +1513,12 @@ Function: local_SSAt::output void local_SSAt::output(std::ostream &out) const { - for(nodest::const_iterator - n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + for(nodest::const_iterator + n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) { - if(n_it->empty()) continue; + if(n_it->empty()) + continue; n_it->output(out, ns); out << '\n'; } @@ -1391,20 +1538,21 @@ Function: local_SSAt::output_verbose void local_SSAt::output_verbose(std::ostream &out) const { - for(nodest::const_iterator - n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + for(nodest::const_iterator + n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) { - if(n_it->empty()) continue; + if(n_it->empty()) + continue; out << "*** " << n_it->location->location_number << " " << n_it->location->source_location << "\n"; n_it->output(out, ns); - if(n_it->loophead!=nodes.end()) + if(n_it->loophead!=nodes.end()) out << "loop back to location " - << n_it->loophead->location->location_number << "\n"; - if(!n_it->enabling_expr.is_true()) + << n_it->loophead->location->location_number << "\n"; + if(!n_it->enabling_expr.is_true()) out << "enabled if " - << from_expr(ns, "", n_it->enabling_expr) << "\n"; + << from_expr(ns, "", n_it->enabling_expr) << "\n"; out << "\n"; } out << "(enable) " << from_expr(ns, "", get_enabling_exprs()) << "\n\n"; @@ -1412,7 +1560,7 @@ void local_SSAt::output_verbose(std::ostream &out) const /*******************************************************************\ -Function: local_SSAt::output +Function: local_SSAt::nodet::output Inputs: @@ -1426,30 +1574,32 @@ void local_SSAt::nodet::output( std::ostream &out, const namespacet &ns) const { + if(!enabling_expr.is_true()) + out << "(enable) " << from_expr(ns, "", enabling_expr) << "\n"; #if 0 - if(!marked) + if(!marked) out << "(not marked)" << "\n"; #endif for(equalitiest::const_iterator - e_it=equalities.begin(); + e_it=equalities.begin(); e_it!=equalities.end(); e_it++) out << "(E) " << from_expr(ns, "", *e_it) << "\n"; for(constraintst::const_iterator - e_it=constraints.begin(); + e_it=constraints.begin(); e_it!=constraints.end(); e_it++) out << "(C) " << from_expr(ns, "", *e_it) << "\n"; for(assertionst::const_iterator - a_it=assertions.begin(); + a_it=assertions.begin(); a_it!=assertions.end(); a_it++) out << "(A) " << from_expr(ns, "", *a_it) << "\n"; for(function_callst::const_iterator - f_it=function_calls.begin(); + f_it=function_calls.begin(); f_it!=function_calls.end(); f_it++) out << "(F) " << from_expr(ns, "", *f_it) << "\n"; @@ -1495,7 +1645,8 @@ bool local_SSAt::has_static_lifetime(const exprt &src) const else if(src.id()==ID_symbol) { const symbolt *s; - if(ns.lookup(to_symbol_expr(src).get_identifier(),s)) return false; + if(ns.lookup(to_symbol_expr(src).get_identifier(), s)) + return false; return s->is_static_lifetime; } else @@ -1514,36 +1665,37 @@ Function: local_SSAt::operator << \*******************************************************************/ -std::list & operator << ( - std::list &dest, +std::vector &operator<<( + std::vector &dest, const local_SSAt &src) { -#ifdef SLICING - ssa_slicert ssa_slicer; - ssa_slicer(dest,src); -#else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - dest.push_back(*e_it); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *e_it)); + else + dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { - dest.push_back(*c_it); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *c_it)); + else + dest.push_back(*c_it); } } -#endif - return dest; } @@ -1559,44 +1711,103 @@ Function: local_SSAt::operator << \*******************************************************************/ -decision_proceduret & operator << ( +std::list &operator<<( + std::list &dest, + const local_SSAt &src) +{ + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) + { + if(n_it->marked) + continue; + for(local_SSAt::nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + { + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *e_it)); + else + dest.push_back(*e_it); + } + + for(local_SSAt::nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + { + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *c_it)); + else + dest.push_back(*c_it); + } + } + return dest; +} + +/*******************************************************************\ + +Function: local_SSAt::operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret &operator<<( decision_proceduret &dest, const local_SSAt &src) { -#ifdef SLICING - std::list tmp; - tmp << src; - for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) - dest << *it; -#else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - dest << *e_it; + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *e_it); + else + dest << *e_it; + +#if 0 + // freeze cond variables + if(e_it->op0().id()==ID_symbol && + e_it->op0().type().id()==ID_bool) + { + const symbol_exprt &symbol=to_symbol_expr(e_it->op0()); + if(id2string(symbol.get_identifier()).find("ssa::$cond")!= + std::string::npos) + { + dest.solver->set_frozen(dest.solver->convert(symbol)); + } + } +#endif } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { - dest << *c_it; + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *c_it); + else + dest << *c_it; } } -#endif return dest; } /*******************************************************************\ -Function: local_SSAt::operator << +Function: local_SSAt::operator<< Inputs: @@ -1606,50 +1817,57 @@ Function: local_SSAt::operator << \*******************************************************************/ -incremental_solvert & operator << ( +incremental_solvert &operator<<( incremental_solvert &dest, const local_SSAt &src) { -#ifdef SLICING - std::list tmp; - tmp << src; - for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) - dest << *it; -#else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *e_it); else dest << *e_it; + +#if 0 + // freeze cond variables + if(e_it->op0().id()==ID_symbol && + e_it->op0().type().id()==ID_bool) + { + const symbol_exprt &symbol=to_symbol_expr(e_it->op0()); + if(id2string(symbol.get_identifier()).find("ssa::$cond")!= + std::string::npos) + { + dest.solver->set_frozen(dest.solver->convert(symbol)); + } + } +#endif } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *c_it); else dest << *c_it; } } -#endif return dest; } /*******************************************************************\ -Function: local_SSAt::get_enabling_expr +Function: local_SSAt::get_enabling_exprs Inputs: @@ -1663,12 +1881,14 @@ exprt local_SSAt::get_enabling_exprs() const { exprt::operandst result; result.reserve(enabling_exprs.size()); - for(std::list::const_iterator it = enabling_exprs.begin(); - it != enabling_exprs.end(); ++it) + for(std::vector::const_iterator it=enabling_exprs.begin(); + it!=enabling_exprs.end(); ++it) { - std::list::const_iterator lh = it; lh++; - if(lh != enabling_exprs.end()) result.push_back(not_exprt(*it)); - else result.push_back(*it); + std::vector::const_iterator lh=it; ++lh; + if(lh!=enabling_exprs.end()) + result.push_back(not_exprt(*it)); + else + result.push_back(*it); } return conjunction(result); } @@ -1687,16 +1907,15 @@ Function: local_SSAt::has_function_calls bool local_SSAt::has_function_calls() const { - bool found = false; - for(local_SSAt::nodest::const_iterator n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + bool found=false; + for(local_SSAt::nodest::const_iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) { - if(!n_it->function_calls.empty()) + if(!n_it->function_calls.empty()) { - found = true; + found=true; break; } } return found; } - diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index da0bbda12..7e70a5e97 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -6,14 +6,15 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_LOCAL_SSA_H -#define CPROVER_LOCAL_SSA_H +#ifndef CPROVER_2LS_SSA_LOCAL_SSA_H +#define CPROVER_2LS_SSA_LOCAL_SSA_H #include #include -#include "../domains/incremental_solver.h" +#include + #include "ssa_domain.h" #include "guard_map.h" #include "ssa_object.h" @@ -33,21 +34,21 @@ class local_SSAt const goto_functiont &_goto_function, const namespacet &_ns, const std::string &_suffix=""): - ns(_ns), goto_function(_goto_function), + ns(_ns), goto_function(_goto_function), ssa_objects(_goto_function, ns), ssa_value_ai(_goto_function, ns), assignments(_goto_function.body, ns, ssa_objects, ssa_value_ai), guard_map(_goto_function.body), ssa_analysis(assignments), - suffix(_suffix) + suffix(_suffix) { - //ENHANCE: in future locst will be used (currently in path-symex/locs.h) - forall_goto_program_instructions(it,_goto_function.body) - location_map[it->location_number] = it; + // ENHANCE: in future locst will be used (currently in path-symex/locs.h) + forall_goto_program_instructions(it, _goto_function.body) + location_map[it->location_number]=it; build_SSA(); } - + void output(std::ostream &) const; void output_verbose(std::ostream &) const; @@ -57,13 +58,13 @@ class local_SSAt public: inline nodet( locationt _location, - std::list::iterator _loophead) - : + std::list::iterator _loophead): + function_calls_inlined(false), enabling_expr(true_exprt()), - marked(false), - location(_location), + marked(false), + location(_location), loophead(_loophead) - { + { } typedef std::vector equalitiest; @@ -74,63 +75,68 @@ class local_SSAt typedef std::vector assertionst; assertionst assertions; - + + typedef std::vector assumptionst; + assertionst assumptions; + typedef std::vector function_callst; function_callst function_calls; + bool function_calls_inlined; - exprt enabling_expr; //for incremental unwinding - bool marked; //for incremental unwinding + exprt enabling_expr; // for incremental unwinding + bool marked; // for incremental unwinding - //custom invariant templates + // custom invariant templates typedef std::vector templatest; templatest templates; - locationt location; //link to goto instruction - std::list::iterator loophead; //link to loop head node - // otherwise points to nodes.end() + locationt location; // link to goto instruction + std::list::iterator loophead; // link to loop head node + // otherwise points to nodes.end() void output(std::ostream &, const namespacet &) const; inline bool empty() const { - return equalities.empty() && constraints.empty() && - assertions.empty() && function_calls.empty(); + return equalities.empty() && constraints.empty() && + assertions.empty() && function_calls.empty(); } }; - + // turns the assertions in the function into constraints void assertions_to_constraints(); - // all the SSA nodes + // all the SSA nodes typedef std::list nodest; nodest nodes; void mark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = true; + for(auto &n : nodes) + n.marked=true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = false; + for(auto &n : nodes) + n.marked=false; } // for incremental unwinding - std::list enabling_exprs; + std::vector enabling_exprs; exprt get_enabling_exprs() const; // function entry and exit variables typedef std::list var_listt; typedef std::set var_sett; - var_listt params; - var_sett globals_in, globals_out; + var_listt params; + var_sett globals_in, globals_out; + std::set nondets; bool has_function_calls() const; const namespacet &ns; const goto_functiont &goto_function; - + // guards ssa_objectt cond_symbol() const; symbol_exprt cond_symbol(locationt loc) const @@ -139,14 +145,18 @@ class local_SSAt symbol_exprt guard_symbol(locationt loc) const { return name(guard_symbol(), OUT, guard_map[loc].guard_source); } exprt edge_guard(locationt from, locationt to) const; - + // auxiliary functions enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT }; - virtual symbol_exprt name(const ssa_objectt &, kindt kind, locationt loc) const; + virtual symbol_exprt name( + const ssa_objectt &, kindt kind, locationt loc) const; symbol_exprt name(const ssa_objectt &, const ssa_domaint::deft &) const; symbol_exprt name_input(const ssa_objectt &) const; - virtual exprt nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const; + virtual exprt nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const; locationt get_def_loc(const symbol_exprt &, locationt loc) const; void replace_side_effects_rec(exprt &, locationt, unsigned &) const; exprt read_lhs(const exprt &, locationt loc) const; @@ -155,34 +165,38 @@ class local_SSAt exprt read_rhs_rec(const exprt &, locationt loc) const; symbol_exprt read_rhs(const ssa_objectt &, locationt loc) const; exprt read_node_in(const ssa_objectt &, locationt loc) const; - void assign_rec(const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); + void assign_rec( + const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); void get_entry_exit_vars(); - + bool has_static_lifetime(const ssa_objectt &) const; bool has_static_lifetime(const exprt &) const; - + exprt dereference(const exprt &expr, locationt loc) const; ssa_objectst ssa_objects; typedef ssa_objectst::objectst objectst; ssa_value_ait ssa_value_ai; assignmentst assignments; - -//protected: + +// protected: guard_mapt guard_map; ssa_ait ssa_analysis; std::string suffix; // an extra suffix - void get_globals(locationt loc, std::set &globals, - bool rhs_value=true, - bool with_returns=true, - const irep_idt &returns_for_function="") const; + void get_globals( + locationt loc, + std::set &globals, + bool rhs_value=true, + bool with_returns=true, + const irep_idt &returns_for_function="") const; nodest::iterator find_node(locationt loc); nodest::const_iterator find_node(locationt loc) const; - void find_nodes(locationt loc, std::list &_nodes) const; + void find_nodes( + locationt loc, std::list &_nodes) const; inline locationt get_location(unsigned location_number) const { @@ -190,6 +204,7 @@ class local_SSAt assert(it!=location_map.end()); return it->second; } + locationt find_location_by_number(unsigned location_number) const; protected: typedef std::map location_mapt; @@ -204,13 +219,20 @@ class local_SSAt void build_guard(locationt loc); void build_function_call(locationt loc); void build_assertions(locationt loc); + void build_assumptions(locationt loc); // custom templates void collect_custom_templates(); replace_mapt template_newvars; exprt template_last_newvar; + + void get_nondet_vars(const exprt &expr); + void get_nondet_vars(); }; +std::vector & operator << + (std::vector &dest, const local_SSAt &src); + std::list & operator << (std::list &dest, const local_SSAt &src); diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index 13adc92c2..fd5246b4e 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -20,7 +20,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: +Function: c_sizeof_type_rec Inputs: @@ -43,10 +43,11 @@ inline static typet c_sizeof_type_rec(const exprt &expr) forall_operands(it, expr) { typet t=c_sizeof_type_rec(*it); - if(t.is_not_nil()) return t; + if(t.is_not_nil()) + return t; } } - + return nil_typet(); } @@ -73,7 +74,7 @@ exprt malloc_ssa( namespacet ns(symbol_table); exprt size=code.op0(); typet object_type=nil_typet(); - + { // special treatment for sizeof(T)*x if(size.id()==ID_mult && @@ -82,20 +83,20 @@ exprt malloc_ssa( { object_type=array_typet( c_sizeof_type_rec(size.op0()), - size.op1()); + size.op1()); } else if(size.id()==ID_mult && - size.operands().size()==2 && - size.op1().find(ID_C_c_sizeof_type).is_not_nil()) + size.operands().size()==2 && + size.op1().find(ID_C_c_sizeof_type).is_not_nil()) { object_type=array_typet( c_sizeof_type_rec(size.op1()), - size.op0()); + size.op0()); } else { typet tmp_type=c_sizeof_type_rec(size); - + if(tmp_type.is_not_nil()) { // Did the size get multiplied? @@ -111,15 +112,17 @@ exprt malloc_ssa( else { mp_integer elements=alloc_size/elem_size; - + if(elements*elem_size==alloc_size) - object_type=array_typet(tmp_type, from_integer(elements, size.type())); + object_type=array_typet( + tmp_type, + from_integer(elements, size.type())); } } } } - // the fall-back is to produce a byte-array + // the fall-back is to produce a byte-array if(object_type.is_nil()) object_type=array_typet(unsigned_char_type(), size); } @@ -127,10 +130,10 @@ exprt malloc_ssa( #ifdef DEBUG std::cout << "OBJECT_TYPE: " << from_type(ns, "", object_type) << std::endl; #endif - + // value symbolt value_symbol; - + value_symbol.base_name="dynamic_object"+suffix; value_symbol.name="ssa::"+id2string(value_symbol.base_name); value_symbol.is_lvalue=true; @@ -140,7 +143,7 @@ exprt malloc_ssa( symbol_table.add(value_symbol); address_of_exprt address_of; - + if(object_type.id()==ID_array) { address_of.type()=pointer_typet(value_symbol.type.subtype()); @@ -154,9 +157,9 @@ exprt malloc_ssa( address_of.op0()=value_symbol.symbol_expr(); address_of.type()=pointer_typet(value_symbol.type); } - + exprt result=address_of; - + if(result.type()!=code.type()) result=typecast_exprt(result, code.type()); @@ -164,49 +167,78 @@ exprt malloc_ssa( } -static void replace_malloc_rec(exprt &expr, - const std::string &suffix, - symbol_tablet &symbol_table, - const exprt &malloc_size, - unsigned &counter) +/*******************************************************************\ + +Function: replace_malloc_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void replace_malloc_rec( + exprt &expr, + const std::string &suffix, + symbol_tablet &symbol_table, + const exprt &malloc_size, + unsigned &counter) { if(expr.id()==ID_side_effect && to_side_effect_expr(expr).get_statement()==ID_malloc) { assert(!malloc_size.is_nil()); - expr.op0() = malloc_size; - - expr = malloc_ssa(to_side_effect_expr(expr),"$"+i2string(counter++)+suffix,symbol_table); + expr.op0()=malloc_size; + + expr=malloc_ssa(to_side_effect_expr(expr), + "$"+i2string(counter++)+suffix, symbol_table); } else - Forall_operands(it,expr) - replace_malloc_rec(*it,suffix,symbol_table,malloc_size,counter); + Forall_operands(it, expr) + replace_malloc_rec(*it, suffix, symbol_table, malloc_size, counter); } -void replace_malloc(goto_modelt &goto_model, - const std::string &suffix) +/*******************************************************************\ + +Function: replace_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void replace_malloc( + goto_modelt &goto_model, + const std::string &suffix) { - unsigned counter = 0; + unsigned counter=0; Forall_goto_functions(f_it, goto_model.goto_functions) { - exprt malloc_size = nil_exprt(); + exprt malloc_size=nil_exprt(); Forall_goto_program_instructions(i_it, f_it->second.body) { if(i_it->is_assign()) { - code_assignt &code_assign = to_code_assign(i_it->code); - if(code_assign.lhs().id()==ID_symbol) - { - // we have to propagate the malloc size + code_assignt &code_assign=to_code_assign(i_it->code); + if(code_assign.lhs().id()==ID_symbol) + { + // we have to propagate the malloc size // in order to get the object type - // TODO: this only works with inlining - const irep_idt &lhs_id = - to_symbol_expr(code_assign.lhs()).get_identifier(); - if(lhs_id == "malloc::malloc_size") - malloc_size = code_assign.rhs(); - } - replace_malloc_rec(code_assign.rhs(),suffix, - goto_model.symbol_table,malloc_size,counter); + // TODO: this only works with inlining, + // and btw, this is an ugly hack + std::string lhs_id= + id2string(to_symbol_expr(code_assign.lhs()).get_identifier()); + if(lhs_id=="malloc::malloc_size" || + lhs_id=="__builtin_alloca::alloca_size") + malloc_size=code_assign.rhs(); + } + replace_malloc_rec(code_assign.rhs(), suffix, + goto_model.symbol_table, malloc_size, counter); } } } diff --git a/src/ssa/malloc_ssa.h b/src/ssa/malloc_ssa.h index d9ef8d6b1..5ef4e5ed4 100644 --- a/src/ssa/malloc_ssa.h +++ b/src/ssa/malloc_ssa.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_MALLOC_SSA_H -#define CPROVER_MALLOC_SSA_H +#ifndef CPROVER_2LS_SSA_MALLOC_SSA_H +#define CPROVER_2LS_SSA_MALLOC_SSA_H #include #include @@ -17,10 +17,8 @@ exprt malloc_ssa( const std::string &suffix, symbol_tablet &); - -#if 1 -void replace_malloc(goto_modelt &goto_model, - const std::string &suffix); -#endif +void replace_malloc( + goto_modelt &goto_model, + const std::string &suffix); #endif diff --git a/src/ssa/simplify_ssa.cpp b/src/ssa/simplify_ssa.cpp index b6a5561c3..d3ab25979 100644 --- a/src/ssa/simplify_ssa.cpp +++ b/src/ssa/simplify_ssa.cpp @@ -12,7 +12,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: simplify_ssa +Function: simplify Inputs: @@ -30,7 +30,7 @@ void simplify(local_SSAt &ssa, const namespacet &ns) n_it++) { local_SSAt::nodet &node=*n_it; - + for(local_SSAt::nodet::equalitiest::iterator e_it=node.equalities.begin(); e_it!=node.equalities.end(); @@ -47,7 +47,7 @@ void simplify(local_SSAt &ssa, const namespacet &ns) { *c_it=simplify_expr(*c_it, ns); } - + for(local_SSAt::nodet::assertionst::iterator a_it=node.assertions.begin(); a_it!=node.assertions.end(); diff --git a/src/ssa/simplify_ssa.h b/src/ssa/simplify_ssa.h index 75693c8c1..1d4efe1bd 100644 --- a/src/ssa/simplify_ssa.h +++ b/src/ssa/simplify_ssa.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SIMPLIFY_SSA_H -#define CPROVER_SIMPLIFY_SSA_H +#ifndef CPROVER_2LS_SSA_SIMPLIFY_SSA_H +#define CPROVER_2LS_SSA_SIMPLIFY_SSA_H #include diff --git a/src/ssa/split_loopheads.cpp b/src/ssa/split_loopheads.cpp deleted file mode 100644 index 73c179d5f..000000000 --- a/src/ssa/split_loopheads.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include -#include - -#include "split_loopheads.h" - -/*******************************************************************\ - -Function: split_loopheads - -Inputs: - -Outputs: - -Purpose: insert skip at jump targets if they are goto, - assume or assert instructions - -\*******************************************************************/ - -void split_loopheads(goto_modelt &goto_model) -{ - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(!i_it->is_backwards_goto()) continue; - goto_programt::targett loophead = i_it->get_target(); - if(i_it->guard.is_true() && !loophead->is_assert()) continue; - - // inserts the skip - goto_programt::targett new_loophead = - f_it->second.body.insert_before(loophead); - new_loophead->make_skip(); - new_loophead->source_location=loophead->source_location; - new_loophead->function=i_it->function; - - // update jumps to loophead - for(std::set::iterator j_it = loophead->incoming_edges.begin(); - j_it != loophead->incoming_edges.end(); j_it++) - { - if(!(*j_it)->is_goto() || (*j_it)->get_target()!=loophead) continue; - (*j_it)->targets.clear(); - (*j_it)->targets.push_back(new_loophead); - } - } - } -} diff --git a/src/ssa/split_loopheads.h b/src/ssa/split_loopheads.h deleted file mode 100644 index c3a220c20..000000000 --- a/src/ssa/split_loopheads.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CPROVER_SPLIT_LOOPHEADS_H -#define CPROVER_SPLIT_LOOPHEADS_H - -#include - -void split_loopheads(goto_modelt &goto_model); - -#endif diff --git a/src/ssa/ssa_build_goto_trace.cpp b/src/ssa/ssa_build_goto_trace.cpp index c24d24188..22891beff 100644 --- a/src/ssa/ssa_build_goto_trace.cpp +++ b/src/ssa/ssa_build_goto_trace.cpp @@ -8,9 +8,14 @@ Author: Daniel Kroening, Peter Schrammel #include #include +#include +#include +#include #include "ssa_build_goto_trace.h" +#define TERM_CEX 1 + /*******************************************************************\ Function: ssa_build_goto_tracet::finalize_lhs @@ -31,20 +36,20 @@ exprt ssa_build_goto_tracet::finalize_lhs(const exprt &src) { index_exprt tmp=to_index_expr(src); tmp.array()=finalize_lhs(tmp.array()); - exprt index = unwindable_local_SSA.read_rhs(tmp.index(),current_pc); + exprt index=unwindable_local_SSA.read_rhs(tmp.index(), current_pc); tmp.index()=simplify_expr(prop_conv.get(index), unwindable_local_SSA.ns); return tmp; } else if(src.id()==ID_dereference) { address_of_exprt tmp1(src); - exprt tmp2 = unwindable_local_SSA.read_rhs(tmp1,current_pc); + exprt tmp2=unwindable_local_SSA.read_rhs(tmp1, current_pc); exprt tmp3=prop_conv.get(tmp2); exprt tmp4=tmp3; if(tmp4.id()==ID_constant && tmp4.type().id()==ID_pointer && tmp4.operands().size()==1 && tmp4.op0().id()==ID_address_of) tmp4=to_address_of_expr(tmp4.op0()).object(); - //TODO: investigate: produces nil sometimes + // TODO: investigate: produces nil sometimes return tmp4; } else if(src.id()==ID_member) @@ -59,6 +64,44 @@ exprt ssa_build_goto_tracet::finalize_lhs(const exprt &src) /*******************************************************************\ +Function: ssa_build_goto_tracet::can_convert_ssa_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_build_goto_tracet::can_convert_ssa_expr(const exprt &expr) +{ + if(expr.id()==ID_member) + { + const member_exprt &member=to_member_expr(expr); + can_convert_ssa_expr(member.struct_op()); + return true; + } + else if(expr.id()==ID_index) + { + const index_exprt &index=to_index_expr(expr); + can_convert_ssa_expr(index.array()); + + mp_integer idx; + if(to_integer(to_constant_expr(index.index()), idx)) + return false; + return true; + } + else if(expr.id()==ID_symbol) + { + return true; + } + + return false; +} + +/*******************************************************************\ + Function: ssa_build_goto_tracet::record_step Inputs: @@ -73,7 +116,7 @@ bool ssa_build_goto_tracet::record_step( goto_tracet &goto_trace, unsigned &step_nr) { - bool taken = true; + bool taken=true; goto_trace_stept step; step.pc=current_pc; step.step_nr=step_nr; @@ -101,98 +144,142 @@ bool ssa_build_goto_tracet::record_step( goto_trace.add_step(step); step_nr++; break; - + case GOTO: - { - exprt cond=current_pc->guard; - exprt cond_read = unwindable_local_SSA.read_rhs(cond,current_pc); - exprt cond_value=simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); - step.type=goto_trace_stept::GOTO; - step.cond_expr = cond_value; //cond - step.cond_value = cond_value.is_true(); + { + exprt cond=current_pc->guard; + exprt cond_read=unwindable_local_SSA.read_rhs(cond, current_pc); + unwindable_local_SSA.rename(cond_read, current_pc); + exprt cond_value= + simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); + step.type=goto_trace_stept::GOTO; + step.cond_expr=cond_value; // cond #if 0 - std::cout << "COND " << from_expr(unwindable_local_SSA.ns, "", cond) - << ": " << from_expr(unwindable_local_SSA.ns, "", cond_read) - << " == " << cond_value.is_true() << std::endl; + assert(cond_value.is_true() || cond_value.is_false()); #endif - if(step.cond_value) - { - goto_trace.add_step(step); - step_nr++; - } - else - taken = false; + step.cond_value=cond_value.is_true(); +#if 0 + std::cout << "COND " << from_expr(unwindable_local_SSA.ns, "", cond) + << ": (" << from_expr(unwindable_local_SSA.ns, "", cond_read) + << ")==" << cond_value.is_true() << std::endl; +#endif + + taken=step.cond_value; + + if(taken) + { + goto_trace.add_step(step); + step_nr++; } - break; + } + break; case ASSERT: + { + // failed or not? + exprt cond=current_pc->guard; + exprt cond_read=unwindable_local_SSA.read_rhs(cond, current_pc); + unwindable_local_SSA.rename(cond_read, current_pc); + exprt cond_value= + simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); + if(cond_value.is_false()) { - // failed or not? - exprt cond=current_pc->guard; - exprt cond_read=unwindable_local_SSA.read_rhs(cond,current_pc); - exprt cond_value=simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); - if(cond_value.is_false()) - { - step.type=goto_trace_stept::ASSERT; - step.comment=id2string(current_pc->source_location.get_comment()); - step.cond_expr=cond; - step.cond_value=false; - goto_trace.add_step(step); - step_nr++; - } + step.type=goto_trace_stept::ASSERT; + step.comment=id2string(current_pc->source_location.get_comment()); + step.cond_expr=cond; + step.cond_value=false; + goto_trace.add_step(step); + step_nr++; } - break; + } + break; case ATOMIC_BEGIN: case ATOMIC_END: case DECL: case DEAD: break; // ignore - + case ASSIGN: - { - const code_assignt &code_assign= - to_code_assign(current_pc->code); - exprt rhs_ssa=unwindable_local_SSA.read_rhs(code_assign.rhs(),current_pc); - exprt rhs_value=prop_conv.get(rhs_ssa); - exprt rhs_simplified=simplify_expr(rhs_value, unwindable_local_SSA.ns); - exprt lhs_ssa=finalize_lhs(code_assign.lhs()); - exprt lhs_simplified=simplify_expr(lhs_ssa, unwindable_local_SSA.ns); - - step.type=goto_trace_stept::ASSIGNMENT; - step.full_lhs=lhs_simplified; - step.full_lhs_value=rhs_simplified; - if(lhs_simplified.id()==ID_symbol) - { - step.lhs_object = to_ssa_expr(lhs_simplified); - step.lhs_object_value=rhs_simplified; - //filter out internal stuff - if(id2string(step.lhs_object.get_identifier()).find("#") - != std::string::npos) - break; - //filter out undetermined values - if(step.lhs_object_value.id()!=ID_constant) - break; - } - if(step.lhs_object.is_nil()) - break; + { + if(has_prefix(id2string(current_pc->function), CPROVER_PREFIX)) + break; + + const code_assignt &code_assign= + to_code_assign(current_pc->code); + + exprt rhs_ssa=unwindable_local_SSA.read_rhs(code_assign.rhs(), current_pc); + unwindable_local_SSA.rename(rhs_ssa, current_pc); + exprt rhs_value=prop_conv.get(rhs_ssa); + exprt rhs_simplified=simplify_expr(rhs_value, unwindable_local_SSA.ns); + exprt lhs_ssa=finalize_lhs(code_assign.lhs()); + exprt lhs_simplified=simplify_expr(lhs_ssa, unwindable_local_SSA.ns); + #if 0 - std::cout << "ASSIGN " << from_expr(unwindable_local_SSA.ns, "", code_assign) - << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) - << " == " << from_expr(unwindable_local_SSA.ns, - "", step.full_lhs_value) << std::endl; + std::cout << "ASSIGN " + << from_expr(unwindable_local_SSA.ns, "", code_assign) + << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) + << "==" << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) + << std::endl; #endif - goto_trace.add_step(step); - step_nr++; + step.type=goto_trace_stept::ASSIGNMENT; + step.full_lhs=lhs_simplified; + step.full_lhs_value=rhs_simplified; + + // filter out internal stuff + if(lhs_simplified.id()==ID_symbol) + { + std::string identifier=id2string(lhs_simplified.get(ID_identifier)); + if(has_prefix(identifier, CPROVER_PREFIX)) + break; + if(identifier.find("#")!=std::string::npos) + break; + if(identifier.find("$")!=std::string::npos) + break; + if(identifier.find("'")!=std::string::npos) + break; } - break; + + if(!can_convert_ssa_expr(lhs_simplified)) + break; + + step.lhs_object=ssa_exprt(lhs_simplified); + step.lhs_object_value=rhs_simplified; + + // skip unresolved lhs + if(step.lhs_object.is_nil()) + break; + + // skip strings (for SV-COMP) + if(step.lhs_object.type().id()==ID_pointer && + to_pointer_type(step.lhs_object.type()).subtype().id()==ID_signedbv) + break; + + // skip undetermined rhs + find_symbols_sett rhs_symbols; + find_symbols(rhs_simplified, rhs_symbols); + if(!rhs_symbols.empty() || rhs_simplified.id()==ID_nondet_symbol) + break; + +#if 0 + std::cout << "ASSIGNMENT ADDED: " + << from_expr(unwindable_local_SSA.ns, "", code_assign) + << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) + << "==" + << from_expr(unwindable_local_SSA.ns, "", step.full_lhs_value) + << std::endl; +#endif + goto_trace.add_step(step); + step_nr++; + } + break; case OTHER: step.type=goto_trace_stept::LOCATION; goto_trace.add_step(step); step_nr++; break; - + case NO_INSTRUCTION_TYPE: assert(false); break; @@ -219,49 +306,77 @@ void ssa_build_goto_tracet::operator()( return; current_pc=unwindable_local_SSA.goto_function.body.instructions.begin(); - unwindable_local_SSA.current_unwindings.clear(); - unsigned last_level = 0; + unwindable_local_SSA.current_unwindings.clear(); + unsigned last_level=0; unsigned step_nr=1; - +#if TERM_CEX + bool stop_next=false; +#endif + while(current_pc!=unwindable_local_SSA.goto_function.body.instructions.end()) { -#if 0 - if(current_pc->is_assign()) - { - const code_assignt &code_assign = to_code_assign(current_pc->code); - if(code_assign.rhs().id()==ID_side_effect && - to_side_effect_expr(code_assign.rhs()).get_statement()==ID_nondet) - { - current_pc++; - continue; - } - } -#endif - unsigned current_level = + unsigned current_level= unwindable_local_SSA.loop_hierarchy_level[current_pc].level; - int level_diff = current_level - last_level; - last_level = current_level; - if(level_diff!=0) - unwindable_local_SSA.decrement_unwindings(level_diff); + long level_diff=(long)current_level-(long)last_level; + #if 0 std::cout << "location: " << current_pc->location_number << std::endl; + std::cout << "current_level: " << current_level << std::endl; + std::cout << "last_level: " << last_level << std::endl; std::cout << "level_diff: " << level_diff << std::endl; - std::cout << "unwindings: " - << unwindable_local_SSA.odometer_to_string(unwindable_local_SSA.current_unwindings,100) << std::endl; #endif - bool taken = record_step(goto_trace, step_nr); - + last_level=current_level; + // we enter a loop if >0 and exit a loop if <0 + if(level_diff!=0l) + { + unwindable_local_SSA.decrement_unwindings(level_diff); + +#if 0 + std::cout << "loop-head : " << current_pc->location_number << std::endl; + std::cout << "unwindings: " + << unwindable_local_SSA.odometer_to_string( + unwindable_local_SSA.current_unwindings, 100) + << std::endl; +#endif + } + + bool taken=record_step(goto_trace, step_nr); + if(!goto_trace.steps.empty() && goto_trace.steps.back().is_assert()) break; // done - + +#if 0 + std::cout << "is_goto: " << current_pc->is_goto() << std::endl; + std::cout << "is_backwards_goto: " << current_pc->is_backwards_goto() + << std::endl; + std::cout << "taken: " << taken << std::endl; +#endif + // get successor if(current_pc->is_goto() && taken) { +#if TERM_CEX + if(termination && stop_next) + { + break; + } +#endif if(current_pc->is_backwards_goto()) { - unwindable_local_SSA.decrement_unwindings(0); + // we de-(!)-crement the unwinding counter + unwindable_local_SSA.decrement_unwindings(0); +#if 0 + std::cout << "loop-end : " << current_pc->location_number << std::endl; + std::cout << "unwindings: " + << unwindable_local_SSA.odometer_to_string( + unwindable_local_SSA.current_unwindings, 100) + << std::endl; +#endif +#if TERM_CEX + stop_next=true; +#endif } current_pc=current_pc->get_target(); } diff --git a/src/ssa/ssa_build_goto_trace.h b/src/ssa/ssa_build_goto_trace.h index dbace61e2..435d0af35 100644 --- a/src/ssa/ssa_build_goto_trace.h +++ b/src/ssa/ssa_build_goto_trace.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SSA_BUILD_GOTO_TRACE_H -#define CPROVER_SSA_BUILD_GOTO_TRACE_H +#ifndef CPROVER_2LS_SSA_SSA_BUILD_GOTO_TRACE_H +#define CPROVER_2LS_SSA_SSA_BUILD_GOTO_TRACE_H #include #include @@ -15,14 +15,17 @@ Author: Daniel Kroening, Peter Schrammel #include "local_ssa.h" #include "unwindable_local_ssa.h" -class ssa_build_goto_tracet { +class ssa_build_goto_tracet +{ public: ssa_build_goto_tracet( unwindable_local_SSAt &_unwindable_local_SSA, - const prop_convt &_prop_conv) - : - unwindable_local_SSA(_unwindable_local_SSA), - prop_conv(_prop_conv) + const prop_convt &_prop_conv, + bool _termination=false) + : + unwindable_local_SSA(_unwindable_local_SSA), + prop_conv(_prop_conv), + termination(_termination) {} void operator()(goto_tracet &); @@ -31,8 +34,10 @@ class ssa_build_goto_tracet { unwindable_local_SSAt &unwindable_local_SSA; const prop_convt &prop_conv; goto_programt::const_targett current_pc; + bool termination; exprt finalize_lhs(const exprt &src); + bool can_convert_ssa_expr(const exprt &expr); bool record_step( goto_tracet &goto_trace, diff --git a/src/ssa/ssa_const_propagator.cpp b/src/ssa/ssa_const_propagator.cpp new file mode 100644 index 000000000..ab5c9fd68 --- /dev/null +++ b/src/ssa/ssa_const_propagator.cpp @@ -0,0 +1,307 @@ +/*******************************************************************\ + +Module: SSA Constant Propagator + +Author: Kumar Madhukar + +\*******************************************************************/ + +// #define DEBUG + +#ifdef DEBUG +#include +#include +#endif + +#include +#include +#include + +#include "ssa_const_propagator.h" + +/*******************************************************************\ + +Function: ssa_const_propagatort::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_const_propagatort::operator()( + std::list &dest, const local_SSAt &src) +{ + values.iterate=true; + while(values.iterate) + { + values.iterate=false; + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) + { + const local_SSAt::nodet &node=*n_it; + + // check if node is active; 'continue' if not active + if(!node.enabling_expr.is_true()) + continue; + + // if src.enabling_exprs does not have a true in the end, + // then also continue + if(src.enabling_exprs.size()>0) + if((src.enabling_exprs.back()).is_true()) + continue; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) + { + equal_exprt e=to_equal_expr(*e_it); + const exprt &lhs=e.lhs(); + exprt &temprhs=e.rhs(); + + // resolving conditional expressions + while(temprhs.id()==ID_if) + { + exprt simp_guard; +#ifdef DEBUG + std::cout << "guard: " << from_expr(src.ns, "", temprhs.op0()) + << std::endl; +#endif + simp_guard=simplify_expr(temprhs.op0(), src.ns); +#ifdef DEBUG + std::cout << "simplified: " << from_expr(src.ns, "", simp_guard) + << std::endl; +#endif + if(simp_guard.is_true()) + temprhs=temprhs.op1(); + else if(simp_guard.is_false()) + temprhs=temprhs.op2(); + else + break; + } + + const exprt &rhs=temprhs; + valuest copy_values=values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values, lhs, rhs, src.ns); + else + copy_values.set_to_top(lhs); + +#ifdef DEBUG + copy_values.output(std::cout, src.ns); +#endif + + if(rhs.id()==ID_symbol) + { + if(!values.maps_to_top(lhs)) + assign(values, rhs, lhs, src.ns); + else + values.set_to_top(rhs); + } + +#ifdef DEBUG + values.output(std::cout, src.ns); +#endif + + values.merge(copy_values); + +#ifdef DEBUG + values.output(std::cout, src.ns); +#endif + } + + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); + c_it!=n_it->constraints.end(); c_it++) + { + if(c_it->id()!=ID_equal) + continue; + + const equal_exprt e=to_equal_expr(*c_it); + const exprt &lhs=e.lhs(); + const exprt &rhs=e.rhs(); + + // if lhs is a variable and rhs is a constant expression + + valuest copy_values=values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values, lhs, rhs, src.ns); + else + copy_values.set_to_top(lhs); + +#ifdef DEBUG + copy_values.output(std::cout, src.ns); +#endif + + // if rhs is a variable and lhs is a constant expression + + if(!values.maps_to_top(lhs)) + assign(values, rhs, lhs, src.ns); + else + values.set_to_top(rhs); + +#ifdef DEBUG + values.output(std::cout, src.ns); +#endif + + values.merge(copy_values); + +#ifdef DEBUG + values.output(std::cout, src.ns); +#endif + } + } + } + +#ifdef DEBUG + values.output(std::cout, src.ns); +#endif + + // iterate over values and get all equalities + for(replace_symbolt::expr_mapt::const_iterator + it=values.replace_const.expr_map.begin(); + it!=values.replace_const.expr_map.end(); + ++it) + { + dest.push_back( + equal_exprt( + symbol_exprt(it->first, it->second.type()), it->second)); + } +} + +bool ssa_const_propagatort::valuest::maps_to_top(const exprt &expr) const +{ + find_symbols_sett symbols; + find_symbols(expr, symbols); + for(find_symbols_sett::const_iterator it=symbols.begin(); + it!=symbols.end(); ++it) + { + if(replace_const.expr_map.find(*it)== + replace_const.expr_map.end()) + return true; + } + return false; +} + +void ssa_const_propagatort::assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const +{ +#ifdef DEBUG + std::cout << "assign: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + values.replace_const(rhs); + +#ifdef DEBUG + std::cout << "replace: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + rhs=simplify_expr(rhs, ns); + +#ifdef DEBUG + std::cout << "simplified: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + dest.set_to(lhs, rhs); +} + +bool ssa_const_propagatort::valuest::set_to_top(const irep_idt &id) +{ + bool result=false; + replace_symbolt::expr_mapt::iterator r_it= + replace_const.expr_map.find(id); + if(r_it!=replace_const.expr_map.end()) + { + replace_const.expr_map.erase(r_it); + result=true; + } + if(top_ids.find(id)==top_ids.end()) + { + top_ids.insert(id); + result=true; + } + return result; +} + +bool ssa_const_propagatort::valuest::set_to_top(const exprt &expr) +{ + return set_to_top(to_symbol_expr(expr).get_identifier()); +} + +void ssa_const_propagatort::valuest::set_to( + const irep_idt &lhs_id, + const exprt &rhs_val) +{ + if(replace_const.expr_map[lhs_id]!=rhs_val) + { + replace_const.expr_map[lhs_id]=rhs_val; + iterate=true; + } + std::set::iterator it=top_ids.find(lhs_id); + if(it!=top_ids.end()) + top_ids.erase(it); +} + +void ssa_const_propagatort::valuest::set_to( + const exprt &lhs, + const exprt &rhs_val) +{ + const irep_idt &lhs_id=to_symbol_expr(lhs).get_identifier(); + set_to(lhs_id, rhs_val); +} + +void ssa_const_propagatort::valuest::output( + std::ostream &out, + const namespacet &ns) const +{ + out << "const map: " << std::endl; + for(replace_symbolt::expr_mapt::const_iterator + it=replace_const.expr_map.begin(); + it!=replace_const.expr_map.end(); + ++it) + out << ' ' << it->first << "=" + << from_expr(ns, "", it->second) << std::endl; + out << "top ids: " << std::endl; + for(std::set::const_iterator + it=top_ids.begin(); + it!=top_ids.end(); + ++it) + out << ' ' << *it << std::endl; +} + +bool ssa_const_propagatort::valuest::merge(const valuest &src) +{ + bool changed=false; + for(replace_symbolt::expr_mapt::const_iterator + it=src.replace_const.expr_map.begin(); + it!=src.replace_const.expr_map.end(); ++it) + { + replace_symbolt::expr_mapt::iterator + c_it=replace_const.expr_map.find(it->first); + if(c_it!=replace_const.expr_map.end()) + { + if(c_it->second!=it->second) + { + set_to_top(it->first); + changed=true; + } + } + else if(top_ids.find(it->first)==top_ids.end()) + { + set_to(it->first, it->second); + changed=true; + } + } + + return changed; +} diff --git a/src/ssa/ssa_const_propagator.h b/src/ssa/ssa_const_propagator.h new file mode 100644 index 000000000..c8db8df51 --- /dev/null +++ b/src/ssa/ssa_const_propagator.h @@ -0,0 +1,75 @@ +/*******************************************************************\ + +Module: SSA Constant Propagator + +Author: Kumar Madhukar + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H +#define CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H + +#include +#include + +#include "local_ssa.h" + +class ssa_const_propagatort:public messaget +{ +public: + void operator()( + std::list &dest, + const local_SSAt &src); + + struct valuest + { + public: + // maps variables to constants + replace_symbolt replace_const; + std::set top_ids; + + void output(std::ostream &, const namespacet &) const; + + bool merge(const valuest &src); + + + void clear() + { + replace_const.expr_map.clear(); + replace_const.type_map.clear(); + top_ids.clear(); + } + + bool empty() const + { + return replace_const.expr_map.empty() && + replace_const.type_map.empty() && + top_ids.empty(); + } + + void set_to(const exprt &lhs, const exprt &rhs_val); + void set_to(const irep_idt &lhs_id, const exprt &rhs_val); + + bool maps_to_top(const exprt &expr) const; + bool set_to_top(const exprt &expr); + bool set_to_top(const irep_idt &id); + + bool iterate; + }; + + valuest values; + +protected: + void assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const; + + exprt evaluate_casts_in_constants( + exprt expr, + const typet& parent_type, + bool &valid) const; +}; + +#endif // CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H diff --git a/src/ssa/ssa_db.cpp b/src/ssa/ssa_db.cpp new file mode 100644 index 000000000..cfb570f0e --- /dev/null +++ b/src/ssa/ssa_db.cpp @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: Storage for Function SSAs + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "ssa_db.h" + +void ssa_dbt::depgraph_create( + const function_namet &function_name, + const namespacet &ns, + ssa_inlinert &ssa_inliner, + bool entry) +{ + depgraph_store[function_name]=new ssa_dependency_grapht(*this, ns); + const local_SSAt &SSA=this->get(function_name); + depgraph_store[function_name]->create(SSA, ssa_inliner, entry); +} + diff --git a/src/ssa/ssa_db.h b/src/ssa/ssa_db.h new file mode 100644 index 000000000..c3c332074 --- /dev/null +++ b/src/ssa/ssa_db.h @@ -0,0 +1,104 @@ +/*******************************************************************\ + +Module: Storage for Function SSAs + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_DB_H +#define CPROVER_2LS_SSA_SSA_DB_H + +#include + +#include +#include +#include +#include + +class ssa_inlinert; +class ssa_dependency_grapht; + +class ssa_dbt +{ +public: + typedef irep_idt function_namet; + typedef std::map functionst; + typedef std::map depgrapht; + typedef std::map solverst; + + explicit ssa_dbt(const optionst &_options): + options(_options) + { + } + + ~ssa_dbt() + { + for(auto &item : store) + delete item.second; + for(auto &item : the_solvers) + delete item.second; + for(auto &item : depgraph_store) + delete item.second; + } + + local_SSAt &get(const function_namet &function_name) const + { + return *store.at(function_name); + } + + ssa_dependency_grapht &get_depgraph( + const function_namet &function_name) const + { + return *depgraph_store.at(function_name); + } + + incremental_solvert &get_solver(const function_namet &function_name) + { + solverst::iterator it=the_solvers.find(function_name); + if(it!=the_solvers.end()) + return *(it->second); + + the_solvers[function_name]= + incremental_solvert::allocate( + store.at(function_name)->ns, + options.get_bool_option("refine")); + return *the_solvers.at(function_name); + } + + functionst &functions() { return store; } + solverst &solvers() { return the_solvers; } + + bool exists(const function_namet &function_name) const + { + return store.find(function_name)!=store.end(); + } + + void create( + const function_namet &function_name, + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + store[function_name]=new unwindable_local_SSAt(goto_function, ns); + } + + void depgraph_create( + const function_namet &function_name, + const namespacet &ns, + ssa_inlinert &ssa_inliner, + bool entry) + { + depgraph_store[function_name]=new ssa_dependency_grapht(*this, ns); + const local_SSAt &SSA=this->get(function_name); + depgraph_store[function_name]->create(SSA, ssa_inliner, entry); + } + + +protected: + const optionst &options; + functionst store; + depgrapht depgraph_store; + solverst the_solvers; +}; + +#endif diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp new file mode 100644 index 000000000..e7490c685 --- /dev/null +++ b/src/ssa/ssa_dependency_graph.cpp @@ -0,0 +1,457 @@ +/*******************************************************************\ + +Module: SSA Dependency Graph + +Author: Madhukar Kumar + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "ssa_dependency_graph.h" + +void ssa_dependency_grapht::output(std::ostream &out) const +{ + for(unsigned index=0; index Used Symbols: "; + for(find_symbols_sett::const_iterator u_it= + depnodes_map[index].used_symbols.begin(); + u_it!=depnodes_map[index].used_symbols.end(); u_it++) + { + out << *u_it << " "; + } + out << "\n"; + + out << "Node#" << index << "; -> Modified Symbols: "; + for(find_symbols_sett::const_iterator m_it= + depnodes_map[index].modified_symbols.begin(); + m_it!=depnodes_map[index].modified_symbols.end(); m_it++) + { + out << *m_it << " "; + } + out << "\n"; + + out << "Successors: "; + for(unsigned i=0; ilocation; + + // loop-head select + // TODO: this is an ugly hack (this can be changed + // as soon as unwindable_local_SSA provides + // smooth renaming with odometers) + if(e_it->op1().id()==ID_if && + e_it->op1().op0().id()==ID_symbol) + { + std::string var_string=id2string(e_it->op1().op0().get(ID_identifier)); + if(((var_string.substr(0, 14))=="ssa::$guard#ls")) + { + temp_node.is_loop=true; + temp_node.guard=not_exprt(e_it->op1().op0()); + } + } + + equal_exprt e=to_equal_expr(*e_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + + if(!ignore_equality_done) + { + std::string var_string=id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0, 11))=="ssa::$guard") && (rhs.is_true())) + { + ignore_equality=true; + ignore_equality_done=true; + } + } + + if(first_node && ignore_equality) + { + if(entry) + { + depnodes_map.push_back(temp_node); + } + ignore_equality=false; + } + else + { + depnodes_map.push_back(temp_node); + } + } + + // collecting symbols from constraints and populating dependency graph nodes + for(local_SSAt::nodet::constraintst::const_iterator c_it= + node.constraints.begin(); + c_it!=node.constraints.end(); c_it++) + { + find_symbols(*c_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*c_it; + temp_node.location=n_it->location; + find_symbols(*c_it, temp_node.used_symbols); + find_symbols(*c_it, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + // collecting symbols from assertionst and populating dependency graph nodes + for(local_SSAt::nodet::assertionst::const_iterator a_it= + node.assertions.begin(); + a_it!=node.assertions.end(); a_it++) + { + find_symbols(*a_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=true; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*a_it; + temp_node.location=n_it->location; + find_symbols(*a_it, temp_node.used_symbols); + depnodes_map.push_back(temp_node); + } + +#if 0 + // collecting symbols from assumptionst + // and populating dependency graph nodes + for(local_SSAt::nodet::assumptionst::const_iterator a_it= + node.assumptions.begin(); + a_it!=node.assumptions.end(); a_it++) + { + find_symbols(*a_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.node_info=*a_it; + find_symbols(*a_it, temp_node.used_symbols); + find_symbols(*a_it, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } +#endif + + // collecting symbols from function_callst + // and populating dependency graph nodes + for(local_SSAt::nodet::function_callst::const_iterator f_it= + node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + if(ssa_db.exists(fname)) + { + const local_SSAt &fSSA=ssa_db.get(fname); + + /******************************************************************/ + /******* additional nodes needed to fix the dependency tree *******/ + + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; + int counter=ssa_inliner.get_rename_counter(); + + ssa_inliner.get_guard_binding(SSA, fSSA, n_it, guard_binding, counter); + +#if 0 + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.node_info=guard_binding; + + equal_exprt e=to_equal_expr(guard_binding); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } +#endif + + ssa_inliner.get_bindings( + SSA, fSSA, n_it, f_it, bindings_in, bindings_out, counter); + + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++) + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*b_it; + temp_node.location=n_it->location; + + equal_exprt e=to_equal_expr(*b_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++) + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*b_it; + temp_node.location=n_it->location; + + equal_exprt e=to_equal_expr(*b_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + /******************************************************************/ + + depnodet temp_node; + temp_node.guard=guard_binding; + temp_node.is_assertion=false; + temp_node.is_function_call=true; + temp_node.is_loop=false; + temp_node.node_info=*f_it; + temp_node.rename_counter=counter; + temp_node.location=n_it->location; + + find_symbols(guard_binding, temp_node.used_symbols); + + for(local_SSAt::var_listt::const_iterator p_it=fSSA.params.begin(); + p_it!=fSSA.params.end(); p_it++) + { + irep_idt id=(*p_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it=fSSA.globals_in.begin(); + g_it!=fSSA.globals_in.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it=fSSA.globals_out.begin(); + g_it!=fSSA.globals_out.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.modified_symbols.insert(id); + } + + depnodes_map.push_back(temp_node); + } + } + } + first_node=false; + + depnodet source_node; + source_node.is_assertion=false; + source_node.is_function_call=false; + source_node.is_loop=false; + + // params and globals_in are the modified_symbols at source_node + + for(local_SSAt::var_listt::const_iterator p_it=SSA.params.begin(); + p_it!=SSA.params.end(); p_it++) + { + irep_idt id=(*p_it).get(ID_identifier); + source_node.modified_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it=SSA.globals_in.begin(); + g_it!=SSA.globals_in.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); + source_node.modified_symbols.insert(id); + } + + depnodes_map.push_back(source_node); // source_node + + top_node_index=depnodes_map.size()-1; + + for(find_symbols_sett::const_iterator + s_it=all_ssa_symbols.begin(); s_it!=all_ssa_symbols.end(); s_it++) + { + for(unsigned m_index=0; m_index +#include + +#include "local_ssa.h" + +class ssa_inlinert; +class ssa_dbt; + +class ssa_dependency_grapht +{ +public: + ssa_dependency_grapht(ssa_dbt &_db, const namespacet &_ns): + ssa_db(_db), + ns(_ns) + { + } + + struct annotated_predecessort + { + int predecessor_node_index; + find_symbols_sett annotation; + }; + + typedef std::list annotated_predecessorst; + + struct depnodet + { + exprt node_info; + exprt guard; // guard binding or loop-head select + bool is_assertion; + bool is_function_call; + bool is_loop; + // bool trivial_guard; + int rename_counter; + find_symbols_sett used_symbols; + find_symbols_sett modified_symbols; + annotated_predecessorst predecessors; + std::vector successors; + local_SSAt::locationt location; + }; + + // typedef std::map depnodest; + typedef std::vector depnodest; + depnodest depnodes_map; + + int top_node_index; + + // special source_node and sink_node + // depnodet source_node=depnodes_map[top_node_index]; + // depnodet sink_node=depnodes_map[0]; + + void create(const local_SSAt &SSA, ssa_inlinert &ssa_inliner, bool entry); + void output(std::ostream &) const; + +protected: + ssa_dbt &ssa_db; + const namespacet &ns; +}; + +#endif // CPROVER_2LS_SSA_SSA_DEPENDENCY_GRAPH_H diff --git a/src/ssa/ssa_dereference.cpp b/src/ssa/ssa_dereference.cpp index 27db94362..0425e7d54 100644 --- a/src/ssa/ssa_dereference.cpp +++ b/src/ssa/ssa_dereference.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -56,7 +56,7 @@ exprt lift_if(const exprt &src) if_expr.true_case()=src; if_expr.true_case().op0()=previous; } - + if(if_expr.false_case().is_not_nil()) { exprt previous=if_expr.false_case(); @@ -103,22 +103,26 @@ bool ssa_may_alias( return to_symbol_expr(e1).get_identifier()== to_symbol_expr(e2).get_identifier(); } - + // __CPROVER symbols if(e1.id()==ID_symbol && - has_prefix(id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) + has_prefix( + id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) return false; if(e2.id()==ID_symbol && - has_prefix(id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) + has_prefix( + id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) return false; if(e1.id()==ID_symbol && - has_suffix(id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) + has_suffix( + id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) return false; if(e2.id()==ID_symbol && - has_suffix(id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) + has_suffix( + id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) return false; // Both member? @@ -127,11 +131,11 @@ bool ssa_may_alias( { const member_exprt &m1=to_member_expr(e1); const member_exprt &m2=to_member_expr(e2); - + // same component? if(m1.get_component_name()!=m2.get_component_name()) return false; - + return ssa_may_alias(m1.struct_op(), m2.struct_op(), ns); } @@ -147,33 +151,35 @@ bool ssa_may_alias( const typet &t1=ns.follow(e1.type()); const typet &t2=ns.follow(e2.type()); - + // If one is an array and the other not, consider the elements if(t1.id()==ID_array && t2.id()!=ID_array) - if(ssa_may_alias(index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) + if(ssa_may_alias( + index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) return true; - + if(t2.id()==ID_array && t2.id()!=ID_array) - if(ssa_may_alias(e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) + if(ssa_may_alias( + e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) return true; - + // Pointers only alias with other pointers, // which is a restriction. if(t1.id()==ID_pointer) return t2.id()==ID_pointer; - + if(t2.id()==ID_pointer) return t1.id()==ID_pointer; - + // Is one a scalar pointer? if(e1.id()==ID_dereference && (t1.id()==ID_signedbv || t1.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; - + if(e2.id()==ID_dereference && (t2.id()==ID_signedbv || t2.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; - + // Is one a pointer? if(e1.id()==ID_dereference || e2.id()==ID_dereference) @@ -185,8 +191,8 @@ bool ssa_may_alias( { return true; } - - // should consider further options, e.g., struct prefixes + + // should consider further options, e.g., struct prefixes return false; } @@ -211,30 +217,31 @@ exprt ssa_alias_guard( const namespacet &ns) { exprt a1=address_canonizer(address_of_exprt(e1), ns); - //TODO: We should compare 'base' pointers here because + // TODO: We should compare 'base' pointers here because // we have a higher chance that there was no pointer arithmetic // on the base pointer than that the result of the pointer // arithmetic points to a base pointer. // The following hack does that: - if(a1.id()==ID_plus) a1 = a1.op0(); - + if(a1.id()==ID_plus) + a1=a1.op0(); + exprt a2=address_canonizer(address_of_exprt(e2), ns); - + // in some cases, we can use plain address equality, // as we assume well-aligned-ness mp_integer size1=pointer_offset_size(e1.type(), ns); mp_integer size2=pointer_offset_size(e2.type(), ns); - + if(size1>=size2) { exprt lhs=a1; exprt rhs=a2; if(ns.follow(rhs.type())!=ns.follow(lhs.type())) rhs=typecast_exprt(rhs, lhs.type()); - + return equal_exprt(lhs, rhs); } - + return same_object(a1, a2); } @@ -264,7 +271,7 @@ exprt ssa_alias_value( exprt a1=address_canonizer(address_of_exprt(e1), ns); exprt a2=address_canonizer(address_of_exprt(e2), ns); - + exprt offset1=pointer_offset(a1); // array index possible? @@ -279,7 +286,8 @@ exprt ssa_alias_value( return index_exprt(e2, offset1, e1.type()); else if(element_size>1) { - exprt index=div_exprt(offset1, from_integer(element_size, offset1.type())); + exprt index= + div_exprt(offset1, from_integer(element_size, offset1.type())); return index_exprt(e2, index, e1.type()); } } @@ -287,8 +295,8 @@ exprt ssa_alias_value( byte_extract_exprt byte_extract(byte_extract_id(), e1.type()); byte_extract.op()=e2; byte_extract.offset()=offset1; - - return byte_extract; + + return byte_extract; } /*******************************************************************\ @@ -304,15 +312,16 @@ Function: dereference_rec \*******************************************************************/ exprt dereference_rec( - const exprt &src, - const ssa_value_domaint &ssa_value_domain, - const std::string &nondet_prefix, - const namespacet &ns) + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) { if(src.id()==ID_dereference) { const exprt &pointer=to_dereference_expr(src).pointer(); - exprt pointer_deref=dereference(pointer, ssa_value_domain, nondet_prefix, ns); + exprt pointer_deref= + dereference(pointer, ssa_value_domain, nondet_prefix, ns); // We use the identifier produced by // local_SSAt::replace_side_effects_rec @@ -337,25 +346,27 @@ exprt dereference_rec( else if(src.id()==ID_member) { member_exprt tmp=to_member_expr(src); - tmp.struct_op()=dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); - + tmp.struct_op()= + dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); + #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; #endif if(tmp.struct_op().is_nil()) return nil_exprt(); - + return lift_if(tmp); } else if(src.id()==ID_address_of) { address_of_exprt tmp=to_address_of_expr(src); - tmp.object()=dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); + tmp.object()= + dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); if(tmp.object().is_nil()) return nil_exprt(); - + return lift_if(tmp); } else @@ -380,10 +391,10 @@ Function: dereference \*******************************************************************/ exprt dereference( - const exprt &src, - const ssa_value_domaint &ssa_value_domain, - const std::string &nondet_prefix, - const namespacet &ns) + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) { #ifdef DEBUG std::cout << "dereference src: " << from_expr(ns, "", src) << '\n'; diff --git a/src/ssa/ssa_dereference.h b/src/ssa/ssa_dereference.h index 2494d5973..c020df6e5 100644 --- a/src/ssa/ssa_dereference.h +++ b/src/ssa/ssa_dereference.h @@ -6,18 +6,14 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_ALIASING_H -#define CPROVER_SSA_ALIASING_H +#ifndef CPROVER_2LS_SSA_SSA_DEREFERENCE_H +#define CPROVER_2LS_SSA_SSA_DEREFERENCE_H #include #include #include "ssa_value_set.h" -//bool ssa_may_alias(const exprt &, const exprt &, const namespacet &); -//exprt ssa_alias_guard(const exprt &, const exprt &, const namespacet &); -//exprt ssa_alias_value(const exprt &, const exprt &, const namespacet &); - exprt dereference( const exprt &, const ssa_value_domaint &, diff --git a/src/ssa/ssa_domain.cpp b/src/ssa/ssa_domain.cpp index 0036e5f6f..a9848e8af 100644 --- a/src/ssa/ssa_domain.cpp +++ b/src/ssa/ssa_domain.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -54,7 +54,7 @@ void ssa_domaint::output( // this maps source -> def out << "PHI " << p_it->first << ": " << (*n_it).second - << " from " + << " from " << (*n_it).first << "\n"; } } @@ -102,7 +102,7 @@ void ssa_domaint::transform( const irep_idt &id=code_dead.get_identifier(); def_map.erase(id); } - + // update source in all defs for(def_mapt::iterator d_it=def_map.begin(); d_it!=def_map.end(); d_it++) @@ -127,7 +127,7 @@ bool ssa_domaint::merge( locationt to) { bool result=false; - + // should traverse both maps simultaneously for(def_mapt::const_iterator d_it_b=b.def_map.begin(); @@ -135,15 +135,15 @@ bool ssa_domaint::merge( d_it_b++) { const irep_idt &id=d_it_b->first; - + // check if we have a phi node for 'id' - + phi_nodest::iterator p_it=phi_nodes.find(id); if(p_it!=phi_nodes.end()) { // yes, simply add to existing phi node loc_def_mapt &phi_node=p_it->second; - phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; + phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; // doesn't get propagated, don't set result to 'true' continue; } @@ -190,7 +190,7 @@ bool ssa_domaint::merge( phi_node[d_it_a->second.source->location_number]=d_it_a->second.def; phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; - + // This phi node is now the new source. d_it_a->second.def.loc=to; d_it_a->second.def.kind=deft::PHI; @@ -203,7 +203,7 @@ bool ssa_domaint::merge( #endif } } - + return result; } @@ -229,7 +229,7 @@ void ssa_ait::initialize(const goto_functionst::goto_functiont &goto_function) { locationt e=goto_function.body.instructions.begin(); ssa_domaint &entry=operator[](e); - + #if 0 // parameters const code_typet::parameterst ¶meters=goto_function.type.parameters(); @@ -243,7 +243,7 @@ void ssa_ait::initialize(const goto_functionst::goto_functiont &goto_function) entry.def_map[id].def.kind=ssa_domaint::deft::INPUT; } #endif - + for(ssa_objectst::objectst::const_iterator o_it=assignments.ssa_objects.objects.begin(); o_it!=assignments.ssa_objects.objects.end(); diff --git a/src/ssa/ssa_domain.h b/src/ssa/ssa_domain.h index 5f3127c70..679fdbc59 100644 --- a/src/ssa/ssa_domain.h +++ b/src/ssa/ssa_domain.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_DOMAIN_H -#define CPROVER_SSA_DOMAIN_H +#ifndef CPROVER_2LS_SSA_SSA_DOMAIN_H +#define CPROVER_2LS_SSA_SSA_DOMAIN_H #include @@ -23,13 +23,13 @@ class ssa_domaint:public ai_domain_baset typedef enum { INPUT, ASSIGNMENT, PHI } kindt; kindt kind; locationt loc; - + inline bool is_input() const { return kind==INPUT; } inline bool is_assignment() const { return kind==ASSIGNMENT; } inline bool is_phi() const { return kind==PHI; } }; - friend inline bool operator == (const deft &a, const deft &b) + friend inline bool operator==(const deft &a, const deft &b) { return a.kind==b.kind && a.loc==b.loc; } @@ -59,10 +59,10 @@ class ssa_domaint:public ai_domain_baset { return out << d.def << " from " << d.source->location_number; } - + typedef std::map def_mapt; def_mapt def_map; - + // The phi nodes map identifiers to incoming branches: // map from source to definition. typedef std::map loc_def_mapt; @@ -74,7 +74,7 @@ class ssa_domaint:public ai_domain_baset locationt to, ai_baset &ai, const namespacet &ns); - + virtual void output( std::ostream &out, const ai_baset &ai, @@ -96,7 +96,7 @@ class ssa_ait:public ait protected: const assignmentst &assignments; - + friend class ssa_domaint; // The overload below is needed to make the entry point get a source diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 13a54781e..9949eb0dc 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -2,7 +2,7 @@ Module: SSA Inliner -Author: Peter Schrammel +Author: Peter Schrammel, Madhukar Kumar \*******************************************************************/ @@ -13,87 +13,281 @@ Author: Peter Schrammel /*******************************************************************\ -Function: ssa_inlinert::get_summary +Function: ssa_inlinert::get_guard_binding Inputs: - Outputs: + Outputs: - Purpose: get summary for function call + Purpose: get guard binding for function call \*******************************************************************/ -void ssa_inlinert::get_summary( +void ssa_inlinert::get_guard_binding( const local_SSAt &SSA, + const local_SSAt &fSSA, local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) + exprt &guard_binding, + int counter) { - counter++; + exprt callee_guard, caller_guard; + callee_guard=fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); + rename(callee_guard, counter); + caller_guard=SSA.guard_symbol(n_it->location); - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - if(forward) - { - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - } - else + guard_binding=equal_exprt(callee_guard, caller_guard); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_bindings + + Inputs: + + Outputs: + + Purpose: get bindings for function call + +\*******************************************************************/ + +void ssa_inlinert::get_bindings( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter) +{ + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + +#if 0 + std::cout << "cs_globals_in: "; + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; + std::cout << std::endl; + + std::cout << "cs_globals_out: "; + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; + std::cout << std::endl; +#endif + + // equalities for arguments + get_replace_params(SSA, fSSA.params, n_it, *f_it, bindings_in, counter); + + // equalities for globals_in + get_replace_globals_in( + fSSA.globals_in, *f_it, cs_globals_in, bindings_in, counter); + + // equalities for globals out (including unmodified globals) + get_replace_globals_out( + fSSA.globals_out, + *f_it, + cs_globals_in, + cs_globals_out, + bindings_out, + counter); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_inlined + + Inputs: + + Outputs: + + Purpose: get inlined function call + + +\*******************************************************************/ + +bool ssa_inlinert::get_inlined( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + int counter, + bool error_summ) +{ + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const local_SSAt &fSSA=ssa_db.get(fname); + + bool assertion_flag=get_summaries( + fSSA, + summaryt::call_sitet(fSSA.goto_function.body.instructions.end()), + forward, + assert_summaries, + noassert_summaries, + bindings, + assertion_map, + error_summ); + + // bindings + exprt guard_binding; + get_guard_binding(SSA, fSSA, n_it, guard_binding, counter); + bindings.push_back(guard_binding); + get_bindings(SSA, fSSA, n_it, f_it, bindings, bindings, counter); + + bool first_equality=true; + for(local_SSAt::nodest::const_iterator n_it=fSSA.nodes.begin(); + n_it!=fSSA.nodes.end(); n_it++) { - SSA.get_globals(loc,cs_globals_out); - SSA.get_globals(loc,cs_globals_in,false); + const local_SSAt::nodet &fnode=*n_it; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + fnode.equalities.begin(); e_it!=fnode.equalities.end(); e_it++) + { + // unless lhs starts with "ssa::guard" and rhs is true + // because that one is replaced by the guard binding + const equal_exprt &e=to_equal_expr(*e_it); + const exprt &lhs=e.lhs(); const exprt &rhs=e.rhs(); + std::string var_string=id2string(to_symbol_expr(lhs).get_identifier()); + if((var_string.substr(0, 11)=="ssa::$guard") && + rhs.is_true() && first_equality) + { + first_equality=false; + } + else + { + noassert_summaries.push_back(*e_it); + rename(noassert_summaries.back(), counter); + } + } + for(local_SSAt::nodet::constraintst::const_iterator c_it= + fnode.constraints.begin(); c_it!=fnode.constraints.end(); c_it++) + { + noassert_summaries.push_back(*c_it); + rename(noassert_summaries.back(), counter); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it= + fnode.assertions.begin(); a_it!=fnode.assertions.end(); a_it++) + { +#if 0 + assert_summaries.push_back(*a_it); + rename(assert_summaries.back(), counter); +#endif + assertion_flag=true; + } } + return assertion_flag; +} + + +/*******************************************************************\ + +Function: ssa_inlinert::get_summary + + Inputs: + + Outputs: + + Purpose: get summary for non-inlined function calls + +\*******************************************************************/ + +bool ssa_inlinert::get_summary( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + int counter, + bool error_summ) +{ + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const summaryt &summary=summary_db.get(fname); + + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + #if 0 std::cout << "cs_globals_in: "; - for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); - it != cs_globals_in.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; std::cout << "cs_globals_out: "; - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; #endif - //equalities for arguments - bindings.push_back(get_replace_params(summary.params,*f_it)); + // equalities for arguments + get_replace_params(SSA, summary.params, n_it, *f_it, bindings, counter); - //equalities for globals_in - if(forward) - bindings.push_back(get_replace_globals_in(summary.globals_in, - cs_globals_in)); - else - bindings.push_back(get_replace_globals_in(summary.globals_out, - cs_globals_out)); + // equalities for globals_in + get_replace_globals_in( + summary.globals_in, *f_it, cs_globals_in, bindings, counter); + + // constraints for transformer - //constraints for transformer exprt transformer; - if(forward) - transformer = summary.fw_transformer.is_nil() ? true_exprt() : - summary.fw_transformer; - else - { - transformer = summary.bw_transformer.is_nil() ? true_exprt() : - summary.bw_transformer; - } - rename(transformer); - summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), - transformer)); - - //equalities for globals out (including unmodified globals) - if(forward) - bindings.push_back(get_replace_globals_out(summary.globals_out, - cs_globals_in,cs_globals_out)); + + if(error_summ) + { + // update transformer using the error_summaries map + summaryt::call_sitet call_site(loc); + summaryt::error_summariest::const_iterator e_it= + summary.error_summaries.find(call_site); + if(e_it!=summary.error_summaries.end() && + !e_it->second.is_nil()) + transformer=e_it->second; + else + transformer=true_exprt(); + } + else + { + if(forward) + transformer=summary.fw_transformer.is_nil() ? true_exprt() : + summary.fw_transformer; + else + transformer=summary.bw_transformer.is_nil() ? true_exprt() : + summary.bw_transformer; + } + + rename(transformer, counter); + if(summary.has_assertion) + { + assert_summaries.push_back( + implies_exprt(SSA.guard_symbol(n_it->location), transformer)); + } else - bindings.push_back(get_replace_globals_out(summary.globals_in, - cs_globals_out,cs_globals_in)); + { + noassert_summaries.push_back( + implies_exprt(SSA.guard_symbol(n_it->location), transformer)); + } + + // equalities for globals out (including unmodified globals) + get_replace_globals_out( + summary.globals_out, + *f_it, + cs_globals_in, + cs_globals_out, + bindings, + counter); + + return summary.has_assertion; } /*******************************************************************\ @@ -102,7 +296,7 @@ Function: ssa_inlinert::get_summaries Inputs: - Outputs: + Outputs: Purpose: get summary for all function calls @@ -110,42 +304,141 @@ Function: ssa_inlinert::get_summaries exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) { - exprt::operandst summaries,bindings; - get_summaries(SSA,true,summaries,bindings); - return and_exprt(conjunction(bindings),conjunction(summaries)); + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings); + return and_exprt(conjunction(bindings), conjunction(summaries)); +} + +exprt ssa_inlinert::get_summaries( + const local_SSAt &SSA, + assertion_mapt &assertion_map) +{ + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings, assertion_map); + return and_exprt(conjunction(bindings), conjunction(summaries)); +} + +void ssa_inlinert::get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings) +{ + assertion_mapt assertion_map; + get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, summaries, summaries, bindings, assertion_map); +} + +void ssa_inlinert::get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map) +{ + get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, summaries, summaries, bindings, assertion_map); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_summaries + + Inputs: + + Outputs: + + Purpose: get summary for all function calls + +\*******************************************************************/ + +bool ssa_inlinert::get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ) +{ + assertion_mapt assertion_map; + return get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, assert_summaries, noassert_summaries, bindings, assertion_map); } -void ssa_inlinert::get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) +bool ssa_inlinert::get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + bool error_summ) { - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + bool assertion_flag=false; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); + a_it!=n_it->assertions.end(); a_it++) + { + assertion_map[n_it->location].push_back(*a_it); + rename(assertion_map[n_it->location].back(), counter); + } + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + // do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site==this_call_site) + continue; - if(summary_db.exists(fname)) + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + // get inlined function + if(n_it->function_calls_inlined) { - get_summary(SSA,n_it,f_it,summary_db.get(fname), - forward,summaries,bindings); + counter++; + bool new_assertion_flag= + get_inlined( + SSA, n_it, f_it, + forward, assert_summaries, noassert_summaries, + bindings, assertion_map, counter, error_summ); + assertion_flag=assertion_flag || new_assertion_flag; + } + // get summary + else if(summary_db.exists(fname)) + { + counter++; + bool new_assertion_flag= + get_summary( + SSA, n_it, f_it, + forward, assert_summaries, noassert_summaries, + bindings, counter, error_summ); + assertion_flag=assertion_flag || new_assertion_flag; } } } + return assertion_flag; } + /*******************************************************************\ Function: ssa_inlinert::replace Inputs: - Outputs: + Outputs: Purpose: replaces function calls by summaries if available in the summary store @@ -153,43 +446,47 @@ Function: ssa_inlinert::replace \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - bool forward, - bool preconditions_as_assertions) +void ssa_inlinert::replace( + local_SSAt &SSA, + bool forward, + bool preconditions_as_assertions, + int counter) { - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + for(local_SSAt::nodet::function_callst::iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); - if(summary_db.exists(fname)) + if(summary_db.exists(fname)) { - summaryt summary = summary_db.get(fname); + summaryt summary=summary_db.get(fname); status() << "Replacing function " << fname << " by summary" << eom; - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); - //replace - replace(SSA,n_it,f_it,cs_globals_in,cs_globals_out,summary, - forward,preconditions_as_assertions); + // replace + replace( + SSA, n_it, f_it, cs_globals_in, cs_globals_out, summary, + forward, preconditions_as_assertions, counter); - //remove function_call + // remove function_call rm_function_calls.insert(f_it); } - else debug() << "No summary available for function " << fname << eom; + else + debug() << "No summary available for function " << fname << eom; commit_node(n_it); } - commit_nodes(SSA.nodes,n_it); + commit_nodes(SSA.nodes, n_it); } } @@ -199,71 +496,78 @@ Function: ssa_inlinert::replace Inputs: - Outputs: + Outputs: - Purpose: replaces inlines functions + Purpose: replaces inlines functions if SSA is available in functions and does nothing otherwise \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - bool recursive, bool rename) +void ssa_inlinert::replace( + local_SSAt &SSA, + const ssa_dbt &ssa_db, + int counter, + bool recursive, + bool rename) { - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + for(local_SSAt::nodet::function_callst::iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); - if(ssa_db.exists(fname)) + if(ssa_db.exists(fname)) { status() << "Inlining function " << fname << eom; - local_SSAt fSSA = ssa_db.get(fname); //copy + local_SSAt fSSA=ssa_db.get(fname); // copy if(rename) { - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - - if(recursive) - { - replace(fSSA,ssa_db,true); - } - - //replace - replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA); + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + + if(recursive) + { + replace(fSSA, ssa_db, true, counter); + } + + // replace + replace( + SSA.nodes, n_it, f_it, + cs_globals_in, cs_globals_out, + fSSA, counter); } else // just add to nodes - { - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++) - { - debug() << "new node: "; fn_it->output(debug(),fSSA.ns); + { + for(local_SSAt::nodest::const_iterator fn_it=fSSA.nodes.begin(); + fn_it!=fSSA.nodes.end(); fn_it++) + { + debug() << "new node: "; fn_it->output(debug(), fSSA.ns); debug() << eom; new_nodes.push_back(*fn_it); - } - } + } + } } - else debug() << "No body available for function " << fname << eom; + else + debug() << "No body available for function " << fname << eom; commit_node(n_it); } - commit_nodes(SSA.nodes,n_it); + commit_nodes(SSA.nodes, n_it); } } /*******************************************************************\ -Function: ssa_inlinert::replace() +Function: ssa_inlinert::replace Inputs: @@ -273,105 +577,113 @@ Function: ssa_inlinert::replace() \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const summaryt &summary, - bool forward, - bool preconditions_as_assertions) +void ssa_inlinert::replace( + local_SSAt &SSA, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + bool forward, + bool preconditions_as_assertions, + int counter) { - counter++; + // equalities for arguments + replace_params(summary.params, *f_it, counter); - //equalities for arguments - replace_params(summary.params,*f_it); + // equalities for globals_in + replace_globals_in(summary.globals_in, cs_globals_in, counter); - //equalities for globals_in - replace_globals_in(summary.globals_in,cs_globals_in); - - //constraints for precondition and transformer + // constraints for precondition and transformer exprt precondition; - if(forward) precondition = summary.fw_precondition; - else precondition = summary.bw_precondition; + if(forward) + precondition=summary.fw_precondition; + else + precondition=summary.bw_precondition; if(!preconditions_as_assertions) { - rename(precondition); + rename(precondition, counter); node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } else { - rename(precondition); + rename(precondition, counter); node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } exprt transformer; - if(forward) transformer = summary.fw_transformer; - else transformer = summary.bw_transformer; - node->constraints.push_back(transformer); //copy - exprt &_transformer = node->constraints.back(); - rename(_transformer); - - //remove function call + if(forward) + transformer=summary.fw_transformer; + else + transformer=summary.bw_transformer; + node->constraints.push_back(transformer); // copy + exprt &_transformer=node->constraints.back(); + rename(_transformer, counter); + + // remove function call rm_function_calls.insert(f_it); - //equalities for globals out (including unmodified globals) - replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out); + // equalities for globals out (including unmodified globals) + replace_globals_out( + summary.globals_out, cs_globals_in, cs_globals_out, counter); } /*******************************************************************\ - Function: ssa_inlinert::replace() + Function: ssa_inlinert::replace Inputs: Outputs: - Purpose: inline function + Purpose: inline function Remark: local_SSAt::nodest maps a goto program target to a single SSA node, - when inlining several calls to the same function - instructions appear factorized by the goto program targets + when inlining several calls to the same function + instructions appear factorized by the goto program targets \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const local_SSAt &function) +void ssa_inlinert::replace( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &function, + int counter) { - counter++; - - //equalities for arguments - replace_params(function.params,*f_it); + // equalities for arguments + replace_params(function.params, *f_it, counter); - //equalities for globals_in - replace_globals_in(function.globals_in,cs_globals_in); + // equalities for globals_in + replace_globals_in(function.globals_in, cs_globals_in, counter); - //add function body - for(local_SSAt::nodest::const_iterator n_it = function.nodes.begin(); - n_it != function.nodes.end(); n_it++) + // add function body + for(local_SSAt::nodest::const_iterator n_it=function.nodes.begin(); + n_it!=function.nodes.end(); n_it++) { - local_SSAt::nodet n = *n_it; //copy - rename(n); + local_SSAt::nodet n=*n_it; // copy + rename(n, counter); new_nodes.push_back(n); } - - //remove function call + + // remove function call rm_function_calls.insert(f_it); - //equalities for globals out (including unmodified globals) - replace_globals_out(function.globals_out,cs_globals_in,cs_globals_out); + // equalities for globals out (including unmodified globals) + replace_globals_out( + function.globals_out, cs_globals_in, cs_globals_out, counter); } /*******************************************************************\ -Function: ssa_inlinert::replace_globals_in() +Function: ssa_inlinert::get_replace_globals_in Inputs: @@ -381,51 +693,60 @@ Function: ssa_inlinert::replace_globals_in() \*******************************************************************/ -exprt ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals) +void ssa_inlinert::get_replace_globals_in( + const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter) { - //equalities for globals_in - exprt::operandst c; - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) + std::string suffix=id2string(funapp_expr.get(ID_suffix)); + + // equalities for globals_in + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) { - symbol_exprt lhs = *it; //copy - rename(lhs); + symbol_exprt lhs=*it; // copy + rename(lhs, counter); symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) + if(find_corresponding_symbol(*it, globals, rhs)) { - debug() << "binding: " << lhs.get_identifier() << " == " + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + + debug() << "binding: " << lhs.get_identifier() << "==" << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs,rhs)); + c.push_back(equal_exprt(lhs, rhs)); } #if 0 else - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif } - return conjunction(c); + // return conjunction(c); } -void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals) +void ssa_inlinert::replace_globals_in( + const local_SSAt::var_sett &globals_in, + const local_SSAt::var_sett &globals, + int counter) { - //equalities for globals_in - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) + // equalities for globals_in + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) { - symbol_exprt lhs = *it; //copy - rename(lhs); + symbol_exprt lhs=*it; // copy + rename(lhs, counter); symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) + if(find_corresponding_symbol(*it, globals, rhs)) { - debug() << "binding: " << lhs.get_identifier() << " == " + debug() << "binding: " << lhs.get_identifier() << "==" << rhs.get_identifier() << eom; - new_equs.push_back(equal_exprt(lhs,rhs)); + new_equs.push_back(equal_exprt(lhs, rhs)); } #if 0 else - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif } @@ -433,7 +754,7 @@ void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, /*******************************************************************\ -Function: ssa_inlinert::replace_params() +Function: ssa_inlinert::get_replace_params Inputs: @@ -443,55 +764,90 @@ Function: ssa_inlinert::replace_params() \*******************************************************************/ -exprt ssa_inlinert::get_replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) +void ssa_inlinert::get_replace_params( + const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter) { - //equalities for arguments - exprt::operandst c; - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); - it != funapp_expr.arguments().end(); it++, p_it++) - { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + // equalities for arguments + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=funapp_expr.arguments().begin(); + it!=funapp_expr.arguments().end(); it++, p_it++) + { +#if 0 + std::cout << "replace param " << from_expr(SSA.ns, "", *p_it) + << "==" << from_expr(SSA.ns, "", *it) << std::endl; +#endif + +#if 0 + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(funapp_expr.arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - - exprt lhs = *p_it; //copy - rename(lhs); - c.push_back(equal_exprt(lhs,*it)); +#endif + + if(SSA.ns.follow(it->type()).id()==ID_struct) + { + exprt rhs=SSA.read_rhs(*it, n_it->location); // copy +#if 0 + std::cout << "split param " << from_expr(SSA.ns, "", *it) + << " into " << from_expr(SSA.ns, "", rhs) << std::endl; +#endif + forall_operands(o_it, rhs) + { + assert(p_it!=params.end()); + exprt lhs=*p_it; // copy + rename(lhs, counter); +#if 0 + std::cout << "split replace param " << from_expr(SSA.ns, "", *p_it) + << "==" << from_expr(SSA.ns, "", *o_it) << std::endl; +#endif + c.push_back(equal_exprt(lhs, *o_it)); + ++p_it; + } + } + else + { + exprt lhs=*p_it; // copy + rename(lhs, counter); + c.push_back(equal_exprt(lhs, *it)); + } } - return conjunction(c); } -void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) +void ssa_inlinert::replace_params( + const local_SSAt::var_listt ¶ms, + const function_application_exprt &funapp_expr, + int counter) { - //equalities for arguments - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); - it != funapp_expr.arguments().end(); it++, p_it++) - { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + // equalities for arguments + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=funapp_expr.arguments().begin(); + it!=funapp_expr.arguments().end(); it++, p_it++) + { + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(funapp_expr.arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - - exprt lhs = *p_it; //copy - rename(lhs); - new_equs.push_back(equal_exprt(lhs,*it)); + + exprt lhs=*p_it; // copy + rename(lhs, counter); + new_equs.push_back(equal_exprt(lhs, *it)); } } /*******************************************************************\ -Function: ssa_inlinert::replace_globals_out() +Function: ssa_inlinert::replace_globals_out Inputs: @@ -501,49 +857,60 @@ Function: ssa_inlinert::replace_globals_out() \*******************************************************************/ -exprt ssa_inlinert::get_replace_globals_out( - const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) +void ssa_inlinert::get_replace_globals_out( + const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter) { - //equalities for globals_out - exprt::operandst c; - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) + std::string suffix=id2string(funapp_expr.get(ID_suffix)); + + // equalities for globals_out + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) { - symbol_exprt rhs = *it; //copy - symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs); + symbol_exprt lhs=*it; // copy + + lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + + symbol_exprt rhs; + if(find_corresponding_symbol(*it, globals_out, rhs)) + rename(rhs, counter); else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - c.push_back(equal_exprt(lhs,rhs)); + { + bool found=find_corresponding_symbol(*it, cs_globals_in, rhs); + assert(found); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + } + c.push_back(equal_exprt(lhs, rhs)); } - return conjunction (c); } void ssa_inlinert::replace_globals_out( - const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) + const local_SSAt::var_sett &globals_out, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter) { - //equalities for globals_out - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) + // equalities for globals_out + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) { - symbol_exprt rhs = *it; //copy + symbol_exprt rhs=*it; // copy symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs); + if(find_corresponding_symbol(*it, globals_out, lhs)) + rename(lhs, counter); else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - new_equs.push_back(equal_exprt(lhs,rhs)); + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + new_equs.push_back(equal_exprt(lhs, rhs)); } } /*******************************************************************\ -Function: ssa_inlinert::havoc() +Function: ssa_inlinert::havoc Inputs: @@ -553,16 +920,61 @@ Function: ssa_inlinert::havoc() \*******************************************************************/ -void ssa_inlinert::havoc(local_SSAt::nodet &node, - local_SSAt::nodet::function_callst::iterator f_it) +void ssa_inlinert::havoc( + local_SSAt::nodet &node, + local_SSAt::nodet::function_callst::iterator f_it) { - //remove function call + // remove function call rm_function_calls.insert(f_it); } /*******************************************************************\ -Function: ssa_inlinert::rename() +Function: ssa_inlinert::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_inlinert::rename(irep_idt &id, int counter, bool attach) +{ + std::string id_str=id2string(id); + + if(!attach) + { + // find first @ where afterwards there are no letters + size_t pos=std::string::npos; + for(size_t i=0; i=0) + id=id_str+"@"+i2string(counter); + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::rename Inputs: @@ -572,24 +984,25 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(exprt &expr) +void ssa_inlinert::rename(exprt &expr, int counter, bool attach) { - if(expr.id()==ID_symbol) + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - symbol_exprt &sexpr = to_symbol_expr(expr); - irep_idt id = id2string(sexpr.get_identifier())+"@"+i2string(counter); - sexpr.set_identifier(id); + irep_idt id=expr.get(ID_identifier); + rename(id, counter, attach); + + expr.set(ID_identifier, id); } - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) + for(exprt::operandst::iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) { - rename(*it); + rename(*it, counter, attach); } } /*******************************************************************\ -Function: ssa_inlinert::rename() +Function: ssa_inlinert::rename Inputs: @@ -599,29 +1012,29 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(local_SSAt::nodet &node) +void ssa_inlinert::rename(local_SSAt::nodet &node, int counter) { - for(local_SSAt::nodet::equalitiest::iterator e_it = node.equalities.begin(); - e_it != node.equalities.end(); e_it++) + for(local_SSAt::nodet::equalitiest::iterator e_it=node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) { - rename(*e_it); + rename(*e_it, counter); } - for(local_SSAt::nodet::constraintst::iterator c_it = node.constraints.begin(); - c_it != node.constraints.end(); c_it++) + for(local_SSAt::nodet::constraintst::iterator c_it=node.constraints.begin(); + c_it!=node.constraints.end(); c_it++) { - rename(*c_it); - } - for(local_SSAt::nodet::assertionst::iterator a_it = node.assertions.begin(); - a_it != node.assertions.end(); a_it++) + rename(*c_it, counter); + } + for(local_SSAt::nodet::assertionst::iterator a_it=node.assertions.begin(); + a_it!=node.assertions.end(); a_it++) { - rename(*a_it); - } - for(local_SSAt::nodet::function_callst::iterator - f_it = node.function_calls.begin(); - f_it != node.function_calls.end(); f_it++) + rename(*a_it, counter); + } + for(local_SSAt::nodet::function_callst::iterator + f_it=node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) { - rename(*f_it); - } + rename(*f_it, counter); + } } /*******************************************************************\ @@ -637,49 +1050,49 @@ Function: ssa_inlinert::rename_to_caller \*******************************************************************/ void ssa_inlinert::rename_to_caller( - local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, exprt &expr) { assert(params.size()==f_it->arguments().size()); replace_mapt replace_map; - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = f_it->arguments().begin(); - it != f_it->arguments().end(); it++, p_it++) + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=f_it->arguments().begin(); + it!=f_it->arguments().end(); ++it, ++p_it) { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(f_it->arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(f_it->arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - replace_map[*p_it] = *it; + replace_map[*p_it]=*it; } - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) { symbol_exprt cg; - if(find_corresponding_symbol(*it,cs_globals_in,cg)) - replace_map[*it] = cg; - else + if(find_corresponding_symbol(*it, cs_globals_in, cg)) + replace_map[*it]=cg; + else { #if 0 - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + replace_map[*it]= + symbol_exprt( + id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); } } - replace_expr(replace_map,expr); + replace_expr(replace_map, expr); } /*******************************************************************\ @@ -695,64 +1108,64 @@ Function: ssa_inlinert::rename_to_callee \*******************************************************************/ void ssa_inlinert::rename_to_callee( - local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, exprt &expr) { replace_mapt replace_map; - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = f_it->arguments().begin(); - it != f_it->arguments().end(); it++, p_it++) + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=f_it->arguments().begin(); + it!=f_it->arguments().end(); ++it, ++p_it) { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(f_it->arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(f_it->arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - replace_map[*it] = *p_it; + replace_map[*it]=*p_it; } -/* replace_expr(replace_map,expr); + /* replace_expr(replace_map, expr); - replace_map.clear(); //arguments might contain globals, - // thus, we have to replace them separately - */ - for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); - it != cs_globals_in.end(); it++) + replace_map.clear(); // arguments might contain globals, + // thus, we have to replace them separately + */ + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) { symbol_exprt cg; - if(find_corresponding_symbol(*it,globals_in,cg)) - replace_map[*it] = cg; + if(find_corresponding_symbol(*it, globals_in, cg)) + replace_map[*it]=cg; else { #if 0 - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + replace_map[*it]= + symbol_exprt( + id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); } } - replace_expr(replace_map,expr); + replace_expr(replace_map, expr); } /*******************************************************************\ -Function: ssa_inlinert::commit_node() +Function: ssa_inlinert::commit_node Inputs: Outputs: Purpose: apply changes to node, must be called after replace and havoc - (needed because replace and havoc usually called while + (needed because replace and havoc usually called while iterating over equalities, and hence we cannot modify them) @@ -760,24 +1173,24 @@ Function: ssa_inlinert::commit_node() void ssa_inlinert::commit_node(local_SSAt::nodest::iterator node) { - //remove obsolete function calls - for(std::set::iterator - it = rm_function_calls.begin(); - it != rm_function_calls.end(); it++) + // remove obsolete function calls + for(std::set::iterator + it=rm_function_calls.begin(); + it!=rm_function_calls.end(); it++) { node->function_calls.erase(*it); } rm_function_calls.clear(); - //insert new equalities - node->equalities.insert(node->equalities.end(), - new_equs.begin(),new_equs.end()); + // insert new equalities + node->equalities.insert( + node->equalities.end(), new_equs.begin(), new_equs.end()); new_equs.clear(); } /*******************************************************************\ -Function: ssa_inlinert::commit_nodes() +Function: ssa_inlinert::commit_nodes Inputs: @@ -787,11 +1200,13 @@ Function: ssa_inlinert::commit_nodes() \*******************************************************************/ -bool ssa_inlinert::commit_nodes(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator n_pos) +bool ssa_inlinert::commit_nodes( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator n_pos) { - if(new_nodes.empty()) return true; - nodes.splice(n_pos,new_nodes,new_nodes.begin(),new_nodes.end()); + if(new_nodes.empty()) + return true; + nodes.splice(n_pos, new_nodes, new_nodes.begin(), new_nodes.end()); return false; } @@ -807,20 +1222,22 @@ Function: ssa_inlinert::find_corresponding_symbol \*******************************************************************/ -bool ssa_inlinert::find_corresponding_symbol(const symbol_exprt &s, - const local_SSAt::var_sett &globals, - symbol_exprt &s_found) +bool ssa_inlinert::find_corresponding_symbol( + const symbol_exprt &s, + const local_SSAt::var_sett &globals, + symbol_exprt &s_found) { - const irep_idt &s_orig_id = get_original_identifier(s); - for(local_SSAt::var_sett::const_iterator it = globals.begin(); - it != globals.end(); it++) + const irep_idt &s_orig_id=get_original_identifier(s); + for(local_SSAt::var_sett::const_iterator it=globals.begin(); + it!=globals.end(); it++) { #if 0 - std::cout << s_orig_id << " =?= " << get_original_identifier(*it) << std::endl; + std::cout << s_orig_id << "=?= " + << get_original_identifier(*it) << std::endl; #endif - if(s_orig_id == get_original_identifier(*it)) + if(s_orig_id==get_original_identifier(*it)) { - s_found = *it; + s_found=*it; return true; } } @@ -833,7 +1250,7 @@ Function: ssa_inlinert::get_original_identifier Inputs: - Outputs: + Outputs: Purpose: TODO: this is a potential source of bugs. Better way to do that? @@ -841,27 +1258,27 @@ Function: ssa_inlinert::get_original_identifier irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) { - std::string id = id2string(s.get_identifier()); + std::string id=id2string(s.get_identifier()); - //find first #@%!$ where afterwards there are no letters - size_t pos = std::string::npos; - for(size_t i=0;i -#include "../summarizer/summary_db.h" -#include "../summarizer/ssa_db.h" -#include "../ssa/local_ssa.h" +#include +#include -class summary_dbt; +#include "ssa_db.h" +#include "local_ssa.h" -class ssa_inlinert : public messaget +class ssa_inlinert:public messaget { public: - explicit ssa_inlinert(summary_dbt &_summary_db) : - counter(0), - summary_db(_summary_db) - {} - - void get_summary(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); - void get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); + ssa_inlinert( + summary_dbt &_summary_db, + ssa_dbt &_ssa_db): + counter(-1), + summary_db(_summary_db), + ssa_db(_ssa_db) + { + } + + typedef std::map assertion_mapt; + + void get_guard_binding( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter); + void get_bindings( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter); + bool get_summary( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + int counter, + bool error_summ=false); + bool get_inlined( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + int counter, + bool error_summ); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings); + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ=false); + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + bool error_summ=false); + + // TODO: need to explicitly pass the correct counter exprt get_summaries(const local_SSAt &SSA); - - void replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site - const summaryt &summary, - bool forward, - bool preconditions_as_assertions); - - void replace(local_SSAt &SSA, - bool forward, - bool preconditions_as_assertions); - - void replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site - const local_SSAt &function); - - void replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - bool recursive=false, - bool rename=true); - - void havoc(local_SSAt::nodet &node, - local_SSAt::nodet::function_callst::iterator f_it); - - //apply changes to node, must be called after replace and havoc + exprt get_summaries( + const local_SSAt &SSA, + assertion_mapt &assertion_map); + + void replace( + local_SSAt &SSA, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site + const summaryt &summary, + bool forward, + bool preconditions_as_assertions, + int counter); + + void replace( + local_SSAt &SSA, + bool forward, + bool preconditions_as_assertions, + int counter); + + void replace( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site + const local_SSAt &function, + int counter); + + void replace( + local_SSAt &SSA, + const ssa_dbt &ssa_db, + int counter, + bool recursive=false, + bool rename=true); + + void havoc( + local_SSAt::nodet &node, + local_SSAt::nodet::function_callst::iterator f_it); + + // apply changes to node, must be called after replace and havoc void commit_node(local_SSAt::nodest::iterator node); - bool commit_nodes(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator n_pos); - - //functions for renaming preconditions to calling context - void rename_to_caller(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); - void rename_to_callee(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); - - static bool find_corresponding_symbol(const symbol_exprt &s, - const local_SSAt::var_sett &globals, - symbol_exprt &s_found); + bool commit_nodes( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator n_pos); + + // functions for renaming preconditions to calling context + void rename_to_caller( + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); + void rename_to_callee( + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); + + static bool find_corresponding_symbol( + const symbol_exprt &s, + const local_SSAt::var_sett &globals, + symbol_exprt &s_found); static irep_idt get_original_identifier(const symbol_exprt &s); - protected: - unsigned counter; + void rename(irep_idt &id, int counter, bool attach=true); + + int get_rename_counter() { return ++counter; } + + // moved from protected to public, for use in summarizer_bw_cex_complete + void rename(exprt &expr, int counter, bool attach=true); + +protected: + int counter; summary_dbt &summary_db; + ssa_dbt &ssa_db; local_SSAt::nodest new_nodes; local_SSAt::nodet::equalitiest new_equs; std::set rm_function_calls; - void replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals); - void replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); - void replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); - - exprt get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals); - exprt get_replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); - exprt get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); - - void rename(exprt &expr); - void rename(local_SSAt::nodet &node); - + void replace_globals_in( + const local_SSAt::var_sett &globals_in, + const local_SSAt::var_sett &globals, + int counter); + void replace_params( + const local_SSAt::var_listt ¶ms, + const function_application_exprt &funapp_expr, + int counter); + void replace_globals_out( + const local_SSAt::var_sett &globals_out, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter); + + void get_replace_globals_in( + const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter); + void get_replace_params( + const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter); + void get_replace_globals_out( + const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter); + + void rename(local_SSAt::nodet &node, int counter); }; - #endif diff --git a/src/ssa/ssa_object.cpp b/src/ssa/ssa_object.cpp index 834db068a..4f27c722c 100644 --- a/src/ssa/ssa_object.cpp +++ b/src/ssa/ssa_object.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -55,6 +55,18 @@ void collect_objects_rec( std::set &objects, std::set &literals); +/*******************************************************************\ + +Function: collect_objects_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void collect_objects_address_of_rec( const exprt &src, const namespacet &ns, @@ -62,9 +74,9 @@ void collect_objects_address_of_rec( std::set &literals) { #ifdef DEBUG - std::cout << "COLLECT ADDRESS OF " << from_expr(ns,"",src) << "\n"; + std::cout << "COLLECT ADDRESS OF " << from_expr(ns, "", src) << "\n"; #endif - + if(src.id()==ID_index) { collect_objects_address_of_rec( @@ -112,10 +124,9 @@ void collect_objects_rec( std::set &objects, std::set &literals) { - - #ifdef DEBUG - std::cout << "COLLECT " << from_expr(ns,"",src) << "\n"; - #endif +#ifdef DEBUG + std::cout << "COLLECT " << from_expr(ns, "", src) << "\n"; +#endif if(src.id()==ID_code) { @@ -146,7 +157,7 @@ void collect_objects_rec( const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -158,10 +169,9 @@ void collect_objects_rec( } else { - - #ifdef DEBUG +#ifdef DEBUG std::cout << "OBJECT " << ssa_object.get_identifier() << "\n"; - #endif +#endif objects.insert(ssa_object); } @@ -239,7 +249,7 @@ void ssa_objectst::add_ptr_objects( } } } - + for(objectst::const_iterator o_it=tmp.begin(); o_it!=tmp.end(); o_it++) @@ -277,7 +287,7 @@ void ssa_objectst::categorize_objects( exprt root_object=o_it->get_root_object(); #ifdef DEBUG - std::cout << "CATEGORIZE " << from_expr(ns,"",root_object) << "\n"; + std::cout << "CATEGORIZE " << from_expr(ns, "", root_object) << "\n"; #endif if(root_object.id()==ID_symbol) @@ -358,13 +368,14 @@ ssa_objectt::identifiert ssa_objectt::object_id_rec( { const member_exprt &member_expr=to_member_expr(src); const exprt &compound_op=member_expr.struct_op(); - + // need to distinguish union and struct members if(is_struct_member(member_expr, ns)) { irep_idt compound_object=object_id_rec(compound_op, ns); - if(compound_object==irep_idt()) return identifiert(); - + if(compound_object==irep_idt()) + return identifiert(); + return identifiert( id2string(compound_object)+ "."+id2string(member_expr.get_component_name())); @@ -445,14 +456,13 @@ Function: is_symbol_struct_member Inputs: - Outputs: + Outputs: returns true for symbol(.member)*, where + all members are struct members. Purpose: \*******************************************************************/ -// Returns true for symbol(.member)*, where -// all members are struct members. bool is_symbol_struct_member(const exprt &src, const namespacet &ns) { return get_struct_rec(src, ns).id()==ID_symbol; @@ -466,12 +476,11 @@ Function: is_symbol_or_deref_struct_member Outputs: - Purpose: + Purpose: returns true for ((*ptr)|symbol)(.member)*, where + all members are struct members. \*******************************************************************/ -// Returns true for ((*ptr)|symbol)(.member)*, where -// all members are struct members. bool is_symbol_or_deref_struct_member(const exprt &src, const namespacet &ns) { exprt struct_op=get_struct_rec(src, ns); @@ -484,14 +493,13 @@ Function: is_deref_struct_member Inputs: - Outputs: + Outputs: returns true for (*ptr)(.member)*, where + all members are struct members. Purpose: \*******************************************************************/ -// Returns true for (*ptr)(.member)*, where -// all members are struct members. bool is_deref_struct_member(const exprt &src, const namespacet &ns) { return get_struct_rec(src, ns).id()==ID_dereference; diff --git a/src/ssa/ssa_object.h b/src/ssa/ssa_object.h index 43fa0da37..37f4b8c8e 100644 --- a/src/ssa/ssa_object.h +++ b/src/ssa/ssa_object.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_OBJECTS_H -#define CPROVER_SSA_OBJECTS_H +#ifndef CPROVER_2LS_SSA_SSA_OBJECT_H +#define CPROVER_2LS_SSA_SSA_OBJECT_H #include @@ -21,7 +21,7 @@ class ssa_objectt inline explicit identifiert(const irep_idt &_src):irep_idt(_src) { } - + inline identifiert() { } @@ -32,27 +32,27 @@ class ssa_objectt identifier(object_id_rec(expr, _ns)) { } - + inline const typet &type() const { return expr.type(); } - + inline const exprt &get_expr() const { return expr; } - + inline identifiert get_identifier() const { return identifier; } - + inline const symbol_exprt symbol_expr() const { return symbol_exprt(identifier, type()); } - + // The identifier is unique, so ordering and comparison // can be done on the identifier, which in turn is // an integer. @@ -65,20 +65,20 @@ class ssa_objectt { return identifier==other.identifier; } - + inline bool operator!=(const ssa_objectt &other) const { return identifier!=other.identifier; } - + // This is for use in if(...) tests, and // implements the 'safe bool' idiom. Shall be replaced // by C++11 explict conversion to bool one day. operator void *() const { - return identifier.empty()?0:(void *)&identifier; + return identifier.empty()?0:(void *)&identifier; // NOLINT(*) } - + exprt get_root_object() const { return get_root_object_rec(expr); @@ -98,7 +98,7 @@ class ssa_objectst // objects, plus categorization typedef std::set objectst; objectst objects, dirty_locals, clean_locals, globals, ptr_objects; - + // literals whose address is taken typedef std::set literalst; literalst literals; @@ -111,7 +111,7 @@ class ssa_objectst add_ptr_objects(ns); categorize_objects(goto_function, ns); } - + protected: void collect_objects( const goto_functionst::goto_functiont &, @@ -120,7 +120,7 @@ class ssa_objectst void categorize_objects( const goto_functionst::goto_functiont &, const namespacet &); - + void add_ptr_objects( const namespacet &); }; diff --git a/src/ssa/ssa_refiner.h b/src/ssa/ssa_refiner.h new file mode 100644 index 000000000..087dd2e91 --- /dev/null +++ b/src/ssa/ssa_refiner.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: SSA Refiner Base Class + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SSA_SSA_REFINER_H +#define CPROVER_SSA_SSA_REFINER_H + +#include + +class ssa_refinert:public messaget +{ + public: + virtual bool operator()() { assert(false); } + virtual unsigned get_unwind() { assert(false); } +}; + +#endif // CPROVER_SSA_SSA_REFINER_H diff --git a/src/ssa/ssa_refiner_monolithic.cpp b/src/ssa/ssa_refiner_monolithic.cpp new file mode 100644 index 000000000..6d168fc23 --- /dev/null +++ b/src/ssa/ssa_refiner_monolithic.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: SSA Refiner for Monolithic Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "ssa_refiner_monolithic.h" + +/*******************************************************************\ + +Function: ssa_refiner_monolithict::operator() + + Inputs: + + Outputs: + + Purpose: refine all + +\*******************************************************************/ + +bool ssa_refiner_monolithict::operator()() +{ + status() << "Unwinding (k=" << unwind << ")" << eom; + summary_db.mark_recompute_all(); // TODO: recompute only functions with loops + ssa_unwinder.unwind_all(unwind); + + return unwind++<=max_unwind; +} diff --git a/src/ssa/ssa_refiner_monolithic.h b/src/ssa/ssa_refiner_monolithic.h new file mode 100644 index 000000000..e14e23b01 --- /dev/null +++ b/src/ssa/ssa_refiner_monolithic.h @@ -0,0 +1,44 @@ +/*******************************************************************\ + +Module: SSA Refiner for Monolithic Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H +#define CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H + +#include "ssa_refiner.h" + +#include +#include "ssa_unwinder.h" + +class summary_dbt; +class ssa_unwindert; + +class ssa_refiner_monolithict:public ssa_refinert +{ +public: + ssa_refiner_monolithict( + summary_dbt &_summary_db, + ssa_unwindert &_ssa_unwinder, + unsigned _max_unwind): + summary_db(_summary_db), + ssa_unwinder(_ssa_unwinder), + max_unwind(_max_unwind), + unwind(0) + { + } + + virtual bool operator()(); + virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } + +protected: + summary_dbt &summary_db; + ssa_unwindert &ssa_unwinder; + const unsigned max_unwind; + unsigned unwind; +}; + +#endif // CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H diff --git a/src/ssa/ssa_refiner_selective.cpp b/src/ssa/ssa_refiner_selective.cpp new file mode 100644 index 000000000..ae16ad2bc --- /dev/null +++ b/src/ssa/ssa_refiner_selective.cpp @@ -0,0 +1,60 @@ +/*******************************************************************\ + +Module: SSA Refiner for selective unwinding and inlining + +Author: Peter Schrammel, Madhukar Kumar + +\*******************************************************************/ + +#include "ssa_refiner_selective.h" + +/*******************************************************************\ + +Function: ssa_refiner_selectivet::operator() + + Inputs: + + Outputs: + + Purpose: refine selectively according to the given reason + +\*******************************************************************/ + +bool ssa_refiner_selectivet::operator()() +{ + // unwind loops "selectively" (those that seem to be the "reason") + for(reasont::const_iterator it=reason.begin(); it!=reason.end(); ++it) + { + for(std::set::const_iterator l_it= + it->second.loops.begin(); + l_it!=it->second.loops.end(); l_it++) + { + unsigned new_unwind= + ssa_unwinder.unwind_loop_once_more(it->first, (*l_it)->location_number); + debug() << "Refining function " << it->first << ": unwinding loop at " + << (*l_it)->location_number << " (k=" << new_unwind << ")" << eom; + unwind=std::max(unwind, new_unwind); + } + } + + // inline functions "selectively" (those that seem to be the "reason") + for(reasont::const_iterator it=reason.begin(); it!=reason.end(); ++it) + { + for(std::set::const_iterator f_it= + it->second.functions.begin(); + f_it!=it->second.functions.end(); f_it++) + { + local_SSAt &SSA=ssa_db.get(it->first); + local_SSAt::nodest::iterator n_it=SSA.find_node(*f_it); + assert(n_it->function_calls.size()==1); + n_it->function_calls_inlined=true; + + irep_idt fname=to_symbol_expr(n_it->function_calls.begin() + ->function()).get_identifier(); + debug() << "Refining function " << it->first << ": inlining call to " + << fname << " at " << (*f_it)->location_number<< eom; + } + } + + return unwind<=max_unwind; +} diff --git a/src/ssa/ssa_refiner_selective.h b/src/ssa/ssa_refiner_selective.h new file mode 100644 index 000000000..2ffcd166c --- /dev/null +++ b/src/ssa/ssa_refiner_selective.h @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: SSA Refiner for selective unwinding and inlining + +Author: Peter Schrammel, Madhukar Kumar + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H +#define CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H + +#include "ssa_refiner.h" + +#include +#include "ssa_inliner.h" +#include "ssa_unwinder.h" + +class ssa_dbt; + +class ssa_refiner_selectivet:public ssa_refinert +{ +public: + struct reason_infot + { + // call_site; restriction: + // we assume that there is a single function call in an SSA node + typedef local_SSAt::locationt function_infot; + typedef local_SSAt::locationt loop_infot; + std::set functions; + std::set loops; + }; + + class reasont:public std::map + { + public: + void merge(const reasont &other) + { + for(const auto &reason : other) + { + reason_infot &r=(*this)[reason.first]; + r.functions.insert( + reason.second.functions.begin(), + reason.second.functions.end()); + r.loops.insert(reason.second.loops.begin(), reason.second.loops.end()); + } + } + }; + + ssa_refiner_selectivet( + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + unsigned _max_unwind, + ssa_inlinert &_ssa_inliner, + reasont &_reason): + ssa_db(_ssa_db), + ssa_unwinder(_ssa_unwinder), + max_unwind(_max_unwind), + ssa_inliner(_ssa_inliner), + unwind(0), + reason(_reason) + { + } + + virtual bool operator()(); + virtual unsigned get_unwind() { return unwind; } + + protected: + ssa_dbt &ssa_db; + ssa_unwindert &ssa_unwinder; + const unsigned max_unwind; + ssa_inlinert &ssa_inliner; + unsigned unwind; + reasont &reason; +}; + +#endif // CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H diff --git a/src/ssa/ssa_slicer.cpp b/src/ssa/ssa_slicer.cpp deleted file mode 100644 index 81dde9aa6..000000000 --- a/src/ssa/ssa_slicer.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include - -#include -#include - -void print_symbols(std::string msg, const find_symbols_sett &symbols) -{ - std::cout << msg << ": " << std::endl; - for(find_symbols_sett::const_iterator - s_it=symbols.begin(); s_it!=symbols.end(); s_it++) - std::cout << " " << *s_it << std::endl; - -} - -void ssa_slicert::operator()(std::list &dest, - const local_SSAt &src) -{ - //collect symbols in assertions - find_symbols_sett new_symbols; - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) - { - if(n_it->marked) continue; - if(n_it->assertions.empty()) continue; - for(local_SSAt::nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) - { - find_symbols(*a_it,new_symbols); - } - } -#ifdef DEBUG - print_symbols("symbols in assertions",new_symbols); -#endif - if(new_symbols.empty()) return; - - //build map symbol -> (definition, constraint set) - symbol_mapt symbol_map; - sliced_equalities = 0; - sliced_constraints = 0; - - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) - { - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); - e_it!=n_it->equalities.end(); - e_it++) - { - find_symbols_sett e_symbols; - find_symbols(e_it->lhs(),e_symbols); - - for(find_symbols_sett::const_iterator - s_it=e_symbols.begin(); s_it!=e_symbols.end(); s_it++) - { - symbol_map[*s_it]; - symbol_map[*s_it].def = e_it; - symbol_map[*s_it].def_node = n_it; - } - } - for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); - c_it!=n_it->constraints.end(); - c_it++) - { - find_symbols_sett c_symbols; - find_symbols(*c_it,c_symbols); - for(find_symbols_sett::const_iterator - s_it=c_symbols.begin(); s_it!=c_symbols.end(); s_it++) - { - if(symbol_map.find(*s_it)==symbol_map.end()) continue; - symbol_map[*s_it].constraints.push_back(constr_infot()); - constr_infot &constr_info = symbol_map[*s_it].constraints.back(); - constr_info.constr = c_it; - constr_info.node = n_it; - } - } - //statistics - if(!n_it->marked) - { - sliced_equalities += n_it->equalities.size(); - sliced_constraints += n_it->constraints.size(); - } - } - //statistics - std::cout << "Total equalities: " << sliced_equalities - << ", total constraints: " << sliced_constraints << std::endl; - - //compute backwards dependencies and add to formula on-the-fly - find_symbols_sett symbols_seen; - while(!new_symbols.empty()) - { - find_symbols_sett old_symbols = new_symbols; - -#ifdef DEBUG - print_symbols("new symbols",new_symbols); -#endif - - new_symbols.clear(); - for(find_symbols_sett::const_iterator - s_it=old_symbols.begin(); s_it!=old_symbols.end(); s_it++) - { - irep_idt sym = *s_it; - if(symbols_seen.find(sym)!=symbols_seen.end()) continue; - if(symbol_map.find(sym)==symbol_map.end()) - { - //handle loopback variables - //here it's getting a bit ugly - std::string sym_str = id2string(sym); - size_t pos1 = sym_str.find("#lb"); - if(pos1==std::string::npos) continue; - size_t pos2 = sym_str.find("%",pos1); - irep_idt basename = sym_str.substr(0,pos1); - std::string unwinder_suffix = ""; - if(pos2 - -#include "local_ssa.h" - -class ssa_slicert : public messaget -{ - public: - - void operator()(std::list &dest, - const local_SSAt &src); - - //statistics - unsigned sliced_equalities; - unsigned sliced_constraints; - - protected: - typedef struct - { - local_SSAt::nodet::constraintst::const_iterator constr; - local_SSAt::nodest::const_iterator node; - } constr_infot; - typedef std::list constraint_sett; - typedef struct - { - local_SSAt::nodet::equalitiest::const_iterator def; - local_SSAt::nodest::const_iterator def_node; - constraint_sett constraints; - } symbol_infot; - typedef hash_map_cont symbol_mapt; - -}; - -#endif diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index fe7f13cf5..1c8dead21 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -6,24 +6,23 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ -//#define DEBUG +// #define DEBUG #include #include "ssa_unwinder.h" -/***************************************************************************** - * - * Function : ssa_local_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : builds data structures for unwinder and transforms SSA (rename to %0) - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::init + + Inputs: + + Outputs: + + Purpose: builds data structures for unwinder and transforms SSA (rename to %0) + +\*******************************************************************/ void ssa_local_unwindert::init() { @@ -31,230 +30,234 @@ void ssa_local_unwindert::init() build_pre_post_map(); build_exit_conditions(); unwind(0); + compute_enable_expr(); #ifdef DEBUG SSA.output_verbose(std::cout); #endif } -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_loop_tree - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::build_loop_tree + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ void ssa_local_unwindert::build_loop_tree() { - //build loop tree structure - //Assumes that initially the nodes are in the same order as in the goto program + // build loop tree structure + // Assumes that initially the nodes are in the same order + // as in the goto program std::list loopheads; - local_SSAt::nodest::iterator n_it = --SSA.nodes.end(); - while(n_it != SSA.nodes.begin()) + local_SSAt::nodest::iterator n_it=--SSA.nodes.end(); + while(n_it!=SSA.nodes.begin()) { - //end of loop found - if (n_it->loophead != SSA.nodes.end()) + // end of loop found + if(n_it->loophead!=SSA.nodes.end()) { - loopt &loop = loops[n_it->loophead->location->location_number]; + loopt &loop=loops[n_it->loophead->location->location_number]; if(loopheads.empty()) { - loop.is_root = true; + loop.is_root=true; } - else + else { - loops[loopheads.back()->location->location_number].loop_nodes.push_back( - n_it->loophead->location->location_number); + loops[loopheads.back()->location->location_number]. + loop_nodes.push_back( + n_it->loophead->location->location_number); } loopheads.push_back(n_it->loophead); loop.body_nodes.push_front(*n_it); #ifdef DEBUG - std::cout << "pop " << n_it->location->location_number - << " for " << n_it->loophead->location->location_number << std::endl; + std::cout << "pop " << n_it->location->location_number + << " for " << n_it->loophead->location->location_number + << std::endl; #endif - //this test is ambiguous if the loop condition is true, + // this test is ambiguous if the loop condition is true, // but shouldn't have any consequences assert(n_it->location->is_backwards_goto()); - loop.is_dowhile = !n_it->location->guard.is_true(); + loop.is_dowhile=!n_it->location->guard.is_true(); SSA.nodes.erase(n_it--); } - //beginning of loop found - else if (n_it == loopheads.back()) + // beginning of loop found + else if(!loopheads.empty() && n_it==loopheads.back()) { #ifdef DEBUG - std::cout << "push " << n_it->location->location_number << std::endl; + std::cout << "push " << n_it->location->location_number << std::endl; #endif loops[n_it->location->location_number].body_nodes.push_front(*n_it); loopheads.pop_back(); - loops[n_it->location->location_number].body_nodes.back().loophead = - loops[n_it->location->location_number].body_nodes.begin(); + loops[n_it->location->location_number].body_nodes.back().loophead= + loops[n_it->location->location_number].body_nodes.begin(); SSA.nodes.erase(n_it--); } - //collect loop body nodes + // collect loop body nodes else if(!loopheads.empty()) { #ifdef DEBUG - std::cout << "add " << n_it->location->location_number - << " for " << loopheads.back()->location->location_number << std::endl; + std::cout << "add " << n_it->location->location_number + << " for " << loopheads.back()->location->location_number + << std::endl; #endif - loops[loopheads.back()->location->location_number].body_nodes.push_front(*n_it); + loops[loopheads.back()->location->location_number]. + body_nodes.push_front(*n_it); SSA.nodes.erase(n_it--); } - else + else --n_it; } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_pre_post_map - * - * Input : - * - * Output : - * - * Purpose : find variables at loop head and backedge - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::build_pre_post_map + + Inputs: + + Outputs: + + Purpose: find variables at loop head and backedge + +\*******************************************************************/ void ssa_local_unwindert::build_pre_post_map() { - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) { assert(!it->second.body_nodes.empty()); - const locationt &pre_loc = it->second.body_nodes.begin()->location; - const locationt &post_loc = (--it->second.body_nodes.end())->location; - SSA.current_location = pre_loc; //TODO: must go away + const locationt &pre_loc=it->second.body_nodes.begin()->location; + const locationt &post_loc=(--it->second.body_nodes.end())->location; + SSA.current_location=pre_loc; // TODO: must go away - //modified variables - const ssa_domaint::phi_nodest &phi_nodes = + // modified variables + const ssa_domaint::phi_nodest &phi_nodes= SSA.ssa_analysis[pre_loc].phi_nodes; - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) + for(local_SSAt::objectst::const_iterator o_it= + SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); o_it++) { - ssa_domaint::phi_nodest::const_iterator p_it = phi_nodes.find( - o_it->get_identifier()); + ssa_domaint::phi_nodest::const_iterator p_it=phi_nodes.find( + o_it->get_identifier()); - if (p_it == phi_nodes.end()) - continue; // object not modified in this loop + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop it->second.modified_vars.push_back(o_it->get_expr()); - symbol_exprt pre = SSA.name(*o_it, local_SSAt::PHI,pre_loc); - it->second.pre_post_map[pre] = SSA.read_rhs(*o_it, post_loc); + symbol_exprt pre=SSA.name(*o_it, local_SSAt::PHI, pre_loc); + it->second.pre_post_map[pre]=SSA.read_rhs(*o_it, post_loc); } } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_exit_conditions - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_conditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ void ssa_local_unwindert::build_exit_conditions() { - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) { - unsigned location_number_end = + unsigned location_number_end= it->second.body_nodes.back().location->location_number; #ifdef DEBUG std::cout << "end: " << location_number_end << std::endl; #endif for(local_SSAt::nodest::iterator n_it=it->second.body_nodes.begin(); - n_it!=it->second.body_nodes.end(); n_it++) + n_it!=it->second.body_nodes.end(); n_it++) { if(!n_it->location->is_goto()) - continue; - local_SSAt::nodest::iterator next = n_it; ++next; - SSA.current_location = n_it->location; //TODO: must go away + continue; + local_SSAt::nodest::iterator next=n_it; ++next; + SSA.current_location=n_it->location; // TODO: must go away if(n_it->location->get_target()->location_number>location_number_end) { - exprt g = SSA.guard_symbol(n_it->location); - exprt c = SSA.cond_symbol(n_it->location); - //need disjunction of all exit conditions + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + // need disjunction of all exit conditions // for each symbol name at exit location - for (exprt::operandst::const_iterator - s_it = it->second.modified_vars.begin(); - s_it != it->second.modified_vars.end(); s_it++) - { - exprt e = SSA.read_rhs(*s_it,n_it->location); - it->second.exit_map[e].push_back(and_exprt(g,c)); - } - it->second.exit_map[g].push_back(and_exprt(g,c)); - it->second.exit_map[c].push_back(and_exprt(g,c)); - //collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g,c)); - } + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back(and_exprt(g, c)); + } + it->second.exit_map[g].push_back(and_exprt(g, c)); + it->second.exit_map[c].push_back(and_exprt(g, c)); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, c)); + } } - else if(next==it->second.body_nodes.end() && - !n_it->location->guard.is_true()) - { //this happens in do-whiles - //ENHANCE: transform goto-program to make SSA uniform in this respect - exprt g = SSA.guard_symbol(n_it->location); - exprt c = SSA.cond_symbol(n_it->location); - for (exprt::operandst::const_iterator - s_it = it->second.modified_vars.begin(); - s_it != it->second.modified_vars.end(); s_it++) - { - exprt e = SSA.read_rhs(*s_it,n_it->location); - it->second.exit_map[e].push_back( - and_exprt(g,not_exprt(c))); - } - it->second.exit_map[g].push_back(and_exprt(g,not_exprt(c))); - it->second.exit_map[c].push_back(and_exprt(g,not_exprt(c))); - //collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g,not_exprt(c))); - } + else if(next==it->second.body_nodes.end() && + !n_it->location->guard.is_true()) + { // this happens in do-whiles + // ENHANCE: transform goto-program to make SSA uniform in this respect + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back( + and_exprt(g, not_exprt(c))); + } + it->second.exit_map[g].push_back(and_exprt(g, not_exprt(c))); + it->second.exit_map[c].push_back(and_exprt(g, not_exprt(c))); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, not_exprt(c))); + } } } } - //collect assertions for hoisting - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + // collect assertions for hoisting + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) { if(!it->second.is_root) continue; - for(loopt::assertion_hoisting_mapt::iterator - h_it = it->second.assertion_hoisting_map.begin(); - h_it != it->second.assertion_hoisting_map.end(); ++h_it) + for(loopt::assertion_hoisting_mapt::iterator + h_it=it->second.assertion_hoisting_map.begin(); + h_it!=it->second.assertion_hoisting_map.end(); ++h_it) { - local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - //find jump target in nodes - while(n_it->location != h_it->first && - n_it != SSA.nodes.end()) ++n_it; - local_SSAt::nodest::const_iterator prev = n_it; - for(; n_it != SSA.nodes.end(); ++n_it, ++prev) + local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + // find jump target in nodes + while(n_it->location!=h_it->first && + n_it!=SSA.nodes.end()) ++n_it; + local_SSAt::nodest::const_iterator prev=n_it; + for(; n_it!=SSA.nodes.end(); ++n_it, ++prev) { - //we collect only on top level, but not beyond loops + // we collect only on top level, but not beyond loops // so, if there is a gap in the nodes, we would jump over a loop - if(n_it!=prev && //this would fail in the first iteration otherwise - prev->location->location_number+1 != - n_it->location->location_number) - break; - if(n_it==prev) --prev; - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) - { - h_it->second.assertions.push_back(*a_it); - } + if(n_it!=prev && // this would fail in the first iteration otherwise + prev->location->location_number+1!= + n_it->location->location_number) + break; + if(n_it==prev) + --prev; + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + h_it->second.assertions.push_back(*a_it); + } } } } @@ -262,219 +265,342 @@ void ssa_local_unwindert::build_exit_conditions() /***************************************************************************** * - * Function : ssa_local_unwindert::unwind + * Function : ssa_local_unwindert::unwind_loop_at_location() * - * Input : + * Input : * - * Output : + * Output : * - * Purpose : unwind all loops up to k starting from previous unwindings + * Purpose : unwind the loop at the given location, up to k starting from + * previous unwindings * * *****************************************************************************/ +unsigned ssa_local_unwindert::unwind_loop_at_location(unsigned loc) +{ + unsigned k=loops.at(loc).current_unwinding+1; + unwind_loop_at_location(loc, k); + return k; +} + +void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) +{ + if(SSA.current_unwinding>=(long)k) + return; + + loopt &loop=loops[loc]; + loop.loop_enabling_exprs.push_back( + symbol_exprt( + "unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k), + bool_typet())); + + // TODO: just for exploratory integration, must go away + SSA.current_unwinding=k; + + // recursively unwind everything + SSA.current_unwindings.clear(); + + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + + if(it->first==loc) + unwind(it->second, k, false, false); // recursive + else + unwind(it->second, it->second.current_unwinding, false, true, k, loc); + // recursive + + assert(SSA.current_unwindings.empty()); + } + + // update current unwinding + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(it->first==loc) + it->second.current_unwinding=k; + } + + compute_enable_expr(); + return; +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: + + Outputs: + + Purpose: unwind all loops up to k starting from previous unwindings + +\*******************************************************************/ + void ssa_local_unwindert::unwind(unsigned k) { - if(SSA.current_unwinding >= k) + if(SSA.current_unwinding>=(long)k) return; - current_enabling_expr = - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), - bool_typet()); - SSA.enabling_exprs.push_back(current_enabling_expr); - SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away - //recursively unwind everything + current_enabling_expr= + symbol_exprt("unwind$"+id2string(fname)+"$enable"+i2string(k), + bool_typet()); + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + it->second.loop_enabling_exprs.push_back( + symbol_exprt( + "unwind$"+id2string(fname)+"$loc"+i2string(it->first)+ + "$enable"+i2string(k), + bool_typet())); + } + + // TODO: just for exploratory integration, must go away + SSA.current_unwinding=k; + + // recursively unwind everything SSA.current_unwindings.clear(); - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) { if(!it->second.is_root) continue; - unwind(it->second,k,false); //recursive + // unwind(it->second, k, false, false); // recursive + unwind(it->second, k, false, true, k, 0, true); // recursive assert(SSA.current_unwindings.empty()); } - //update current unwinding - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + // update current unwinding + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) { it->second.current_unwinding=k; } + + compute_enable_expr(); } -/***************************************************************************** - * - * Function : ssa_local_unwindert::unwind - * - * Input : - * - * Output : - * - * Purpose : unwind all instances of given loop up to k - starting from previous unwindings, - and recurse - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: -void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) + Outputs: + + Purpose: unwind all instances of given loop up to k + starting from previous unwindings, and recur + +\*******************************************************************/ + +void ssa_local_unwindert::unwind( + loopt &loop, + unsigned k, + bool is_new_parent, + bool propagate, + unsigned prop_unwind, + unsigned prop_loc, + bool propagate_all) { - odometert context = SSA.current_unwindings; + odometert context=SSA.current_unwindings; #ifdef DEBUG std::cout << "unwind(k=" << k << ", is_new_parent=" << is_new_parent << "), "; - std::cout << "context=" << SSA.odometer_to_string(context,context.size()) - << std::endl; + std::cout << "context=" << SSA.odometer_to_string(context, context.size()) + << std::endl; #endif SSA.increment_unwindings(1); - for(unsigned i = 0; i<=k; ++i) + for(long i=0; i<=k; ++i) { - //add new unwindings of this loop + // add new unwindings of this loop if(i>loop.current_unwinding || is_new_parent) { add_loop_body(loop); - //set new loop end node + // set new loop end node if(i==0) { - assert(loop.end_nodes.find(context) == loop.end_nodes.end()); - loop.end_nodes[context] = --SSA.nodes.end(); - assert(loop.end_nodes.find(context) != loop.end_nodes.end()); + assert(loop.end_nodes.find(context)==loop.end_nodes.end()); + loop.end_nodes[context]=--SSA.nodes.end(); + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); #ifdef DEBUG - std::cout << "end node for context " - << SSA.odometer_to_string(context,context.size()) << ": " - << loop.end_nodes[context]->location->location_number << " == " - << loop.body_nodes.back().location->location_number << std::endl; + std::cout << "end node for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.end_nodes[context]->location->location_number << "==" + << loop.body_nodes.back().location->location_number + << std::endl; #endif } if(i>0) { add_loop_connector(loop); } - //in while loops we can only hoist in iterations %2 and higher - // otherwise we would block the loop exit that is only + // in while loops we can only hoist in iterations %2 and higher + // otherwise we would block the loop exit that is only // reachable via !guardls - bool is_last = (loop.is_dowhile && i==0) || - (!loop.is_dowhile && (i==0 || i==1)); - add_assertions(loop,is_last); - add_hoisted_assertions(loop,is_last); + bool is_last=(loop.is_dowhile && i==0) || + (!loop.is_dowhile && (i==0 || i==1)); + add_assertions(loop, is_last); + add_hoisted_assertions(loop, is_last); } if(i==k) { add_loop_head(loop); - //update loop head + // update loop head #ifdef DEBUG - std::cout << "update loop head for context " - << SSA.odometer_to_string(context,context.size()) << ": " - << loop.body_nodes.begin()->location->location_number << std::endl; + std::cout << "update loop head for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.body_nodes.begin()->location->location_number + << std::endl; #endif - assert(loop.end_nodes.find(context) != loop.end_nodes.end()); - loop.end_nodes[context]->loophead = --SSA.nodes.end(); - assert(loop.end_nodes[context]->loophead->location->location_number == - loop.body_nodes.begin()->location->location_number); + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); + loop.end_nodes[context]->loophead=--SSA.nodes.end(); + assert( + loop.end_nodes[context]->loophead->location->location_number== + loop.body_nodes.begin()->location->location_number); } - //recurse into child loops - for(std::vector::iterator l_it = loop.loop_nodes.begin(); - l_it != loop.loop_nodes.end(); ++l_it) + // recurse into child loops + for(const auto &l : loop.loop_nodes) { #ifdef DEBUG std::cout << i << ">" << loop.current_unwinding << std::endl; #endif - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent); + if(propagate_all==true) + { + unwind( + loops[l], k, i>loop.current_unwinding || is_new_parent, false); + } + else + { + if(propagate==true) + { + // if this child loop is the desired loop then unwind k + // and do not propagate + // else unwind loop.current_unwinding and propagate + if(l==prop_loc) + { + unwind( + loops[l], k, i>loop.current_unwinding || is_new_parent, false); + } + else + { + unwind( + loops[l], + loops[l].current_unwinding, + i>loop.current_unwinding || is_new_parent, + true, + prop_unwind, + prop_loc); + } + } + else + { + unwind( + loops[l], + loops[l].current_unwinding, + i>loop.current_unwinding || is_new_parent, + false); + } + } } SSA.increment_unwindings(0); } SSA.increment_unwindings(-1); - add_exit_merges(loop,k); +#if 0 + std::cout << "calling add_exit_merge with k=" << k << "\n"; +#endif + add_exit_merges(loop, k); } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_body - * - * Input : - * - * Output : - * - * Purpose : duplicates the loop body for the current instance - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_body + + Inputs: + + Outputs: + + Purpose: duplicates the loop body for the current instance + +\*******************************************************************/ void ssa_local_unwindert::add_loop_body(loopt &loop) { - local_SSAt::nodest::iterator it = loop.body_nodes.begin(); - ++it; //skip loop head, we'll do that separately - for(; it != loop.body_nodes.end(); ++it) + local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + ++it; // skip loop head, we'll do that separately + for(; it!=loop.body_nodes.end(); ++it) { - if(it->equalities.empty() && + if(it->equalities.empty() && it->constraints.empty() && it->function_calls.empty()) continue; #ifdef DEBUG - std::cout << "add body node: " - << it->location->location_number << std::endl; + std::cout << "add body node: " + << it->location->location_number << std::endl; #endif - SSA.nodes.push_back(*it); //copy - local_SSAt::nodet &node = SSA.nodes.back(); - node.loophead = SSA.nodes.end(); - node.marked = false; - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) + SSA.nodes.push_back(*it); // copy + local_SSAt::nodet &node=SSA.nodes.back(); + node.loophead=SSA.nodes.end(); + node.marked=false; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) { SSA.rename(*e_it, node.location); } - for (local_SSAt::nodet::constraintst::iterator c_it = - node.constraints.begin(); c_it != node.constraints.end(); c_it++) + for(local_SSAt::nodet::constraintst::iterator c_it= + node.constraints.begin(); c_it!=node.constraints.end(); c_it++) { SSA.rename(*c_it, node.location); } - for (local_SSAt::nodet::function_callst::iterator f_it = - node.function_calls.begin(); - f_it != node.function_calls.end(); f_it++) + for(local_SSAt::nodet::function_callst::iterator f_it= + node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) { SSA.rename(*f_it, node.location); } - //will do assertions separately + // will do assertions separately node.assertions.clear(); } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_assertions - * - * Input : - * - * Output : - * - * Purpose : adds the new loop head - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_assertions + + Inputs: + + Outputs: + + Purpose: adds the assertions and assumptions + +\*******************************************************************/ void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) { - for(local_SSAt::nodest::iterator it = loop.body_nodes.begin(); - it != loop.body_nodes.end(); ++it) + for(local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + it!=loop.body_nodes.end(); ++it) { if(it->assertions.empty()) continue; - SSA.nodes.push_back(local_SSAt::nodet(it->location, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node = SSA.nodes.back(); - node.assertions = it->assertions; - SSA.current_location = node.location; //TODO: must go away - for (local_SSAt::nodet::assertionst::iterator a_it = - node.assertions.begin(); a_it != node.assertions.end(); a_it++) + SSA.nodes.push_back( + local_SSAt::nodet(it->location, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.assertions=it->assertions; + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::assertionst::iterator a_it= + node.assertions.begin(); a_it!=node.assertions.end(); a_it++) { SSA.rename(*a_it, node.location); - if(!is_last) //only add assumptions if we are not in %0 iteration + if(!is_last) // only add assumptions if we are not in %0 iteration { - if(is_kinduction) - node.constraints.push_back(*a_it); - else if(is_bmc) - { - //only add in base case - exprt gs = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, loop.body_nodes.back().location); - node.constraints.push_back(implies_exprt(not_exprt(gs),*a_it)); + if(is_kinduction) + node.constraints.push_back(*a_it); + else if(is_bmc) + { + // only add in base case + exprt gs= + SSA.name( + SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, + loop.body_nodes.back().location); + node.constraints.push_back(implies_exprt(not_exprt(gs), *a_it)); } } } @@ -485,28 +611,28 @@ void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_head - * - * Input : - * - * Output : - * - * Purpose : adds the new loop head - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_head + + Inputs: + + Outputs: + + Purpose: adds the new loop head + +\*******************************************************************/ void ssa_local_unwindert::add_loop_head(loopt &loop) { // new connecting loop head for the current instance // (enabled for this iteration) - SSA.nodes.push_back(loop.body_nodes.front()); //copy loop head + SSA.nodes.push_back(loop.body_nodes.front()); // copy loop head local_SSAt::nodet &node=SSA.nodes.back(); - node.marked = false; - node.enabling_expr = current_enabling_expr; - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) + node.marked=false; + node.enabling_expr=current_enabling_expr; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) { SSA.rename(*e_it, node.location); } @@ -518,288 +644,338 @@ void ssa_local_unwindert::add_loop_head(loopt &loop) #endif } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_connector - * - * Input : - * - * Output : - * - * Purpose : adds a connector to the previous iteration - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_connector + + Inputs: + + Outputs: + + Purpose: adds a connector to the previous iteration + +\*******************************************************************/ void ssa_local_unwindert::add_loop_connector(loopt &loop) { // connector to previous iteration (permanently added) const local_SSAt::nodet &orig_node=loop.body_nodes.front(); - SSA.nodes.push_back(local_SSAt::nodet(orig_node.location, - SSA.nodes.end())); //add new node + SSA.nodes.push_back( + local_SSAt::nodet(orig_node.location, SSA.nodes.end())); // add new node local_SSAt::nodet &node=SSA.nodes.back(); - SSA.current_location = node.location; //TODO: must go away - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - orig_node.equalities.begin(); - e_it != orig_node.equalities.end(); e_it++) + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + orig_node.equalities.begin(); + e_it!=orig_node.equalities.end(); e_it++) { if(e_it->rhs().id()==ID_if) { node.equalities.push_back(*e_it); - node.equalities.back().rhs() = - loop.pre_post_map[to_symbol_expr(e_it->lhs())]; + node.equalities.back().rhs()= + loop.pre_post_map[to_symbol_expr(e_it->lhs())]; SSA.rename(node.equalities.back().rhs(), node.location); SSA.decrement_unwindings(0); SSA.rename(node.equalities.back().lhs(), node.location); SSA.increment_unwindings(0); } else if(e_it->lhs().id()==ID_symbol && - !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), - "ssa::$guard")) - { //this is needed for while loops + !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), + "ssa::$guard")) + { // this is needed for while loops node.equalities.push_back(*e_it); SSA.decrement_unwindings(0); SSA.rename(node.equalities.back(), node.location); SSA.increment_unwindings(0); } } - //continuation guard and condition - exprt g_rhs = and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + // continuation guard and condition + exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); SSA.decrement_unwindings(0); - exprt g_lhs = SSA.guard_symbol(loop.body_nodes.begin()->location); + exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); SSA.increment_unwindings(0); - node.equalities.push_back(equal_exprt(g_lhs,g_rhs)); + node.equalities.push_back(equal_exprt(g_lhs, g_rhs)); } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_exit_merges - * - * Input : - * - * Output : - * - * Purpose : adds the merge connector for the loop exits for the current instance - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_exit_merges + + Inputs: + + Outputs: + + Purpose: adds the merge connector for the loop exits for the current instance + +\*******************************************************************/ void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) { SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, - SSA.nodes.end())); //add new node + SSA.nodes.end())); // add new node local_SSAt::nodet &node=SSA.nodes.back(); - node.enabling_expr = current_enabling_expr; + node.enabling_expr=current_enabling_expr; - for (loopt::exit_mapt::const_iterator x_it = loop.exit_map.begin(); - x_it != loop.exit_map.end(); x_it++) + for(loopt::exit_mapt::const_iterator x_it=loop.exit_map.begin(); + x_it!=loop.exit_map.end(); x_it++) { - node.equalities.push_back(build_exit_merge(x_it->first, - disjunction(x_it->second),k,node.location)); + node.equalities.push_back( + build_exit_merge( + x_it->first, disjunction(x_it->second), k, node.location)); } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_exit_merge - * - * Input : - * - * Output : - * - * Purpose : generates exit merge expression for a given expression - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_merge + + Inputs: + + Outputs: + + Purpose: generates exit merge expression for a given expression + +\*******************************************************************/ equal_exprt ssa_local_unwindert::build_exit_merge( exprt e, const exprt &exits, unsigned k, locationt loc) { - exprt re = e; + exprt re=e; SSA.increment_unwindings(1); - SSA.rename(re, loc); //%0 - for (unsigned i = 1; i <= k; i++) + SSA.rename(re, loc); // %0 + for(long i=1; i<=k; i++) { SSA.increment_unwindings(0); - exprt cond_expr = exits; + exprt cond_expr=exits; SSA.rename(cond_expr, loc); - exprt true_expr = e; + exprt true_expr=e; SSA.rename(true_expr, loc); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); + exprt false_expr=re; + re=if_exprt(cond_expr, true_expr, false_expr); } SSA.increment_unwindings(-1); - SSA.rename(e, loc); //lhs - return equal_exprt(e,re); + SSA.rename(e, loc); // lhs + return equal_exprt(e, re); } -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_hoisted_assertions - * - * Input : - * - * Output : - * - * Purpose : adds the assumptions for hoisted assertions - for the current instance - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::add_hoisted_assertions + + Inputs: + + Outputs: + + Purpose: adds the assumptions for hoisted assertions + for the current instance + +\*******************************************************************/ void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) { - for(loopt::assertion_hoisting_mapt::const_iterator - it = loop.assertion_hoisting_map.begin(); - it != loop.assertion_hoisting_map.end(); ++it) + for(loopt::assertion_hoisting_mapt::const_iterator + it=loop.assertion_hoisting_map.begin(); + it!=loop.assertion_hoisting_map.end(); ++it) { - if(!is_last //only add assumptions if we are not in %0 iteration - && is_kinduction && !it->second.assertions.empty()) - { - exprt e = disjunction(it->second.exit_conditions); - SSA.rename(e, loop.body_nodes.begin()->location); - SSA.nodes.push_back(local_SSAt::nodet(it->first, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node = SSA.nodes.back(); - node.constraints.push_back( - implies_exprt(e,conjunction(it->second.assertions))); + if(!is_last // only add assumptions if we are not in %0 iteration + && is_kinduction && !it->second.assertions.empty()) + { + exprt e=disjunction(it->second.exit_conditions); + SSA.rename(e, loop.body_nodes.begin()->location); + SSA.nodes.push_back( + local_SSAt::nodet(it->first, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.constraints.push_back( + implies_exprt(e, conjunction(it->second.assertions))); #ifdef DEBUG - std::cout << "adding hoisted assumption: " - << from_expr(SSA.ns, "", node.constraints.back()) - << std::endl; + std::cout << "adding hoisted assumption: " + << from_expr(SSA.ns, "", node.constraints.back()) + << std::endl; #endif - } + } } } /*****************************************************************************\ * - * Function : ssa_local_unwindert::loop_continuation_conditions + * Function : ssa_local_unwindert::compute_enable_expr * * Input : * * Output : * - * Purpose : return loop continuation conditions for all loops in this function + * Purpose : updates the enable_expr * *****************************************************************************/ +void ssa_local_unwindert::compute_enable_expr() +{ + SSA.enabling_exprs.clear(); + for(loop_mapt::const_iterator it=loops.begin(); it!=loops.end(); ++it) + { + for(exprt::operandst::const_iterator + e_it=((it->second).loop_enabling_exprs).begin(); + e_it!=((it->second).loop_enabling_exprs).end(); e_it++) + { + exprt::operandst::const_iterator lh=e_it; lh++; + if(lh!=((it->second).loop_enabling_exprs).end()) + SSA.enabling_exprs.push_back(not_exprt(*e_it)); + else + SSA.enabling_exprs.push_back(*e_it); + } + } +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: return loop continuation conditions for all loops in this function + +\*******************************************************************/ + void ssa_local_unwindert::loop_continuation_conditions( exprt::operandst& loop_cont) const { SSA.current_unwindings.clear(); - for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) + for(const auto &l : loops) { - if(!it->second.is_root) + if(l.second.is_root) continue; - loop_continuation_conditions(it->second,loop_cont); //recursive + loop_continuation_conditions(l.second, loop_cont); // recursive assert(SSA.current_unwindings.empty()); } } -/***************************************************************************** - * - * Function : ssa_local_unwindert::get_continuation_condition - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: return loop continuation conditions for all instances + of the given loop in this function + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + const locationt& loop_id, + exprt::operandst &loop_cont) const +{ + if(loops.empty()) // ignore silently, TBD + return; + + const loopt &loop=loops.at(loop_id->location_number); + loop_cont.insert(loop_cont.end(), + loop.current_continuation_conditions.begin(), + loop.current_continuation_conditions.end()); +} +/*******************************************************************\ + +Function: ssa_local_unwindert::get_continuation_condition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const { - SSA.current_location = loop.body_nodes.back().location; //TODO: must go away + SSA.current_location=loop.body_nodes.back().location; // TODO: must go away return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + SSA.cond_symbol(loop.body_nodes.back().location)); } -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : recursively construct loop continuation conditions - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: recursively construct loop continuation conditions + +\*******************************************************************/ void ssa_local_unwindert::loop_continuation_conditions( const loopt& loop, exprt::operandst& loop_cont) const { SSA.increment_unwindings(1); - loop_cont.push_back(get_continuation_condition(loop)); //%0 - for(unsigned i=0; i<=loop.current_unwinding; ++i) + loop_cont.push_back(get_continuation_condition(loop)); // %0 + for(long i=0; i<=loop.current_unwinding; ++i) { - //recurse into child loops - for(std::vector::const_iterator l_it = loop.loop_nodes.begin(); - l_it != loop.loop_nodes.end(); ++l_it) + // recur into child loops + for(const auto &l : loop.loop_nodes) { - loop_continuation_conditions(loops.at(*l_it),loop_cont); + loop_continuation_conditions(loops.at(l), loop_cont); } SSA.increment_unwindings(0); } SSA.increment_unwindings(-1); } +/*******************************************************************\ -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::unwinder_rename - * - * Input : var, node - * - * Output : var is returned with a suffix that reflects the current unwinding - * with the context taken from the node - * - * E.g. - * - * node must look like - * - * cond"somesuffix" == TRUE - * - * e.g. cond%1%2%5%0 == TRUE - * - * and variable might be guard#ls25 - * - * if the current_unwinding is 6 - * - * the variable should be converted to guard#ls25%1%2%5%5 - * - * Note that if current_unwinding is X then suffixes can have at most - * X-1 in its parts - * - *****************************************************************************/ +Function: ssa_local_unwindert::unwinder_rename + + Inputs: var, node + + Outputs: var is returned with a suffix that reflects the current unwinding + with the context taken from the node + + Purpose: E.g. node must look like + cond"somesuffix"==TRUE, e.g. cond%1%2%5%0==TRUE + and variable might be guard#ls25 + if the current_unwinding is 6 + the variable should be converted to guard#ls25%1%2%5%5 + + Note that if current_unwinding is X then suffixes can have at most + X-1 in its parts -void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const +\*******************************************************************/ + +void ssa_local_unwindert::unwinder_rename( + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const { - //TODO: replace this by SSA.rename + // TODO: replace this by SSA.rename // this requires odometer information in the nodes - - //only to be called for backedge nodes - //This is very dirty hack :-( - if(SSA.current_unwinding<0) return; + + // only to be called for backedge nodes + // This is very dirty hack + if(SSA.current_unwinding<0) + return; + assert(node.equalities.size()>=1); - //copy suffix from equality lhs to var - std::string id = + // copy suffix from equality lhs to var + std::string id= id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); - size_t pos = id.find_first_of("%"); - if(pos==std::string::npos) return; - size_t pos1 = id.find_last_of("%"); + size_t pos=id.find_first_of("%"); + if(pos==std::string::npos) + return; + size_t pos1=id.find_last_of("%"); std::string suffix; - unsigned unwinding = pre ? SSA.current_unwinding : 0; + unsigned unwinding=pre ? SSA.current_unwinding : 0; if(pos==pos1) { - suffix = "%"+i2string(unwinding); + suffix="%"+i2string(unwinding); } else { - suffix = id.substr(pos,pos1-pos); - suffix += "%"+i2string(unwinding); + suffix=id.substr(pos, pos1-pos); + suffix+="%"+i2string(unwinding); } var.set_identifier(id2string(var.get_identifier())+suffix); @@ -808,94 +984,160 @@ void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, #endif } -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind - * - * Input : fname - name of the goto-function to be unwound, k - unwinding depth - * - * Output : false - if id does not correspond to any goto-function in the - * unwinder_map - * - * Purpose : incrementally unwind a function 'id' up to depth k. Initializer - * must have been invoked before calling this function - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind_loop_alone + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::unwind_loop_alone( + const irep_idt fname, + unsigned loc, + unsigned int k) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + it->second.unwind_loop_at_location(loc, k); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind_loop_once_more + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned ssa_unwindert::unwind_loop_once_more( + const irep_idt fname, + unsigned loc) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + return it->second.unwind_loop_at_location(loc); +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::find_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_local_unwindert::find_loop( + unsigned location_number, const loopt *&loop) const +{ + loop_mapt::const_iterator it=loops.find(location_number); + if(it!=loops.end()) + { + loop=&it->second; + return true; + } + return false; +} + +/*******************************************************************\ + +Function: ssa_unwindert::unwind + + Inputs: fname-name of the goto-function to be unwound, k-unwinding depth + + Outputs: false-if id does not correspond to any goto-function in the + unwinder_map + + Purpose: incrementally unwind a function 'id' up to depth k. Initializer + must have been invoked before calling this function + +\*******************************************************************/ void ssa_unwindert::unwind(const irep_idt fname, unsigned int k) { assert(is_initialized); - unwinder_mapt::iterator it = unwinder_map.find(fname); - assert(it != unwinder_map.end()); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); it->second.unwind(k); } -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind_all - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_unwindert::unwind_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ void ssa_unwindert::unwind_all(unsigned int k) { assert(is_initialized); - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - it->second.unwind(k); - } + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.unwind(k); } -/*****************************************************************************\ - * - * Function : ssa_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : Initialize unwinder_map by computing hierarchical tree_loopnodet - * for every goto-function - * Set is_initialized to true. Initializer must be called before - * unwind funcitions are called. - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_unwindert::init + + Inputs: + + Outputs: + + Purpose: Initialize unwinder_map by computing hierarchical tree_loopnodet + for every goto-function + Set is_initialized to true. Initializer must be called before + unwind funcitions are called. + +\*******************************************************************/ + void ssa_unwindert::init(bool is_kinduction, bool is_bmc) { - ssa_dbt::functionst& funcs = ssa_db.functions(); - for (ssa_dbt::functionst::iterator it = funcs.begin(); - it != funcs.end(); it++) + ssa_dbt::functionst& funcs=ssa_db.functions(); + for(auto &f : funcs) { - unwinder_map.insert(unwinder_pairt(it->first, - ssa_local_unwindert(it->first, (*(it->second)), - is_kinduction, is_bmc))); + unwinder_map.insert( + unwinder_pairt( + f.first, + ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); } } -/*****************************************************************************\ - * - * Function : ssa_unwindert::init_localunwinders - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ +/*******************************************************************\ + +Function: ssa_unwindert::init_localunwinders + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ void ssa_unwindert::init_localunwinders() { - for(unwinder_mapt::iterator it=unwinder_map.begin(); - it!=unwinder_map.end();it++) - { - it->second.init(); - } - is_initialized = true; + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.init(); + is_initialized=true; } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 830efd357..57a02c007 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -6,131 +6,177 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ -#ifndef CPROVER_DELTACHECK_SSA_UNWINDER_H -#define CPROVER_DELTACHECK_SSA_UNWINDER_H +#ifndef CPROVER_2LS_SSA_SSA_UNWINDER_H +#define CPROVER_2LS_SSA_SSA_UNWINDER_H #include "unwindable_local_ssa.h" -#include "../summarizer/ssa_db.h" +#include "ssa_db.h" class ssa_local_unwindert { public: typedef local_SSAt::locationt locationt; typedef unwindable_local_SSAt::odometert odometert; - - ssa_local_unwindert(const irep_idt _fname, unwindable_local_SSAt& _SSA, - bool _is_kinduction, bool _is_bmc) - : - fname(_fname),SSA(_SSA), is_kinduction(_is_kinduction), is_bmc(_is_bmc) - {} + + ssa_local_unwindert( + const irep_idt _fname, + unwindable_local_SSAt& _SSA, + bool _is_kinduction, + bool _is_bmc): + fname(_fname), + SSA(_SSA), + is_kinduction(_is_kinduction), + is_bmc(_is_bmc) + { + } void init(); + void unwind_loop_at_location(unsigned loc, unsigned k); + unsigned unwind_loop_at_location(unsigned loc); void unwind(unsigned k); - //TODO: not yet sure how to do that -/* void unwind(locationt loop_head_loc, unsigned k) - { unwind(loops[loop_head_loc],k); } */ - - //TOOD: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related - void loop_continuation_conditions(exprt::operandst& loop_cont) const; +#if 0 + // TODO: not yet sure how to do that + void unwind(locationt loop_head_loc, unsigned k) + { + unwind(loops[loop_head_loc], k); + } +#endif - //TODO: these two should be possible with unwindable_local_ssa facilities - //exprt rename_invariant(const exprt& inv_in) const; - //void unwinder_rename(symbol_exprt &var,const local_SSAt::nodet &node, bool pre) const; + // TODO: this should be loop specific in future, + // maybe move to unwindable_local_ssa as it is not really unwinder related + void compute_enable_expr(); + void loop_continuation_conditions(exprt::operandst &loop_cont) const; + void loop_continuation_conditions( + const locationt& loop_id, + exprt::operandst &loop_cont) const; + +#if 0 + // TODO: these two should be possible with unwindable_local_ssa facilities + exprt rename_invariant(const exprt& inv_in) const; + void unwinder_rename( + symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; +#endif - //TODO: this must go away, should use SSA.rename instead - void unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const; -protected: - const irep_idt fname; - unwindable_local_SSAt& SSA; - bool is_kinduction,is_bmc; - symbol_exprt current_enabling_expr; //TODO must become loop-specific + // TODO: this must go away, should use SSA.rename instead + void unwinder_rename( + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; - class loopt //loop tree + class loopt // loop tree { public: - loopt() - : - is_dowhile(false), - is_root(false), - current_unwinding(-1) - {} + loopt(): + is_dowhile(false), + is_root(false), + current_unwinding(-1) + { + } local_SSAt::nodest body_nodes; - //pointer to loop end nodes in SSA for updating current loop head - std::map end_nodes; - std::vector loop_nodes; //child loops + // pointer to loop end nodes in SSA for updating current loop head + std::map end_nodes; + std::vector loop_nodes; // child loops bool is_dowhile; bool is_root; long current_unwinding; - typedef std::map exit_mapt; + + // to have an enabling_expr and current_unwindings (odometert) + exprt::operandst loop_enabling_exprs; + + exprt::operandst current_continuation_conditions; + + typedef std::map exit_mapt; exit_mapt exit_map; - std::map pre_post_map; + std::map pre_post_map; std::vector modified_vars; - //for assertion hoisting - typedef struct { + // for assertion hoisting + typedef struct + { exprt::operandst exit_conditions; exprt::operandst assertions; } assertions_after_loopt; - typedef std::map assertion_hoisting_mapt; + typedef std::map assertion_hoisting_mapt; assertion_hoisting_mapt assertion_hoisting_map; - }; - //use location numbers as indices, as target addresses make + bool find_loop(unsigned location_number, const loopt *&loop) const; + +protected: + const irep_idt fname; + unwindable_local_SSAt &SSA; + bool is_kinduction, is_bmc; + symbol_exprt current_enabling_expr; // TODO must become loop-specific + + // use location numbers as indices, as target addresses make // things non-deterministic - typedef std::map loop_mapt; + typedef std::map loop_mapt; loop_mapt loops; void build_loop_tree(); void build_pre_post_map(); void build_exit_conditions(); - void unwind(loopt &loop, unsigned k, bool is_new_parent); + void unwind( + loopt &loop, + unsigned k, + bool is_new_parent, + bool propagate=false, + unsigned prop_unwind=0, + unsigned prop_loc=0, + bool propagate_all=false); exprt get_continuation_condition(const loopt& loop) const; - void loop_continuation_conditions(const loopt& loop, - exprt::operandst &loop_cont) const; - + void loop_continuation_conditions( + const loopt& loop, exprt::operandst &loop_cont) const; + void add_loop_body(loopt &loop); void add_assertions(loopt &loop, bool is_last); void add_loop_head(loopt &loop); void add_loop_connector(loopt &loop); void add_exit_merges(loopt &loop, unsigned k); - equal_exprt build_exit_merge(exprt e, const exprt &exits, - unsigned k, locationt loc); + equal_exprt build_exit_merge( + exprt e, const exprt &exits, unsigned k, locationt loc); void add_hoisted_assertions(loopt &loop, bool is_last); }; -class ssa_unwindert : public messaget +class ssa_unwindert:public messaget { - public: - typedef std::map unwinder_mapt; - typedef std::pair unwinder_pairt; + typedef std::map unwinder_mapt; + typedef std::pair unwinder_pairt; - ssa_unwindert(ssa_dbt& _db) - : - ssa_db(_db), is_initialized(false) - {} + explicit ssa_unwindert(ssa_dbt& _db): + ssa_db(_db), + is_initialized(false) + { + } void init(bool is_kinduction, bool is_bmc); void init_localunwinders(); + void unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned k); + unsigned unwind_loop_once_more(const irep_idt fname, unsigned loc); void unwind(const irep_idt fname, unsigned k); void unwind_all(unsigned k); ssa_local_unwindert &get(const irep_idt& fname) - { return unwinder_map.at(fname); } + { + return unwinder_map.at(fname); + } + + const ssa_local_unwindert &get(const irep_idt& fname) const + { + return unwinder_map.at(fname); + } protected: - ssa_dbt& ssa_db; + ssa_dbt &ssa_db; bool is_initialized; unwinder_mapt unwinder_map; - }; -#endif +#endif // CPROVER_2LS_SSA_SSA_UNWINDER_H diff --git a/src/ssa/ssa_unwinder_old.cpp b/src/ssa/ssa_unwinder_old.cpp deleted file mode 100644 index 25fe0d2ac..000000000 --- a/src/ssa/ssa_unwinder_old.cpp +++ /dev/null @@ -1,1556 +0,0 @@ -/******************************************************************* - Module: SSA Unwinder - - Author: Saurabh Joshi - - \*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include "ssa_unwinder.h" - - -void ssa_local_unwindert::set_return_var(const irep_idt& id) -{ - return_var="c::"+id2string(id)+"#return_value"; -} -std::string ssa_local_unwindert::keep_first_two_hash(const std::string& str) const -{ - std::size_t pos = str.find('#'); - if(pos==std::string::npos) return str; - pos=str.find('#',pos+1); - if(pos==std::string::npos) return str; - return str.substr(0,pos); -} -void ssa_local_unwindert::dissect_loop_suffix(const irep_idt& id, - irep_idt& before_suffix,std::list& iterations,bool baseonly) const -{ - std::string s = id2string(id); - - std::size_t pos=s.find_first_of("%"); - if(pos==std::string::npos) { return; } - - before_suffix=s.substr(0,pos); - if(baseonly) return; - std::size_t pos1; - - do - { - pos1=s.find_first_of("%",pos+1); - if(pos1==std::string::npos) continue; - // if(pos1==pos+1) {assert(false&& "Ill formed loop suffix");} - //TODO use safe string to unsigned - - iterations.push_back(string2integer(s.substr(pos+1,pos1-(pos+1)),10).to_ulong()); - pos=pos1; - - }while(pos1!=std::string::npos); - - //if(pos==(s.length()-1)) {assert(false && "Ill-formed loop suffix");} - - iterations.push_back(string2integer(s.substr(pos+1)).to_ulong()); - -} -/***************************************************************************** - * - * Function : ssa_local_unwindert::get_base_name - * - * Input : id - symbol name - * - * Output : irep_id - a symbol name which is stripped off of all suffixes - * beginnign with the first # - * - *****************************************************************************/ -irep_idt ssa_local_unwindert::get_base_name(const irep_idt& id) -{ - std::string s = id2string(id); - std::size_t pos = s.find("#"); - if(pos==std::string::npos) {return id;} - std::string s1=s.substr(0,pos); - return irep_idt(s1); -} -void ssa_local_unwindert::is_void_func() -{ - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) { - - if(as_string(o_it->get_identifier()).find(RETVAR)){ isvoid=false; return;} - } - isvoid=true; - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::ssa_local_unwindert - * - * Input : _SSA - a reference to an object of type local_SSAt - * - * Output : - * - * Purpose : This just initialises the reference and does nothing else. A - * separate initialier init() is provided to populate the - * hierarchical loop - * data structure - * - * - *****************************************************************************/ -ssa_local_unwindert::ssa_local_unwindert(local_SSAt& _SSA,bool k_induct, - bool _ibmc):isvoid(true), SSA(_SSA), - current_unwinding(0),prev_unwinding(std::numeric_limits::max()), - is_initialized(false),is_kinduction(k_induct), - is_ibmc(_ibmc){ } -/******************************************************************* - Struct: compare_node_iterators - - Inputs: - - Outputs: - - Purpose: a function object to pass as a comparator for std::set - to enable us to order iterators. It takes the address of the - object being pointed to by iterators and compared them as - unsigned long - - \*******************************************************************/ -bool compare_node_iteratorst::operator()(const local_SSAt::nodest::iterator& a, - const local_SSAt::nodest::iterator& b) const { - return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); -} -bool compare_node_iteratorst::operator()(const local_SSAt::nodest::const_iterator& a, - const local_SSAt::nodest::const_iterator& b) const { - return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); -} -/*****************************************************************************\ - * Function : ssa_local_unwindert::init - * - * Inputs : None - * - * Outputs : None - * - * Purpose : from local_SSA, construct a hierarchical tree_loopnodet rooted at - * root_node. loophead of a loop is always stored as the first node in - * body_nodes. A backedge node is always the last node in body_nodes. - * A nested loop is stored as a tree_loopnodet in loop_nodes - * - *****************************************************************************/ - -void ssa_local_unwindert::init() { - std::set loopheads; - - loopheads.clear(); - for (local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) { - //continue until a back-edge is found - if (n_it->loophead == SSA.nodes.end()) - continue; - // all the loop-head nodes must be inserted - assert(loopheads.insert(n_it->loophead).second); - } - - if (loopheads.empty()) { - is_initialized = true; - loopless = true; - return; - } - - loopless = false; - tree_loopnodet* current_node = NULL; - - std::list current_stack; - current_stack.push_back(&root_node); - current_node = current_stack.back(); - for (local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) { - if (loopheads.find(n_it) != loopheads.end()) { - //we've found a loop-head, so create a - //tree_loopnodet for the nested loop - current_node = new tree_loopnodet(); - current_stack.push_back(current_node); - current_node = current_stack.back(); - // ASSUME : first node in body_nodes is always the - // loop-head of the loop except root_node -#if 0 - //get the guard for the loophead to be reachable - //get the loop condition - current_node->entry_guard=SSA.guard_symbol(n_it->location); - current_node->cond_expr=SSA.cond_symbol(n_it->location); -#endif - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked = false; - -#if 0 - { - //compute pre_post_exprs for the current loop - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[n_it->location].phi_nodes; - - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - ssa_domaint::phi_nodest::const_iterator p_it = - phi_nodes.find(o_it->get_identifier()); - - if(p_it==phi_nodes.end()) continue; // object not modified in this loop - - symbol_exprt pre = - SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - symbol_exprt post = SSA.read_rhs(*o_it, n_it->location); - - current_node->pre_post_exprs[pre] = post; - std::cout << pre << " ---" << post << std::endl; - } - } -#endif - } else if (n_it->loophead != SSA.nodes.end()) { - //we've found the end of the loop so move - // up the stack - - //ASSUME : the last node in the body_nodes - // of a loop is always the back-edge node - //copy assertions after the loop for assertion hoisting - current_node->assertions_after_loop = n_it->loophead->assertions_after_loop; - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked = false; - - //reset the back-edge - current_node->body_nodes.rbegin()->loophead=SSA.nodes.end(); - - { - local_SSAt::nodet loophead = current_node->body_nodes.front(); - loophead.marked = false; - //compute pre_post_exprs for the current loop - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[loophead.location].phi_nodes; - - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) { - ssa_domaint::phi_nodest::const_iterator p_it = phi_nodes.find( - o_it->get_identifier()); - - if (p_it == phi_nodes.end()) - continue; // object not modified in this loop - //capture which variables are modified - //this will be used during renaming - current_node->vars_modified.insert(o_it->get_identifier()); - - - symbol_exprt pre = SSA.name(*o_it, local_SSAt::LOOP_BACK, - n_it->location); - symbol_exprt post = SSA.read_rhs(*o_it, n_it->location); - - current_node->pre_post_exprs[pre] = post; - - } - } - -#if 0 - //get the guard at the end of the loop body - //this guard needs to be fed to the next iteration - current_node->exit_guard=SSA.guard_symbol(n_it->location); -#endif - assert(!current_stack.empty()); - //assert would fail for unstructured program - current_stack.pop_back(); - - tree_loopnodet* new_current_node = current_stack.back(); - //hope push_back does a copy by value - yes it does - new_current_node->loop_nodes.push_back(*current_node); - - delete current_node; - - - current_node = new_current_node; - } else { - //a normal node belongs to body_nodes of the current loop - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked=false; - } - - } - //only the top level body_nodes are required - // all other required nodes will be inserted during unwinding - // so avoid duplicates by removing them from SSA.nodes - SSA.nodes.clear(); - SSA.nodes.insert(SSA.nodes.begin(), root_node.body_nodes.begin(), - root_node.body_nodes.end()); - - populate_parents(); - is_void_func(); - - if(!isvoid) - { - populate_return_val_mod(); - } - - //populate connector for every loop - for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); - lit!=root_node.loop_nodes.end();lit++) - { - populate_connectors(*lit); - } - - put_varmod_in_parent(); -#if 0 - //pointers of tree_loopnodet are stable now and they must not be - //modified beyond this point. Now populate "parent" field of - //every node - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - it->parent = current_node; -#if 1 - // if a variable is modified in child loop then consider it modified - //for the current loop for the purpose of renaming - //NOTE : this code only looks at the child. Not sure if you should look at - // all the descendents for the purpose of renaming - current_node->vars_modified.insert(it->vars_modified.begin(),it->vars_modified.end()); -#endif - worklist.push_back(&(*it)); - } - - } - -#endif - is_initialized=true; - -} -void ssa_local_unwindert::propagate_varmod_to_ancestors(const irep_idt& id,tree_loopnodet* current_loop) -{ - current_loop->vars_modified.insert(id); - if(current_loop->parent!=NULL && current_loop->parent!=&root_node) - { - propagate_varmod_to_ancestors(id,current_loop->parent); - } -} - -void ssa_local_unwindert::populate_parents() -{ - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - it->parent = current_node; - - worklist.push_back(&(*it)); - } - - } -} -void ssa_local_unwindert::put_varmod_in_parent() -{ - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - - - // if a variable is modified in child loop then consider it modified - //for the current loop for the purpose of renaming - //NOTE : this code only looks at the child. Not sure if you should look at - // all the descendents for the purpose of renaming - current_node->vars_modified.insert(it->vars_modified.begin(),it->vars_modified.end()); - worklist.push_back(&(*it)); - } - - } -} -void ssa_local_unwindert::populate_return_val_mod() -{ - std::list worklist; - for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); - lit!=root_node.loop_nodes.end();lit++) - { - worklist.push_back(&(*lit)); - } - -while(!worklist.empty()) -{ - tree_loopnodet* current_loop=worklist.back(); - worklist.pop_back(); - for(local_SSAt::nodest::iterator nit=current_loop->body_nodes.begin(); - nit!=current_loop->body_nodes.end();nit++) - { - for(local_SSAt::nodet::equalitiest::iterator eit=nit->equalities.begin(); - eit!=nit->equalities.end();eit++) - { - - if(eit->lhs().id()==ID_symbol) - { - - symbol_exprt sym_e=to_symbol_expr(eit->lhs()); - irep_idt sym_id=sym_e.get_identifier(); - std::string s = as_string(sym_id); - - std::size_t pos=s.find(RETVAR); - if(pos!=std::string::npos) - { - - irep_idt id= keep_first_two_hash(s); - propagate_varmod_to_ancestors(id,current_loop); - current_loop->return_nodes.insert(nit); - } - } - } - } - for(loop_nodest::iterator lit=current_loop->loop_nodes.begin(); - lit!=current_loop->loop_nodes.end();lit++) - { - worklist.push_back(&(*lit)); - } -} - -} -void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) -{ - typedef std::map varobj_mapt; - varobj_mapt varobj_map; - body_nodest::const_iterator it=current_loop.body_nodes.begin(); - - body_nodest::const_reverse_iterator lit = current_loop.body_nodes.rbegin(); - - std::vector exit_conditions; - - unsigned int end_location = lit->location->location_number; - - - for(local_SSAt::nodet::equalitiest::const_iterator eqit=lit->equalities.begin(); - eqit!=lit->equalities.end();eqit++) - { - if(eqit->lhs()==SSA.cond_symbol(lit->location)) - { - if(!eqit->rhs().is_true()) - { - current_loop.is_dowhile=true; - - } - break; - } - } - exprt cond_e; - exprt guard_e; - exprt loop_continue_e; - if(!current_loop.is_dowhile) - { - cond_e = SSA.cond_symbol(it->location); - guard_e=SSA.guard_symbol(it->location); - exit_conditions.push_back(cond_e); - //either you reached head of the loop and exit - //or you have not reached the end of the loop - //this will happen only for while. What about dowhile? - loop_continue_e = and_exprt(cond_e,guard_e); - loop_continue_e=or_exprt(loop_continue_e,not_exprt(SSA.guard_symbol(lit->location))); - } - else - { - cond_e = SSA.cond_symbol(lit->location); - guard_e=SSA.guard_symbol(lit->location); - exprt not_cond_e=not_exprt(cond_e); - exit_conditions.push_back(not_cond_e); - loop_continue_e=and_exprt(not_cond_e,guard_e); - } - - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) - { - std::set::const_iterator fit = current_loop.vars_modified.find(o_it->get_identifier()); - if(fit==current_loop.vars_modified.end()) continue; - - varobj_map[*fit]=o_it; - //though #return_value is added into vars_modified - //we don't want PHI connectors for them - if(as_string(*fit).find(RETVAR)!=std::string::npos) continue; - - if(!current_loop.is_dowhile) - { - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.name(*o_it,local_SSAt::PHI,it->location),loop_continue_e)); - } - else - { - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*o_it,lit->location),loop_continue_e)); - } - } - -if(!current_loop.is_dowhile) -{ - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); -} -else -{ - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(lit->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(lit->location),loop_continue_e)); -} -//since loophead is processed we can probably go on? -it++; - - -for(;it!=current_loop.body_nodes.end();it++) -{ - body_nodest::const_iterator next_node = it; next_node++; - if(next_node==current_loop.body_nodes.end()) break; - - if(!is_break_node(*it,end_location)) continue; - //no separete treatment for return nodes required as break nodes - // are all nodes with jump out of the loop which include the return - //nodes - exprt break_cond_e=SSA.cond_symbol(it->location); - //NOTE : do we check if the end of the guard has reached in loop_continue_e? -loop_continue_e = and_exprt(break_cond_e,SSA.guard_symbol(it->location)); -if(!is_return_node(current_loop,it)) -{ -exit_conditions.push_back(break_cond_e); -} - - for(varobj_mapt::iterator vit=varobj_map.begin();vit!=varobj_map.end();vit++) - { - if(it==current_loop.body_nodes.begin() && - (as_string(vit->first).find(RETVAR)!=std::string::npos)) continue; - - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*(vit->second),it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); - } - - -} - -current_loop.exit_condition=disjunction(exit_conditions); - -for(loop_nodest::iterator loopit=current_loop.loop_nodes.begin(); - loopit!=current_loop.loop_nodes.end();loopit++) -{ - populate_connectors(*loopit); -} - -} - -bool ssa_local_unwindert::is_break_node(const local_SSAt::nodet& node, - const unsigned int end_location) const -{ - local_SSAt::locationt instr = node.location; - if(!instr->is_goto()) return false; - - // a break should have only one target - if(instr->targets.size()>1) return false; - - if(instr->targets.front()->location_number <= end_location ) return false; - return true; - -} - -bool ssa_local_unwindert::is_return_node(const tree_loopnodet& current_loop, - const local_SSAt::nodest::const_iterator& node) const -{ - return_nodest::const_iterator it=current_loop.return_nodes.find(node); - return (it!=current_loop.return_nodes.end()); -} -/*****************************************************************************\ - * Function : ssa_local_unwindert::unwind - * - * Input : k - unwind_depth - * - * Output : new nodes added to reflect incremental unwinding - * - * Purpose : for all the loops at root-level call unwinder to do - * incremental unwinding - * Pre condition : k must be greater than current_unwinding - * - *****************************************************************************/ -void ssa_local_unwindert::unwind(const irep_idt& fname,unsigned int k) { - assert(is_initialized); - if (loopless) - return; - if (k <= current_unwinding) - assert(false && "unwind depth smaller than previous unwinding!!"); - -//watch out for border-line cases - //parameter to unwind must never be 0 - prev_unwinding=current_unwinding; - - local_SSAt::nodest new_nodes; - irep_idt func_name = "unwind:"+as_string(fname)+":enable_"+i2string(k); - /* if(return_var.empty()) - { - return_var=as_string(fname)+"#return_value"; - }*/ - symbol_exprt new_sym(func_name,bool_typet()); - SSA.enabling_exprs.push_back(new_sym); - for (loop_nodest::iterator it = root_node.loop_nodes.begin(); - it != root_node.loop_nodes.end(); it++) { - unwind(*it, "", false, k, new_sym,new_nodes); - } -//commit all the nodes - SSA.nodes.splice(SSA.nodes.begin(), new_nodes); - current_unwinding = k; - - SSA.current_unwinding = k-1; -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::need_renaming - * - * Input : current_loop - the current context of renaming, - * id - id to be renamed - * - * Output : bool - If id needs to be renamed within current context - * - * Purpose : If a symbol starts with "$cond" or "$guard" it will always have - * to be renamed in the current context. If the variable - * is modified in the current context (loop) then also it has to - * be renamed - * - *****************************************************************************/ -int ssa_local_unwindert::need_renaming(tree_loopnodet& current_loop, - const irep_idt& id) -{ - - //irep_idt base_id = get_base_name(id); -modvar_levelt::iterator mit = current_loop.modvar_level.find(id); - if(mit!=current_loop.modvar_level.end()) - { - return mit->second; - } - - //std::string s = id2string(base_id); - // if(s.find("$cond")!=std::string::npos) return true; - // if(s.find("$guard")!=std::string::npos) return true; - if(current_loop.vars_modified.find(id)!=current_loop.vars_modified.end()) - { - - current_loop.modvar_level[id]=0; - return 0; - - } - - if(current_loop.parent==NULL || current_loop.parent==&root_node) { current_loop.modvar_level[id]=-1; return -1;} - int mylevel = need_renaming(*current_loop.parent,id); - if(mylevel < 0) { current_loop.modvar_level[id]=-1; return -1;} - current_loop.modvar_level[id] = mylevel+1; - return mylevel+1; - - - -} - -unsigned int ssa_local_unwindert::get_last_iteration(std::string& suffix, bool& result) -{ - std::size_t pos = suffix.find_last_of("%"); - if(pos==std::string::npos) {result=false; return 0;} - unsigned int val = safe_string2unsigned(suffix.substr(pos+1)); - assert(val < std::numeric_limits::max()); - suffix=suffix.substr(0,pos); - result = true; - return val; - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::rename - * - * Input : expr - to be renamed, suffix - parent context, - * iteration - iteration in the current context(loop), current_loop - * - * Output : - * - * Purpose : expr is renamed with iteration of the current_loop appended - * if it is modified in the current_loop or if it starts - * with "$cond" or "$guard". For all other cases, - * only the parent context (suffix) is added to the expression - * - * A negative iteration indicates that it must only get parent - * suffix - * - * - * - * - *****************************************************************************/ -void ssa_local_unwindert::rename(exprt &expr, std::string suffix, - const int iteration,tree_loopnodet& current_loop) { - if (expr.id() == ID_symbol) { - symbol_exprt &sexpr = to_symbol_expr(expr); - irep_idt vid=sexpr.get_identifier(); - irep_idt base_id = get_base_name(vid); - bool isreturnvar=(as_string(vid).find(RETVAR)!=std::string::npos); - isreturnvar= isreturnvar||(as_string(vid).find(RETVAR1)!=std::string::npos); - int mylevel; - if(iteration<0) - { - - irep_idt id = id2string(vid) + suffix; - sexpr.set_identifier(id); - return; - } - - std::string s = id2string(base_id); - if(s.find("$guard")!=std::string::npos - || s.find("$cond")!=std::string::npos - || isreturnvar - || (mylevel = need_renaming(current_loop,base_id))==0) - { - - irep_idt id=id2string(vid) + suffix + "%" + i2string(iteration); - sexpr.set_identifier(id); - return; - } - if(mylevel<0) return; - - std::string fsuffix = suffix; - std::size_t pos; - for(unsigned int i=1;i L1_1_head - * uwind1=> L2_1_head - * L2_1_body - * uwind1=> L2_connector - * uwind1=> L1_connector - * and if we move to unwind_depth==2 - * - * uwind2=> L1_2_head - * L2_2 //full - * L2_1 - * uwind2=> L2_connector - * L1_1_head - * uwind2=> L2_2_head // partial - * L2_1 - * uwind2=> L2_connector - * uwind2=> L1_connector - * - * - * See that for L1_1 needs to be unwound partially and so is the case of - * loops nested inside it - * L1_2 on the other hand has to be unwound fully. Also, Li_connector are - * loop head nodes that connect the unwinding with the rest of the code. - * These connections needs to be broken for incremental unwinding so - * their equalities are transfered as constraint where new symbol - * new_sym => e (e is an equality) - * The topmost loop head also needs to change as it is the only one - * where phi nodes exist - * is introduced. To force the equality, set new_sym to true - * - * Backedge from the end of every Li_1 to Li_k_head (k being the unwind_depth) - * is maintained via an iterator. - * - * WARNING : Order of the nodes are completely arbitrary and should not - * be relied on at all. e.g. In the resulting SSA.nodes, a loop body - * may appear after loop head - * - *****************************************************************************/ -void ssa_local_unwindert::unwind(tree_loopnodet& current_loop, - std::string suffix, bool full, const unsigned int unwind_depth, symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes) { - - // a loop has to have at least one body_node, if not, it can not - // have a nested loop either so return - if (current_loop.body_nodes.empty()) - return; - - for (unsigned int i = 0; i < unwind_depth; i++) { - bool tmp = i < current_unwinding ? full : true; - for (loop_nodest::iterator it = current_loop.loop_nodes.begin(); - it != current_loop.loop_nodes.end(); it++) { - - unwind((*it), suffix + "%" + i2string(i), tmp, unwind_depth,new_sym, new_nodes); - - } - } - - unsigned int min_iter = full ? 0 : current_unwinding; - for (unsigned int i = min_iter; i < unwind_depth; i++) { - //process the loophead first - local_SSAt::nodest::iterator it = current_loop.body_nodes.begin(); - //unwinding is done from bottom to top, so topmost unwinding - // is special and is done after this loop - if (i > 0) { - - local_SSAt::nodet node = *it; //copy - node.marked = false; - assert(node.assertions.empty()); //loophead must not have assertions! - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - if (e_it->rhs().id() != ID_if) { - if (e_it->lhs() == SSA.guard_symbol(node.location)) { - //ASSUME : last node in body_nodes is - // always the back-edge node - //This back edge nodes gives us the reachability - //guard at the end of the loop, - //which should be used as reachability guard - //from previous to current iteration - rename(e_it->lhs(), suffix, i-1,current_loop); - exprt e = SSA.guard_symbol( - current_loop.body_nodes.rbegin()->location); - rename(e, suffix, i,current_loop); - e_it->rhs() = e; - - } else { - rename(*e_it, suffix, i-1,current_loop); - } - continue; - } - - if_exprt &e = to_if_expr(e_it->rhs()); -#if 0 - if(i==0) - { //for the first iteration, take the input - //coming from above - rename(e_it->lhs(),suffix, i,current_loop); - e_it->rhs() = e.false_case(); - } - else -#endif - { - //for other iterations, take the loopback - //value - e_it->rhs() = current_loop.pre_post_exprs[e.true_case()]; - rename(e_it->rhs(), suffix, i,current_loop); - rename(e_it->lhs(), suffix, i-1,current_loop); - } - } - new_nodes.push_back(node); - } -#ifdef ASSERTION_HOISTING - assertion_hoisting(current_loop,*it,suffix,is_kinduction, - unwind_depth,new_sym,new_nodes); -#endif - - it++; - //now process the rest of the nodes - for (; it != current_loop.body_nodes.end(); it++) { - //copy the body node, rename and store in new_nodes - local_SSAt::nodet new_node = (*it); - new_node.marked = false; - - rename(new_node, suffix, i,current_loop); - if(is_kinduction && !new_node.assertions.empty() &&( - (current_loop.is_dowhile && i>0) - || (!current_loop.is_dowhile && i>1))) - { //convert all assert to assumes for k-induction - //except the bottom most iteration - - //assertions should be converted to assume only if you are in the step case - //of k-induction and not the base case. that means - // you want guardls=> assume and \not guardls => assert - // as of now this conflicts with checking spurious examples - //so just removing the assertion if it is NOT the bottom most iteration. - // Unless you have checked it for all unwinding less than k, this will - // lead to unsoundness (a bug may not be found if the assertion can fail in iterations - //other than the last - -#if 1 - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); - rename(guard_select,suffix,i,current_loop); - - - for(local_SSAt::nodet::assertionst::iterator ait=new_node.assertions.begin(); - ait!=new_node.assertions.end();ait++) - { -// new_node.constraints.push_back(implies_exprt(not_exprt(guard_select),*ait)); - new_node.constraints.push_back(implies_exprt(guard_select,*ait)); -// new_node.constraints.push_back(*ait); - } -#else - //for linear k-induction (k increases only by 1, you do not need to check if - //you are in the step case or not, since for k-1 even base case must have hold - new_node.constraints.insert(new_node.constraints.end(),new_node.assertions.begin(),new_node.assertions.end()); -#endif - new_node.assertions.clear(); - } - else if(is_ibmc &&( - (current_loop.is_dowhile && i>0) - || (!current_loop.is_dowhile && i>1))) - { //convert all assert to assumes for incremental-bmc - //except the bottom most iteration - - //when you are in base case (guard_select is false) - //for k-1 you already have proved that there is no counter-example - //with k-1 bound. So converting assertion to assume just helps the solver - - - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.begin()->location); - rename(guard_select,suffix,i,current_loop); - for(local_SSAt::nodet::assertionst::iterator ait=new_node.assertions.begin(); - ait!=new_node.assertions.end();ait++) - { - new_node.constraints.push_back(implies_exprt(not_exprt(guard_select),*ait)); - } - - - new_node.assertions.clear(); - } - new_nodes.push_back(new_node); - } - if(i==0) - { - //this is a full unwinding, so end of the loop must be stored - local_SSAt::nodest::iterator le_it = new_nodes.end(); - le_it--; //now points to the last element of the bottom most iteration - - //store the end of this loop, its "loophead" field will be - //pointed to the topmost loophead node - current_loop.loopends_map[suffix] = le_it; - - { - //add all the loop continuation expressions for the bottom most iterations %0 - //this is requested by peter - //used for checking whether loops have been fully unwound - exprt loopend_guard = SSA.guard_symbol(current_loop.body_nodes.rbegin()->location); - exprt loopend_cond = SSA.cond_symbol(current_loop.body_nodes.rbegin()->location); - rename(loopend_guard,suffix,i,current_loop); - rename(loopend_cond,suffix,i,current_loop); - current_loop.loop_continuation_exprs.push_back(and_exprt(loopend_guard,loopend_cond)); - } - } - - } - - //symbol_exprt new_sym("unwind_" + i2string(unwind_depth), bool_typet()); - //SSA.enabling_exprs.push_back(new_sym); - - //only the last element in enabling_exprs needs to be - //set to true, all others should be set to false to enable constraint - // wrt current unwind_depth - { - //copy the original loop head as the first (topmost)iteration - local_SSAt::nodest::iterator it = current_loop.body_nodes.begin(); - local_SSAt::nodet node = *it; //copy - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - - if (e_it->rhs().id() == ID_if) - { - rename(e_it->lhs(), suffix, unwind_depth-1,current_loop); - if_exprt &e = to_if_expr(e_it->rhs()); - rename(e.cond(),suffix, unwind_depth-1,current_loop); - rename(e.true_case(),suffix, unwind_depth-1,current_loop); - -#if 0 - //VERY DIRTY HACK, condition and true case at the topmost iteration - // are free variables, so suffixing "0" so that it always matches - //with the bottom most iteration. Later in the analysis these suffixes - //are needed to be matched :-( - rename(e.cond(),suffix + "%" + i2string(0)); - rename(e.true_case(),suffix + "%" + i2string(0)); -#endif -//for false_case the value comes from above so evaluate in the parent -// context - //if no enclosing loop then no need to rename the false_case - if(current_loop.parent!=NULL && current_loop.parent!= &root_node) - { - - bool result; - std::string parent_suffix = suffix; - unsigned int parent_iteration=get_last_iteration(parent_suffix,result); - - //if there is an enclosing loop, it must return with a valid iteration - if(!result) assert(false); - - rename(e.false_case(),parent_suffix,parent_iteration,*current_loop.parent); - - } - - } - else if (SSA.guard_symbol(node.location) == e_it->lhs()) { - - rename(e_it->lhs(), suffix, unwind_depth-1,current_loop); - rename(e_it->rhs(),suffix,-1,current_loop); - - } else { - rename(*e_it, suffix, unwind_depth-1,current_loop); - } - node.enabling_expr = new_sym; - //exprt e = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(e); - } - //node.equalities.clear(); - new_nodes.push_back(node); - - - local_SSAt::nodest::iterator le_it = new_nodes.end(); - le_it--; //points to the topmost loophead node - - //insert the backedge - current_loop.loopends_map[suffix]->loophead=le_it; - - //print for debugging -#if 0 - std::cout << "Loop end node------" << std::endl; - current_loop.loopends_map[suffix]->output(std::cout,SSA.ns); - std::cout << "Corresponding loop head node----" << std::endl; - current_loop.loopends_map[suffix]->loophead->output(std::cout,SSA.ns); -#endif - - - } - add_connector_node(current_loop,suffix,unwind_depth,new_sym,new_nodes); - -} - -void ssa_local_unwindert::add_connector_node(tree_loopnodet& current_loop, - std::string suffix, - const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes) -{ - //copy the original loop head - - local_SSAt::nodet node=current_loop.body_nodes.front(); - node.marked = false; - node.equalities.clear(); - node.assertions.clear(); - node.constraints.clear(); - node.templates.clear(); - - - for(expr_break_mapt::iterator e_it=current_loop.connectors.begin(); - e_it!=current_loop.connectors.end();e_it++) - { - exprt e = e_it->first; - exprt re = e; - rename(re, suffix, 0,current_loop); - for (unsigned int i = 1; i < unwind_depth; i++) { - - - exprt cond_expr = e_it->second; - rename(cond_expr,suffix,i,current_loop); - exprt true_expr = e; - rename(true_expr, suffix,i,current_loop); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); - } - exprt rhs = re; - exprt lhs=e; - - rename(lhs,suffix,-1,current_loop); - node.equalities.push_back(equal_exprt(lhs,rhs)); - - node.enabling_expr = new_sym; - //exprt ie = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(ie); - - } - //node.equalities.clear(); - new_nodes.push_back(node); - } - - -void ssa_local_unwindert::loop_continuation_conditions( - const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const -{ - loop_cont_e.insert(loop_cont_e.end(), - current_loop.loop_continuation_exprs.begin(), - current_loop.loop_continuation_exprs.end()); - for(loop_nodest::const_iterator it=current_loop.loop_nodes.begin(); - it!=current_loop.loop_nodes.end();it++) - { - loop_continuation_conditions(*it,loop_cont_e); - } -} - -void ssa_local_unwindert::loop_continuation_conditions( - exprt::operandst& loop_cont_e) const -{ - loop_continuation_conditions(root_node,loop_cont_e); -} - -void ssa_local_unwindert::assertion_hoisting(tree_loopnodet& current_loop, - const local_SSAt::nodet& tmp_node, - const std::string& suffix, const bool is_kinduction, - const unsigned int unwind_depth, - symbol_exprt& new_sym, local_SSAt::nodest& new_nodes) - -{ - - if(suffix=="" && is_kinduction) - { - unsigned lower_bound = current_loop.is_dowhile? 1 : 2; - exprt assertion_hoist_e = conjunction(current_loop.assertions_after_loop); - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); - rename(guard_select,suffix,unwind_depth-1,current_loop); - for(unsigned int i=lower_bound;i0) - // || (!current_loop.is_dowhile && (i-1)>1)))) - // { - local_SSAt::nodet node= tmp_node; - node.marked=false; - node.assertions.clear(); - node.equalities.clear(); - node.constraints.clear(); - node.templates.clear(); - node.assertions_after_loop.clear(); - - // if(is_kinduction &&( - // (current_loop.is_dowhile && (i-1)>0) - // || (!current_loop.is_dowhile && (i-1)>1))) - // { //convert all assert to assumes for k-induction - //except the bottom most iteration - - //assertions should be converted to assume only if you are in the step case - //of k-induction and not the base case. that means - // you want guardls=> assume and \not guardls => assert - // as of now this conflicts with checking spurious examples - //so just removing the assertion if it is NOT the bottom most iteration. - // Unless you have checked it for all unwinding less than k, this will - // lead to unsoundness (a bug may not be found if the assertion can fail in iterations - //other than the last - - - - - //if outermost loop, do the assertion hoisting. - //for innerloop assertion hoisting is not necessary because assertions are - //assumed in the parent context anyway - - exprt exit_cond_e=current_loop.exit_condition; - if(!assertion_hoist_e.is_true()&& !exit_cond_e.is_false()) - { - //rename(assertion_hoist_e,suffix,-1,current_loop); - - rename(exit_cond_e,suffix,i,current_loop); - - exprt hoist_cond_e = and_exprt(guard_select,exit_cond_e); - - node.constraints.push_back(implies_exprt(hoist_cond_e,assertion_hoist_e)); - node.enabling_expr = new_sym; - new_nodes.push_back(node); - } - - // } - } - } - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::unwinder_rename - * - * Input : var, node - * - * Output : var is returned with a suffix that reflects the current unwinding - * with the context taken from the node - * - * E.g. - * - * node must look like - * - * cond"somesuffix" == TRUE - * - * e.g. cond%1%2%5%0 == TRUE - * - * and variable might be guard#ls25 - * - * if the current_unwinding is 6 - * - * the variable should be converted to guard#ls25%1%2%5%5 - * - * Note that if current_unwinding is X then suffixes can have at most - * X-1 in its parts - * - *****************************************************************************/ - -void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const -{ - //only to be called for backedge nodes - //This is very dirty hack :-( - if(current_unwinding==0) return; - assert(node.equalities.size()>=1); - //copy suffix from equality lhs to var - std::string id = - id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); - size_t pos = id.find_first_of("%"); - if(pos==std::string::npos) return; - size_t pos1 = id.find_last_of("%"); - std::string suffix; - unsigned unwinding = pre ? current_unwinding-1 : 0; - if(pos==pos1) - { - suffix = "%"+i2string(unwinding); - } - else - { - suffix = id.substr(pos,pos1-pos); - suffix += "%"+i2string(unwinding); - } - - - var.set_identifier(id2string(var.get_identifier())+suffix); -#ifdef DEBUG - std::cout << "new id: " << var.get_identifier() << std::endl; -#endif -} - - -unsigned ssa_local_unwindert::rename_required(const exprt& e, - const unsigned prev_unwinding) const -{ - if(e.id()==ID_symbol) - { - const symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,false); - bool rename_required=true; - for(std::list::iterator it=iterations.begin(); - it!=iterations.end();it++) - { - if(*it!=(prev_unwinding-1)) rename_required=false; - } - //if(iterations.back()==(prev_unwinding-1)) return iterations.size(); - if(rename_required) return iterations.size(); - - - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::const_iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth) return depth; - } - } - } - - return 0; - -} - - -void ssa_local_unwindert::rename_invariant(exprt& e,const irep_idt& suffix) const -{ - if(e.id()==ID_symbol) - { - symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,true); - - - sym.set_identifier(id2string(basename)+id2string(suffix)); - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - rename_invariant(*e_it,suffix); - } - } - } - -} -/***************************************************************************** - * - * Function : ssa_local_unwindert::rename_invariant - * - * Input : inv_in - list of input invariants that is to be renamed for reuse - * - * Output : inv_out - list of renamed invariants - * - * Purpose : For the purpose of reuse of invariant, rename all - * - * - *****************************************************************************/ -void ssa_local_unwindert::rename_invariant(const exprt::operandst& inv_in, - std::vector& inv_out,const unsigned prev_unwinding) const -{ - - if(prev_unwinding==0 || prev_unwinding==std::numeric_limits::max()) - { - return; - } - for(std::vector::const_iterator e_it=inv_in.begin(); - e_it!=inv_in.end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth==0) continue; - - std::vector iter_vector(depth-1,current_unwinding-1); - - do - { - - irep_idt suffix; - - for(std::vector::const_iterator vit=iter_vector.begin(); - vit!=iter_vector.end();vit++) - { - - suffix = id2string(suffix)+"%"+i2string(*vit); - } - suffix = id2string(suffix)+"%"+i2string(current_unwinding-1); - inv_out.push_back(*e_it); - exprt& e = inv_out.back(); - rename_invariant(e,suffix); - } while(odometer_increment(iter_vector,current_unwinding)); - } -} - -exprt ssa_local_unwindert::rename_invariant(const exprt& inv_in) const -{ - if(inv_in.is_true()) return inv_in; - - exprt::operandst inv_in_operands; - if(inv_in.id()!=ID_and) inv_in_operands.push_back(inv_in); - else inv_in_operands = inv_in.operands(); - - std::vector new_inv; - rename_invariant(inv_in_operands,new_inv,prev_unwinding); - - return conjunction(new_inv); -} - - -bool ssa_local_unwindert::odometer_increment(std::vector& odometer, - unsigned base) const -{ - if(odometer.empty()) return false; - unsigned i=odometer.size()-1; - while(true) - { - if(odometer[i] < base-1) {odometer[i]++; return true;} - odometer[i]=0; - if(i==0) return false; //overflow - i--; - - } -return false; -} -/*****************************************************************************\ - * - * Function : ssa_unwindert::ssa_unwindert - * - * Input : reference to ssa_db - * - * Output : - * - * Purpose : Constructor, set is_initialized to false. Initializer init() - * must be invoked before unwind functions are called - * - *****************************************************************************/ -ssa_unwindert::ssa_unwindert(ssa_dbt& _db) : - ssa_db(_db),is_initialized(false) { - - -} - -/*****************************************************************************\ - * - * Function : ssa_new_unwindert::unwind - * - * Input : id - name of the goto-function to be unwound, k - unwinding depth - * - * Output : false - if id does not correspond to any goto-function in the - * unwinder_map - * - * Purpose : incrementally unwind a function 'id' up to depth k. Initializer - * must have been invoked before calling this function - * - *****************************************************************************/ - -void ssa_unwindert::unwind(const irep_idt id, unsigned int k) { - assert(is_initialized); - unwinder_mapt::iterator it; - - it = unwinder_map.find(id); - if (it == unwinder_map.end()) - assert(false && "Function not found"); - it->second.unwind(it->first,k); - -} - -/*****************************************************************************\ - * - * Function : - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::unwind_all(unsigned int k) { - - assert(is_initialized); - - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - it->second.unwind(it->first,k); - } - -} - -/*****************************************************************************\ - * - * Function : - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::output(std::ostream & out) { - if(!is_initialized) return; - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - out << "Unwinding for function" << it->first << std::endl; - it->second.output(out); - } -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : Initialize unwinder_map by computing hierarchical tree_loopnodet - * for every goto-function - * Set is_initialized to true. Initializer must be called before - * unwind funcitions are called. - * - *****************************************************************************/ -void ssa_unwindert::init(bool kinduction, bool _ibmc) -{ - ssa_dbt::functionst& funcs = ssa_db.functions(); - for (ssa_dbt::functionst::iterator it = funcs.begin(); it != funcs.end(); - it++) { - unwinder_map.insert( - unwinder_pairt(it->first, ssa_local_unwindert((*(it->second)),kinduction,_ibmc))); - } - - -} - -void ssa_unwindert::init_localunwinders() -{ - for(unwinder_mapt::iterator it=unwinder_map.begin(); - it!=unwinder_map.end();it++) - { - it->second.set_return_var(it->first); - it->second.init(); - } - is_initialized=true; -} diff --git a/src/ssa/ssa_unwinder_old.h b/src/ssa/ssa_unwinder_old.h deleted file mode 100644 index df01fa8a5..000000000 --- a/src/ssa/ssa_unwinder_old.h +++ /dev/null @@ -1,186 +0,0 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Saurabh Joshi - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SSA_UNWINDER_H -#define CPROVER_DELTACHECK_SSA_UNWINDER_H - -#include - -#include "../ssa/local_ssa.h" -#include "../summarizer/ssa_db.h" - -#define RETVAR "#return_value" -#define RETVAR1 "return_value___VERIFIER_nondet" - -struct compare_node_iteratorst { - bool operator()(const local_SSAt::nodest::iterator& a, - const local_SSAt::nodest::iterator& b) const; - bool operator()(const local_SSAt::nodest::const_iterator& a, - const local_SSAt::nodest::const_iterator& b) const; -}; - -class ssa_local_unwindert : public messaget -{ - irep_idt return_var; - bool isvoid; - local_SSAt& SSA; - unsigned int current_unwinding; - unsigned int prev_unwinding; - class tree_loopnodet; - typedef std::list loop_nodest; - typedef std::map loopends_mapt; - typedef std::map modvar_levelt; - typedef std::set exprst; - typedef exprt cond_et; - typedef exprt guard_et; - typedef std::map expr_break_mapt; - typedef std::pair exp_guard_cond_pairt; - typedef std::set return_nodest; - typedef local_SSAt::nodest body_nodest; - bool loopless; - - class tree_loopnodet - { - public: - return_nodest return_nodes; - //exprst connectors; - exprt::operandst assertions_after_loop; - exprt::operandst loop_continuation_exprs; - exprt exit_condition; - expr_break_mapt connectors; - tree_loopnodet* parent; - local_SSAt::nodest body_nodes; - std::map pre_post_exprs; - modvar_levelt modvar_level; - std::set vars_modified; -#if 0 - symbol_exprt entry_guard; - symbol_exprt exit_guard; - symbol_exprt cond_expr; -#endif - loop_nodest loop_nodes; - loopends_mapt loopends_map; - bool is_dowhile; - - tree_loopnodet(){parent=NULL;is_dowhile=false;} - - void output(std::ostream& out,const namespacet& ns) - { - - - out << "Body nodes" << std::endl; - for(local_SSAt::nodest::iterator it=body_nodes.begin(); - it!=body_nodes.end();it++) - { - it->output(out,ns); - } - out << "Nested loop nodes" << std::endl; - for(loop_nodest::iterator it=loop_nodes.begin(); - it!=loop_nodes.end();it++) - { - it->output(out,ns); - - } - - } - }; - tree_loopnodet root_node; - std::string keep_first_two_hash(const std::string& str) const; - void put_varmod_in_parent(); - void populate_parents(); - void propagate_varmod_to_ancestors(const irep_idt& id, - tree_loopnodet* current_loop); - void populate_return_val_mod(); - void is_void_func(); - bool is_break_node(const local_SSAt::nodet& node, - const unsigned int end_location) const; - bool is_return_node(const tree_loopnodet& current_loop, - const local_SSAt::nodest::const_iterator& node) const; - - void populate_connectors(tree_loopnodet& current_loop); - void unwind(tree_loopnodet& current_loop, - std::string suffix,bool full, - const unsigned int unwind_depth,symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes); - void rename(local_SSAt::nodet& node,std::string suffix, - const int iteration,tree_loopnodet& current_loop); - void rename(exprt &expr, std::string suffix, - const int iteration,tree_loopnodet& current_loop); - int need_renaming(tree_loopnodet& current_loop, - const irep_idt& id); - unsigned int get_last_iteration(std::string& suffix, bool& result); - irep_idt get_base_name(const irep_idt& id); - unsigned rename_required(const exprt& e, - const unsigned prev_unwinding) const; - void rename_invariant(exprt& e,const irep_idt& suffix) const; - void add_connector_node(tree_loopnodet& current_loop, - std::string suffix, - const unsigned int unwind_depth, - symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes); - void loop_continuation_conditions( - const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const; - void assertion_hoisting(tree_loopnodet& current_loop, - const local_SSAt::nodet& tmp_node, - const std::string& suffix, const bool is_kinduction, - const unsigned int unwind_depth, - symbol_exprt& new_sym, local_SSAt::nodest& new_nodes); - bool is_initialized; -public : - void set_return_var(const irep_idt& id); - void dissect_loop_suffix(const irep_idt& id, - irep_idt& before_suffix, - std::list& iterations, bool baseonly) const; - void rename_invariant(const std::vector& inv_in, - std::vector& inv_out,const unsigned prev_unwinding) const; - exprt rename_invariant(const exprt& inv_in) const; - void loop_continuation_conditions(exprt::operandst& loop_cont_e) const; - bool odometer_increment(std::vector& odometer, - unsigned base) const; - bool is_kinduction; - bool is_ibmc; - void init(); - void output(std::ostream& out) - { - SSA.output(out); - } -ssa_local_unwindert(local_SSAt& _SSA, bool k_induct, bool _ibmc); - void unwind(const irep_idt& fname,unsigned int k); - - void unwinder_rename(symbol_exprt &var,const local_SSAt::nodet &node, bool pre) const; -}; - -class ssa_unwindert : public messaget -{ - -public: - typedef std::map unwinder_mapt; - typedef std::pair unwinder_pairt; - - ssa_unwindert(ssa_dbt& _db); - - void init(bool kinduction, bool _ibmc); - - void init_localunwinders(); - - void unwind(const irep_idt id,unsigned int k); - - void unwind_all(unsigned int k); - - ssa_local_unwindert &get(const irep_idt& fname) { return unwinder_map.at(fname); } - - void output(std::ostream & out); - -protected: - ssa_dbt& ssa_db; - bool is_initialized; - unwinder_mapt unwinder_map; - -}; - -#endif diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index 950de1f3f..aa362160e 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -19,7 +19,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: ssa_value_domaint::output +Function: ssa_value_domaint::transform Inputs: @@ -46,7 +46,7 @@ void ssa_value_domaint::transform( { // Perhaps look at condition, for stuff like // p!=0 or the like. - //exprt cond_deref=dereference(from->guard, *this, "", ns); + // exprt cond_deref=dereference(from->guard, *this, "", ns); } else if(from->is_decl()) { @@ -61,18 +61,18 @@ void ssa_value_domaint::transform( // functions may alter state almost arbitrarily: // * any global-scoped variables // * any dirty locals - - #if 0 + +#if 0 for(objectst::const_iterator - o_it=ssa_objects.dirty_locals.begin(); + o_it=ssa_objects.dirty_locals.begin(); o_it!=ssa_objects.dirty_locals.end(); o_it++) assign(*o_it, it, ns); for(objectst::const_iterator - o_it=ssa_objects.globals.begin(); + o_it=ssa_objects.globals.begin(); o_it!=ssa_objects.globals.end(); o_it++) assign(*o_it, it, ns); - #endif +#endif // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) @@ -106,26 +106,26 @@ void ssa_value_domaint::assign_lhs_rec( const namespacet &ns, bool add) { - #ifdef DEBUG +#ifdef DEBUG std::cout << "assign_lhs_rec lhs: " << from_expr(ns, "", lhs) << '\n'; std::cout << "assign_lhs_rec rhs: " << from_expr(ns, "", rhs) << '\n'; - #endif - +#endif + // is the lhs an object? if(is_symbol_struct_member(lhs, ns)) { const typet &lhs_type=ns.follow(lhs.type()); - + if(lhs_type.id()==ID_struct) { // Are we assigning an entire struct? // If so, need to split into pieces, recursively. - + const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { @@ -133,13 +133,13 @@ void ssa_value_domaint::assign_lhs_rec( member_exprt new_rhs(rhs, it->get_name(), it->type()); assign_lhs_rec(new_lhs, new_rhs, ns, add); // recursive call } - + return; // done } // object? ssa_objectt ssa_object(lhs, ns); - + if(ssa_object) { valuest tmp_values; @@ -153,9 +153,11 @@ void ssa_value_domaint::assign_lhs_rec( lhs_values=tmp_values; #if 0 - std::cout << "value_set: "; lhs_values.output(std::cout,ns); std::cout << std::endl; + std::cout << "value_set: "; + lhs_values.output(std::cout, ns); + std::cout << std::endl; #endif - + if(lhs_values.empty()) value_map.erase(ssa_object); } @@ -177,25 +179,24 @@ void ssa_value_domaint::assign_lhs_rec( } else if(lhs.id()==ID_dereference) { -// assert(false); // should have been removed - - //not yet removed if there is an array inside a struct referenced by pointer + // not yet removed + // if there is an array inside a struct referenced by pointer assign_lhs_rec(to_dereference_expr(lhs).pointer(), rhs, ns, true); } else if(lhs.id()==ID_member) { - #if 0 +#if 0 // non-flattened struct or union member const member_exprt &member_expr=to_member_expr(lhs); assign(member_expr.struct_op(), loc, ns); - #endif +#endif } else if(lhs.id()==ID_complex_real || lhs.id()==ID_complex_imag) { - #if 0 +#if 0 assert(lhs.operands().size()==1); assign(lhs.op0(), loc, ns); - #endif +#endif } } @@ -221,7 +222,7 @@ void ssa_value_domaint::assign_rhs_rec( #ifdef DEBUG std::cout << "assign_rhs_rec: " << from_expr(ns, "", rhs) << '\n'; #endif - + if(rhs.id()==ID_address_of) { const exprt &op=to_address_of_expr(rhs).object(); @@ -250,8 +251,9 @@ void ssa_value_domaint::assign_rhs_rec( if(it->type().id()==ID_pointer) { mp_integer pointer_offset=pointer_offset_size(it->type().subtype(), ns); - if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + if(pointer_offset<1) + pointer_offset=1; + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); } } @@ -262,25 +264,24 @@ void ssa_value_domaint::assign_rhs_rec( if(rhs.type().id()==ID_pointer) { mp_integer pointer_offset=pointer_offset_size(rhs.type().subtype(), ns); - if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + if(pointer_offset<1) + pointer_offset=1; + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); } } else if(rhs.id()==ID_dereference) { - // std::cout << rhs.pretty() << std::endl; - // assert(false); // should have been removed - - //not yet removed if there is an array inside a struct referenced by pointer + // not yet removed + // if there is an array inside a struct referenced by pointer assign_rhs_rec(dest, rhs.op0(), ns, true, 1); } else { // object? - + ssa_objectt ssa_object(rhs, ns); - + if(ssa_object) { value_mapt::const_iterator m_it=value_map.find(ssa_object); @@ -288,7 +289,8 @@ void ssa_value_domaint::assign_rhs_rec( if(m_it!=value_map.end()) { valuest tmp_values=m_it->second; - if(offset) tmp_values.offset=true; + if(offset) + tmp_values.offset=true; tmp_values.alignment=merge_alignment(tmp_values.alignment, alignment); dest.merge(tmp_values); } @@ -325,12 +327,15 @@ void ssa_value_domaint::assign_rhs_rec_address_of( if(ssa_object) { dest.value_set.insert(ssa_object); - if(offset) dest.offset=true; + if(offset) + dest.offset=true; } else if(rhs.id()==ID_if) { - assign_rhs_rec_address_of(dest, to_if_expr(rhs).true_case(), ns, offset, alignment); - assign_rhs_rec_address_of(dest, to_if_expr(rhs).false_case(), ns, offset, alignment); + assign_rhs_rec_address_of( + dest, to_if_expr(rhs).true_case(), ns, offset, alignment); + assign_rhs_rec_address_of( + dest, to_if_expr(rhs).false_case(), ns, offset, alignment); } else if(rhs.id()==ID_index) { @@ -340,8 +345,9 @@ void ssa_value_domaint::assign_rhs_rec_address_of( { offset=true; mp_integer pointer_offset=pointer_offset_size(rhs.type(), ns); - if(pointer_offset<1) pointer_offset=1; - a=merge_alignment(a, integer2long(pointer_offset)); + if(pointer_offset<1) + pointer_offset=1; + a=merge_alignment(a, integer2ulong(pointer_offset)); } assign_rhs_rec_address_of(dest, to_index_expr(rhs).array(), ns, offset, a); @@ -367,12 +373,16 @@ void ssa_value_domaint::valuest::output( if(offset) { out << " offset"; - if(alignment!=0) out << "*" << alignment; + if(alignment!=0) + out << "*" << alignment; } - if(null) out << " null"; - if(unknown) out << " unknown"; - if(integer_address) out << " integer_address"; + if(null) + out << " null"; + if(unknown) + out << " unknown"; + if(integer_address) + out << " integer_address"; for(value_sett::const_iterator it=value_set.begin(); it!=value_set.end(); @@ -398,7 +408,7 @@ void ssa_value_domaint::output( const namespacet &ns) const { for(value_mapt::const_iterator - v_it=value_map.begin(); + v_it=value_map.begin(); v_it!=value_map.end(); v_it++) { @@ -425,20 +435,37 @@ bool ssa_value_domaint::valuest::merge(const valuest &src) bool result=false; // bits - if(src.offset && !offset) { offset=true; result=true; } - if(src.null && !null) { null=true; result=true; } - if(src.unknown && !unknown) { unknown=true; result=true; } - if(src.integer_address && !integer_address) { integer_address=true; result=true; } + if(src.offset && !offset) + { + offset=true; + result=true; + } + if(src.null && !null) + { + null=true; + result=true; + } + if(src.unknown && !unknown) + { + unknown=true; + result=true; + } + if(src.integer_address && !integer_address) + { + integer_address=true; + result=true; + } // value set unsigned old_size=value_set.size(); value_set.insert(src.value_set.begin(), src.value_set.end()); - if(old_size!=value_set.size()) result=true; - + if(old_size!=value_set.size()) + result=true; + // alignment alignment=merge_alignment(alignment, src.alignment); - return result; + return result; } /*******************************************************************\ @@ -461,11 +488,11 @@ bool ssa_value_domaint::merge( value_mapt::iterator v_it=value_map.begin(); const value_mapt &new_value_map=other.value_map; bool result=false; - + for(value_mapt::const_iterator - it=new_value_map.begin(); + it=new_value_map.begin(); it!=new_value_map.end(); - ) // no it++ + ) // no it++ { if(v_it==value_map.end() || it->firstfirst) { @@ -479,15 +506,15 @@ bool ssa_value_domaint::merge( v_it++; continue; } - + assert(v_it->first==it->first); - + if(v_it->second.merge(it->second)) result=true; v_it++; it++; } - + return result; } diff --git a/src/ssa/ssa_value_set.h b/src/ssa/ssa_value_set.h index 719ba6308..4e1f8afc1 100644 --- a/src/ssa/ssa_value_set.h +++ b/src/ssa/ssa_value_set.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_VALUE_SET_H -#define CPROVER_SSA_VALUE_SET_H +#ifndef CPROVER_2LS_SSA_SSA_VALUE_SET_H +#define CPROVER_2LS_SSA_SSA_VALUE_SET_H #include @@ -17,7 +17,8 @@ class ssa_value_domaint:public ai_domain_baset { public: virtual void transform(locationt, locationt, ai_baset &, const namespacet &); - virtual void output(std::ostream &, const ai_baset &, const namespacet &) const; + virtual void output( + std::ostream &, const ai_baset &, const namespacet &) const; bool merge(const ssa_value_domaint &, locationt, locationt); struct valuest @@ -27,39 +28,42 @@ class ssa_value_domaint:public ai_domain_baset value_sett value_set; bool offset, null, unknown, integer_address; unsigned alignment; - + inline valuest(): - offset(false), null(false), unknown(false), integer_address(false), + offset(false), + null(false), + unknown(false), + integer_address(false), alignment(0) { } - + void output(std::ostream &, const namespacet &) const; - + bool merge(const valuest &src); - + inline void clear() { *this=valuest(); } - - bool empty() const + + inline bool empty() const { return value_set.empty() && !null && !unknown && !integer_address; } }; - + // maps objects to values typedef std::map value_mapt; value_mapt value_map; - + const valuest operator()(const exprt &src, const namespacet &ns) const { valuest tmp; assign_rhs_rec(tmp, src, ns, false, 0); return tmp; } - + protected: void assign_lhs_rec( const exprt &lhs, const exprt &rhs, @@ -81,9 +85,12 @@ class ssa_value_domaint:public ai_domain_baset static unsigned merge_alignment(unsigned a, unsigned b) { // could use lcm here - if(a==b) return a; - if(a==0) return b; - if(b==0) return a; + if(a==b) + return a; + if(a==0) + return b; + if(b==0) + return a; return 1; } }; diff --git a/src/ssa/translate_union_member.cpp b/src/ssa/translate_union_member.cpp index 593ab4dc0..a3e078ef5 100644 --- a/src/ssa/translate_union_member.cpp +++ b/src/ssa/translate_union_member.cpp @@ -25,14 +25,15 @@ Function: translate_union_member void translate_union_member(exprt &dest, const namespacet &ns) { +#if 0 if(dest.id()==ID_member) { + // TODO } - #if 0 address_of_exprt address_of_expr(member_expr.struct_op()); pointer_typet pointer_type(member_expr.type()); typecast_exprt typecast_expr(address_of_expr, pointer_type); return dereference_exprt(typecast_expr, member_expr.type()); - #endif +#endif } diff --git a/src/ssa/translate_union_member.h b/src/ssa/translate_union_member.h index 740d7df29..5b546cc0f 100644 --- a/src/ssa/translate_union_member.h +++ b/src/ssa/translate_union_member.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_TRANSLATE_UNION_MEMBER_H -#define CPROVER_TRANSLATE_UNION_MEMBER_H +#ifndef CPROVER_2LS_SSA_TRANSLATE_UNION_MEMBER_H +#define CPROVER_2LS_SSA_TRANSLATE_UNION_MEMBER_H #include #include diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 19a01b4a4..4dc6d1c99 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -31,10 +31,10 @@ Function: unwindable_local_SSAt::increment_unwindings void unwindable_local_SSAt::increment_unwindings(int mode) { - if(mode==0) + if(mode==0) { assert(current_unwindings.size()>=1); - unsigned index = current_unwindings.size()-1; + unsigned index=current_unwindings.size()-1; assert(current_unwindings[index]::max()); current_unwindings[index]++; } @@ -43,9 +43,9 @@ void unwindable_local_SSAt::increment_unwindings(int mode) assert(mode==1); current_unwindings.push_back(0); } - else //mode <=-1 + else // mode <=-1 { - for(int i=0;i>mode;i--) + for(int i=0; i>mode; --i) current_unwindings.pop_back(); } } @@ -64,10 +64,10 @@ Function: unwindable_local_SSAt::decrement_unwindings void unwindable_local_SSAt::decrement_unwindings(int mode) { - if(mode==0) + if(mode==0) { assert(current_unwindings.size()>=1); - unsigned index = current_unwindings.size()-1; + unsigned index=current_unwindings.size()-1; assert(current_unwindings[index]>=1); current_unwindings[index]--; } @@ -76,9 +76,9 @@ void unwindable_local_SSAt::decrement_unwindings(int mode) assert(mode==1); current_unwindings.push_back(current_unwinding); } - else //mode <=-1 + else // mode <=-1 { - for(int i=0;i>mode;i--) + for(int i=0; i>mode; --i) current_unwindings.pop_back(); } } @@ -98,13 +98,13 @@ Function: unwindable_local_SSAt::odometer_to_string std::string unwindable_local_SSAt::odometer_to_string( const odometert &odometer, unsigned level) const { - if(current_unwinding<0) //not yet unwind=0 + if(current_unwinding<0) // not yet unwind=0 return ""; if(level>odometer.size()) - level = odometer.size(); - std::string unwind_suffix = ""; - for(unsigned i=0;ilocation_number << std::endl; std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << def_level << std::endl; std::cout << "RENAME_SYMBOL: " - << object.get_identifier() << " --> " - << s.get_identifier() << std::endl; + << object.get_identifier() << " --> " + << s.get_identifier() << std::endl; #endif return s; @@ -147,7 +152,7 @@ Function: unwindable_local_SSAt::get_def_level Outputs: - Purpose: returns the definition level of a variable in the loop + Purpose: returns the definition level of a variable in the loop hierarchy \*******************************************************************/ @@ -155,33 +160,69 @@ Function: unwindable_local_SSAt::get_def_level unsigned unwindable_local_SSAt::get_def_level( locationt def_loc, locationt current_loc) const { - loop_hierarchy_levelt::const_iterator lhl_it = + loop_hierarchy_levelt::const_iterator lhl_it= loop_hierarchy_level.find(def_loc); - unsigned def_level = 0; - if(lhl_it != loop_hierarchy_level.end()) + unsigned def_level=0; + if(lhl_it!=loop_hierarchy_level.end()) { - def_level = lhl_it->second.level; - // If a variable is defined in an other loop (that is on - // the same level as we are [we should should check that] - // then we have to take the "merged version" + def_level=lhl_it->second.level; + // If a variable is defined in an other loop + // that is defined on the same or a higher level + // then we have to take the "merged version" // The reason for this is that the "exit mergers" actually // introduce a new SSA variable version on the context level of a loop. - loop_hierarchy_levelt::const_iterator current_lhl = + loop_hierarchy_levelt::const_iterator current_lhl= loop_hierarchy_level.find(current_loc); + #if 0 - std::cout << "def_level: " << def_level << std::endl; - std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; - std::cout << "current_location: " << current_loc->location_number << std::endl; - std::cout << "current_loop_number: " << current_lhl->second.loop_number << std::endl; + std::cout << "current_location: " + << current_loc->location_number << std::endl; + std::cout << "parent_location: " + << + (current_lhl->second.parent_loc!=goto_function.body.instructions.end()? + current_lhl->second.parent_loc->location_number:-1) + << std::endl; + std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; + std::cout << "current_number: " + << current_lhl->second.loop_number << std::endl; #endif - if(current_lhl != loop_hierarchy_level.end() && - lhl_it->second.loop_number != current_lhl->second.loop_number && - lhl_it->second.level == current_lhl->second.level) + + if(current_lhl!=loop_hierarchy_level.end() && + current_lhl->second.level==0) + { + def_level=0; + } + else if(current_lhl!=loop_hierarchy_level.end() && + lhl_it->second.loop_number!=current_lhl->second.loop_number && + def_level>0) { - if(current_lhl->second.level>0) - def_level = current_lhl->second.level-1; - else - def_level = 0; + bool is_parent=false; + while(current_lhl->second.parent_loc!= + goto_function.body.instructions.end()) + { +#if 0 + std::cout << "current-loc: " + << current_lhl->first->location_number << std::endl; + std::cout << "parent-loc: " + << current_lhl->second.parent_loc->location_number + << std::endl; + std::cout << "def_level: " << def_level << std::endl; + std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; + std::cout << "current_loop_number: " + << current_lhl->second.loop_number << std::endl; +#endif + + current_lhl=loop_hierarchy_level.find(current_lhl->second.parent_loc); + if(lhl_it->second.loop_number==current_lhl->second.loop_number) + { + is_parent=true; + break; + } + } + if(!is_parent) + { + --def_level; + } } } return def_level; @@ -199,23 +240,28 @@ Function: unwindable_local_SSAt::nondet_symbol \*******************************************************************/ -exprt unwindable_local_SSAt::nondet_symbol(std::string prefix, - const typet &type, locationt loc, unsigned counter) const +exprt unwindable_local_SSAt::nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const { - std::string unwind_suffix = odometer_to_string(current_unwindings, - current_unwindings.size()); + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); exprt s(ID_nondet_symbol, type); const irep_idt identifier= prefix+ i2string(loc->location_number)+ "."+i2string(counter)+unwind_suffix+suffix; s.set(ID_identifier, identifier); + #if 0 std::cout << "DEF_LOC: " << loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << current_unwindings.size() << std::endl; std::cout << "RENAME_SYMBOL: " - << s.get(ID_identifier) << std::endl; + << s.get(ID_identifier) << std::endl; #endif + return s; } @@ -233,36 +279,45 @@ Function: unwindable_local_SSAt::rename void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { + if(expr.id()==ID_function_application) + { + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); + expr.set(ID_suffix, unwind_suffix); + } if(expr.id()==ID_symbol) { - symbol_exprt &s = to_symbol_expr(expr); + symbol_exprt &s=to_symbol_expr(expr); locationt def_loc; - //we could reuse name(), but then we would have to search in the ssa_objects - //ENHANCEMENT: maybe better to attach base name, ssa name, + // we could reuse name(), + // but then we would have to search in the ssa_objects + // ENHANCEMENT: maybe better to attach base name, ssa name, // and def_loc to the symbol_expr itself - irep_idt id = get_ssa_name(s,def_loc); - unsigned def_level = get_def_level(def_loc, current_loc); - std::string unwind_suffix = odometer_to_string(current_unwindings, - def_level); + irep_idt id=get_ssa_name(s, def_loc); + unsigned def_level=get_def_level(def_loc, current_loc); + std::string unwind_suffix= + odometer_to_string(current_unwindings, def_level); s.set_identifier(id2string(id)+unwind_suffix); + s.set(ID_suffix, unwind_suffix); #if 0 - std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; - std::cout << "DEF_LEVEL: " << def_level << std::endl; - std::cout << "O.size: " << current_unwindings.size() << std::endl; - std::cout << "current: " << current_unwinding << std::endl; - std::cout << "RENAME_SYMBOL: " - << id << " --> " - << s.get_identifier() << std::endl; + std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; + std::cout << "DEF_LEVEL: " << def_level << std::endl; + std::cout << "O.size: " << current_unwindings.size() << std::endl; + std::cout << "current: " << current_unwinding << std::endl; + std::cout << "RENAME_SYMBOL: " + << id << " --> " + << s.get_identifier() << std::endl; #endif } if(expr.id()==ID_nondet_symbol) { - std::string unwind_suffix = odometer_to_string(current_unwindings, - current_unwindings.size()); - expr.set(ID_identifier, - id2string(expr.get(ID_identifier))+unwind_suffix+suffix); + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); + expr.set(ID_suffix, unwind_suffix); + expr.set(ID_identifier, + id2string(expr.get(ID_identifier))+unwind_suffix+suffix); } - Forall_operands(it,expr) + Forall_operands(it, expr) rename(*it, current_loc); } @@ -279,31 +334,69 @@ Function: unwindable_local_SSAt::get_ssa_name \*******************************************************************/ irep_idt unwindable_local_SSAt::get_ssa_name( - const symbol_exprt &symbol_expr, locationt &loc) + const symbol_exprt &symbol_expr, locationt &loc) const { - std::string s = id2string(symbol_expr.get_identifier()); + std::string s=id2string(symbol_expr.get_identifier()); #if 0 std::cout << "id: " << s << std::endl; #endif - std::size_t pos2 = s.find("%"); - std::size_t pos1 = s.find_last_of("#"); + std::size_t pos2=s.find("%"); + std::size_t pos1=s.find_last_of("#"); if(pos1==std::string::npos) return irep_idt(s); if(pos2==std::string::npos) - pos2 = s.size(); - if(s.substr(pos1+1,2) == "lb") pos1 += 2; - else if(s.substr(pos1+1,2) == "ls") pos1 += 2; - else if(s.substr(pos1+1,3) == "phi") pos1 += 3; - else if((pos2 == pos1+13) && (s.substr(pos1+1,12) == "return_value")) + pos2=s.size(); + if(s.substr(pos1+1, 2)=="lb") + pos1+=2; + else if(s.substr(pos1+1, 2)=="ls") + pos1+=2; + else if(s.substr(pos1+1, 3)=="phi") + pos1+=3; + else if((pos2==pos1+13) && (s.substr(pos1+1, 12)=="return_value")) return irep_idt(s); #if 0 - std::cout << s << ", " << s.substr(pos1+1,pos2-pos1-1) << ", " << s.substr(0,pos2) << std::endl; + std::cout << s << ", " << s.substr(pos1+1, pos2-pos1-1) + << ", " << s.substr(0, pos2) << std::endl; #endif - loc = get_location( - safe_string2unsigned(s.substr(pos1+1,pos2-pos1-1))); - return irep_idt(s.substr(0,pos2)); + loc=get_location( + safe_string2unsigned(s.substr(pos1+1, pos2-pos1-1))); + return irep_idt(s.substr(0, pos2)); } +/*******************************************************************\ + +Function: unwindable_local_SSAt::get_full_ssa_name + + Inputs: + + Outputs: + + Purpose: retrieve ssa name, location, and odometer + +\*******************************************************************/ + +irep_idt unwindable_local_SSAt::get_full_ssa_name( + const symbol_exprt &symbol_expr, + locationt &loc, + odometert &odometer) const +{ + const std::string s=id2string(symbol_expr.get_identifier()); + std::size_t pos1=s.find("%"); + if(pos1!=std::string::npos) + { + std::size_t pos2=0; + do + { + pos2=s.find("%", pos1+1); + if(pos2==std::string::npos) + pos2=s.size(); + odometer.push_back(safe_string2unsigned(s.substr(pos1+1, pos2-pos1-1))); + pos1=pos2; + } + while(pos2!=s.size()); + } + return get_ssa_name(symbol_expr, loc); +} /*******************************************************************\ @@ -321,7 +414,7 @@ void unwindable_local_SSAt::compute_loop_hierarchy() { loop_hierarchy_level.clear(); std::list loopheads; - goto_programt::const_targett i_it = goto_function.body.instructions.end(); + goto_programt::const_targett i_it=goto_function.body.instructions.end(); do { --i_it; @@ -330,34 +423,41 @@ void unwindable_local_SSAt::compute_loop_hierarchy() std::cout << "location: " << i_it->location_number << std::endl; if(i_it->is_goto()) std::cout << "- target: " << i_it->get_target()->location_number - << std::endl; + << std::endl; #endif if(i_it->is_backwards_goto()) { - loopheads.push_back(i_it->get_target()); - loop_hierarchy_level[loopheads.back()].loop_number = i_it->loop_number; + local_SSAt::locationt parent=goto_function.body.instructions.end(); + if(!loopheads.empty()) + parent=loopheads.back(); + local_SSAt::locationt loophead=i_it->get_target(); + loopheads.push_back(loophead); + loop_hierarchy_level[loophead].loop_number=i_it->loop_number; + loop_hierarchy_level[loophead].parent_loc=parent; } if(!loopheads.empty()) { - loop_hierarchy_level[i_it].loop_number = - loop_hierarchy_level[loopheads.back()].loop_number; - loop_hierarchy_level[i_it].level = loopheads.size(); + loop_hierarchy_level[i_it].loop_number= + loop_hierarchy_level[loopheads.back()].loop_number; + loop_hierarchy_level[i_it].level=loopheads.size(); + loop_hierarchy_level[i_it].parent_loc= + loop_hierarchy_level[loopheads.back()].parent_loc; #if 0 - std::cout << "- current: " << - loopheads.back()->location_number << std::endl; + std::cout << "- current: " << + loopheads.back()->location_number << std::endl; #endif - - if(i_it == loopheads.back()) + + if(i_it==loopheads.back()) { #if 0 - std::cout << "- is loop head" << std::endl; + std::cout << "- is loop head" << std::endl; #endif - loopheads.pop_back(); + loopheads.pop_back(); } } } - while(i_it != goto_function.body.instructions.begin()); + while(i_it!=goto_function.body.instructions.begin()); } diff --git a/src/ssa/unwindable_local_ssa.h b/src/ssa/unwindable_local_ssa.h index 5cc1abc58..462e89169 100644 --- a/src/ssa/unwindable_local_ssa.h +++ b/src/ssa/unwindable_local_ssa.h @@ -6,22 +6,21 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ -#ifndef CPROVER_DELTACHECK_SSA_UNWINDABLE_LOCAL_SSA_H -#define CPROVER_DELTACHECK_SSA_UNWINDABLE_LOCAL_SSA_H +#ifndef CPROVER_2LS_SSA_UNWINDABLE_LOCAL_SSA_H +#define CPROVER_2LS_SSA_UNWINDABLE_LOCAL_SSA_H #include #include "local_ssa.h" -class unwindable_local_SSAt : public local_SSAt +class unwindable_local_SSAt:public local_SSAt { public: unwindable_local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, - const std::string &_suffix="") - : - local_SSAt(_goto_function,_ns,_suffix), + const std::string &_suffix=""): + local_SSAt(_goto_function, _ns, _suffix), current_unwinding(-1) { compute_loop_hierarchy(); @@ -29,43 +28,59 @@ class unwindable_local_SSAt : public local_SSAt virtual ~unwindable_local_SSAt() {} - virtual symbol_exprt name(const ssa_objectt &obj, - kindt kind, locationt loc) const - { return name(obj,kind,loc,loc); } - symbol_exprt name(const ssa_objectt &, kindt, - locationt def_loc, locationt current_loc) const; - virtual exprt nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const; - - //control renaming during unwindings + virtual symbol_exprt name( + const ssa_objectt &obj, + kindt kind, + locationt loc) const + { + return name(obj, kind, loc, loc); + } + symbol_exprt name( + const ssa_objectt &, kindt, locationt def_loc, locationt current_loc) const; + virtual exprt nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const; + + // control renaming during unwindings typedef std::vector odometert; odometert current_unwindings; - long current_unwinding; //TODO: must go away - locationt current_location; //TOOD: must go away, not sure how; + long current_unwinding; // TODO: must go away + locationt current_location; // TOOD: must go away, not sure how; // mode==0: current, mode>0 push, mode<0 pop void increment_unwindings(int mode); // mode==0: current, mode>0 push, mode<0 pop void decrement_unwindings(int mode); - std::string odometer_to_string(const odometert &odometer, - unsigned level) const; + std::string odometer_to_string( + const odometert &odometer, + unsigned level) const; void rename(exprt &expr, locationt loc); - typedef struct { + typedef struct + { unsigned level; unsigned loop_number; - } loop_hierarchy_infot; + local_SSAt::locationt parent_loc; + } + loop_hierarchy_infot; - typedef std::map + typedef std::map loop_hierarchy_levelt; loop_hierarchy_levelt loop_hierarchy_level; + irep_idt get_full_ssa_name( + const symbol_exprt &symbol_expr, + locationt &loc, + odometert &odometer) const; + protected: - irep_idt get_ssa_name(const symbol_exprt &, locationt &loc); + irep_idt get_ssa_name(const symbol_exprt &, locationt &loc) const; + unsigned get_def_level(locationt def_loc, locationt current_loc) const; void compute_loop_hierarchy(); - }; #endif diff --git a/src/storefront/Makefile b/src/storefront/Makefile deleted file mode 100644 index 55c7aa7ce..000000000 --- a/src/storefront/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -include ../config.inc - -SRC = storefront_main.cpp storefront_parse_options.cpp data.cpp \ - property_view.cpp file_view.cpp trace_view.cpp - -OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - ../html/html_escape$(OBJEXT) \ - ../html/syntax_highlighting$(OBJEXT) \ - ../html/logo$(OBJEXT) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -LIBS = - -CLEANFILES = storefront$(EXEEXT) - -all: storefront$(EXEEXT) - -############################################################################### - -storefront$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/storefront/data.cpp b/src/storefront/data.cpp deleted file mode 100644 index 391a40991..000000000 --- a/src/storefront/data.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include "data.h" - -/*******************************************************************\ - -Function: datat::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void datat::read(const std::string &file) -{ - xmlt xml; - - console_message_handlert message_handler; - parse_xml(file, message_handler, xml); - - read(xml); -} - -/*******************************************************************\ - -Function: datat::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void datat::read(const xmlt &xml) -{ - for(xmlt::elementst::const_iterator - it=xml.elements.begin(); - it!=xml.elements.end(); - it++) - { - if(it->name=="property") - { - propertyt property; - - for(xmlt::elementst::const_iterator - e_it=it->elements.begin(); - e_it!=it->elements.end(); - e_it++) - { - if(e_it->name=="file") - property.file=e_it->data; - else if(e_it->name=="line") - property.line=unsafe_string2unsigned(e_it->data); - else if(e_it->name=="category") - property.category=e_it->data; - else if(e_it->name=="message") - property.message=e_it->data; - } - - properties.push_back(property); - } - else if(it->name=="description") - { - description=it->data; - } - } -} - diff --git a/src/storefront/data.h b/src/storefront/data.h deleted file mode 100644 index 046c58ceb..000000000 --- a/src/storefront/data.h +++ /dev/null @@ -1,40 +0,0 @@ -/*******************************************************************\ - -Module: Data - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_DATA_H -#define CPROVER_DELTACHECK_DATA_H - -#include - -class datat -{ -public: - class propertyt - { - public: - irep_idt file; - unsigned line; - irep_idt category; - std::string message; - }; - - typedef std::vector propertiest; - propertiest properties; - - std::string description; - - inline void add(const propertyt &e) - { - properties.push_back(e); - } - - void read(const std::string &file); - void read(const class xmlt &); -}; - -#endif diff --git a/src/storefront/file_view.cpp b/src/storefront/file_view.cpp deleted file mode 100644 index a20041d77..000000000 --- a/src/storefront/file_view.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include "../html/html_escape.h" -#include "../html/syntax_highlighting.h" - -#include "data.h" - -/*******************************************************************\ - -Function: file_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void print_file(const datat &data, irep_idt file, std::ostream &out) -{ - out << "
\n"; - out << "
" << html_escape(file) << "
\n"; - out << "
\n"; - - std::ifstream in(file.c_str()); - if(!in) - { - } - else - { - // line to property number - std::map > line_map; - - for(datat::propertiest::const_iterator - e_it=data.properties.begin(); - e_it!=data.properties.end(); - e_it++) - if(e_it->file==file) - { - line_map[e_it->line].push_back(e_it-data.properties.begin()); - } - - syntax_highlightingt syntax_highlighting(out); - - unsigned line_no=1; - - std::string line; - while(std::getline(in, line)) - { - syntax_highlighting.strong_class=""; - syntax_highlighting.line_no=line_no; - - std::vector &properties=line_map[line_no]; - - if(!properties.empty()) - { - syntax_highlighting.strong_class="alarm"; - } - - syntax_highlighting(line); - - line_no++; - } - } - - out << "
\n\n"; -} - -/*******************************************************************\ - -Function: file_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void file_view(const datat &data) -{ - std::ofstream out("file_view.html"); - - out << "\n\n"; - out << "" << html_escape(data.description) << "\n"; - - out << "\n"; - out << "\n"; - - out << "\n"; - - out << "
\n"; - - out << "
\n"; - out << html_escape(data.description) << "\n"; - out << "
\n"; - - std::set files; - - for(datat::propertiest::const_iterator - e_it=data.properties.begin(); - e_it!=data.properties.end(); - e_it++) - files.insert(e_it->file); - - for(std::set::const_iterator - f_it=files.begin(); - f_it!=files.end(); - f_it++) - { - if(has_prefix(id2string(*f_it), "/usr/include/")) - continue; - - if(has_prefix(id2string(*f_it), "")) - continue; - - print_file(data, *f_it, out); - } - - out << "\n\n"; -} diff --git a/src/storefront/file_view.h b/src/storefront/file_view.h deleted file mode 100644 index e0c5ca8ee..000000000 --- a/src/storefront/file_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: File View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_FILE_VIEW_H -#define CPROVER_DELTACHECK_FILE_VIEW_H - -void file_view(const class datat &); - -#endif diff --git a/src/storefront/property_view.cpp b/src/storefront/property_view.cpp deleted file mode 100644 index 21c5b31e8..000000000 --- a/src/storefront/property_view.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/*******************************************************************\ - -Module: Property View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "data.h" - -/*******************************************************************\ - -Function: property_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void property_view(const class datat &) -{ - -} - - - - diff --git a/src/storefront/property_view.h b/src/storefront/property_view.h deleted file mode 100644 index 83f1eae61..000000000 --- a/src/storefront/property_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Property View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PROPERTY_VIEW_H -#define CPROVER_DELTACHECK_PROPERTY_VIEW_H - -void property_view(const class datat &); - -#endif diff --git a/src/storefront/storefront_parse_options.cpp b/src/storefront/storefront_parse_options.cpp deleted file mode 100644 index 2cf35655c..000000000 --- a/src/storefront/storefront_parse_options.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "../deltacheck/version.h" - -#include "storefront_parse_options.h" -#include "data.h" -#include "file_view.h" -#include "trace_view.h" -#include "property_view.h" - -/*******************************************************************\ - -Function: storefront_parse_optionst::storefront_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -storefront_parse_optionst::storefront_parse_optionst( - int argc, const char **argv): - parse_options_baset(STOREFRONT_OPTIONS, argc, argv) -{ -} - -/*******************************************************************\ - -Function: storefront_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int storefront_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - try - { - if(cmdline.args.empty()) - { - usage_error(); - return 10; - } - - // read config - datat data; - - for(unsigned i=0; i - -#define STOREFRONT_OPTIONS \ - "(verbosity):(version)" - -class storefront_parse_optionst:public parse_options_baset -{ -public: - virtual int doit(); - virtual void help(); - - storefront_parse_optionst( - int argc, const char **argv); - -protected: -}; - -#endif diff --git a/src/storefront/trace_view.cpp b/src/storefront/trace_view.cpp deleted file mode 100644 index 73c2a30b3..000000000 --- a/src/storefront/trace_view.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "data.h" - -/*******************************************************************\ - -Function: trace_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void trace_view(const class datat &) -{ - -} - - - diff --git a/src/storefront/trace_view.h b/src/storefront/trace_view.h deleted file mode 100644 index fcaaf7a4f..000000000 --- a/src/storefront/trace_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_TRACE_VIEW_H -#define CPROVER_DELTACHECK_TRACE_VIEW_H - -void trace_view(const class datat &); - -#endif diff --git a/src/summarizer/2ls-wrapper.sh b/src/summarizer/2ls-wrapper.sh deleted file mode 100644 index ab4dadd9c..000000000 --- a/src/summarizer/2ls-wrapper.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash - -parse_property_file() -{ - local fn=$1 - - cat $fn | sed 's/[[:space:]]//g' | perl -n -e ' -if(/^CHECK\(init\((\S+)\(\)\),LTL\(G(\S+)\)\)$/) { - print "ENTRY=$1\n"; - print "PROPERTY=\"--error-label $1\"\n" if($2 =~ /^!label\((\S+)\)$/); - print "PROPERTY=\" \"\n" if($2 =~ /^!call\(__VERIFIER_error\(\)\)$/); - print "PROPERTY=\"--pointer-check --memory-leak-check --bounds-check\"\n" if($2 =~ /^valid-(free|deref|memtrack)$/); - print "PROPERTY=\"--signed-overflow-check\"\n" if($2 =~ /^!SignedIntegerOverflow$/); -}' -} - -parse_result() -{ - if tail -n 50 $LOG.ok | grep -q "^[[:space:]]*__CPROVER_memory_leak == NULL$" ; then - echo 'FALSE(valid-memtrack)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*dereference failure:" ; then - echo 'FALSE(valid-deref)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*double free$" ; then - echo 'FALSE(valid-free)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*free argument has offset zero$" ; then - echo 'FALSE(valid-free)' - else - echo FALSE - fi -} - -BIT_WIDTH="--64" -BM="" -PROP_FILE="" - -while [ -n "$1" ] ; do - case "$1" in - --32|--64) BIT_WIDTH="$1" ; shift 1 ;; - --propertyfile) PROP_FILE="$2" ; shift 2 ;; - *) BM="$1" ; shift 1 ;; - esac -done - -if [ -z "$BM" ] || [ -z "$PROP_FILE" ] ; then - echo "Missing benchmark or property file" - exit 1 -fi - -if [ ! -s "$BM" ] || [ ! -s "$PROP_FILE" ] ; then - echo "Empty benchmark or property file" - exit 1 -fi - -eval `parse_property_file $PROP_FILE` -export ENTRY -export PROPERTY -export BIT_WIDTH -export BM - -export LOG=`mktemp -t 2ls-log.XXXXXX` -trap "rm -f $LOG.ok $LOG.cex" EXIT - -./2ls --k-induction --competition-mode --graphml-cex $LOG.cex $BIT_WIDTH $PROPERTY --function $ENTRY $BM >> $LOG.ok 2>&1 -ec=$? -cat $LOG.ok -cat $LOG.cex -if [ $ec -eq 0 ] -then - echo "TRUE" -fi -if [ $ec -eq 5 ] -then - echo "UNKNOWN" -fi -if [ $ec -eq 10 ] -then - parse_result - cp $LOG.cex witness.graphml -fi -exit $ec - diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile deleted file mode 100644 index 07ae39e51..000000000 --- a/src/summarizer/Makefile +++ /dev/null @@ -1,117 +0,0 @@ -include ../config.inc - -SRC = summarizer_base.cpp \ - summarizer_fw.cpp summarizer_bw.cpp \ - summarizer_fw_term.cpp summarizer_bw_term.cpp \ - summarizer_fw_contexts.cpp \ - summarizer_main.cpp summarizer_parse_options.cpp \ - show.cpp summary_checker_base.cpp \ - summary_checker_ai.cpp summary_checker_bmc.cpp \ - summary_checker_kind.cpp \ - cover_goals_ext.cpp horn_encoding.cpp \ - summary_db.cpp summary.cpp ssa_db.cpp \ - array_abstraction.cpp preprocessing_util.cpp \ - instrument_goto.cpp function_signature.cpp - -OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ - $(CBMC)/src/linking/linking$(LIBEXT) \ - $(CBMC)/src/assembler/assembler$(LIBEXT) \ - $(CBMC)/src/big-int/big-int$(LIBEXT) \ - $(CBMC)/src/goto-programs/goto-programs$(LIBEXT) \ - $(CBMC)/src/goto-symex/goto-symex$(LIBEXT) \ - $(CBMC)/src/analyses/analyses$(LIBEXT) \ - $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ - $(CBMC)/src/langapi/langapi$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - $(CBMC)/src/json/json$(LIBEXT) \ - $(CBMC)/src/solvers/solvers$(LIBEXT) \ - $(CBMC)/src/util/util$(LIBEXT) \ - -DELTA_OBJ+=\ - $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ - ../ssa/local_ssa$(OBJEXT) \ - ../ssa/malloc_ssa$(OBJEXT) \ - ../ssa/ssa_domain$(OBJEXT) \ - ../ssa/assignments$(OBJEXT) \ - ../ssa/guard_map$(OBJEXT) \ - ../ssa/ssa_object$(OBJEXT) \ - ../ssa/address_canonizer$(OBJEXT) \ - ../ssa/ssa_dereference$(OBJEXT) \ - ../ssa/simplify_ssa$(OBJEXT) \ - ../ssa/ssa_build_goto_trace$(OBJEXT) \ - ../ssa/split_loopheads$(OBJEXT)\ - ../ssa/ssa_inliner$(OBJEXT)\ - ../ssa/ssa_unwinder$(OBJEXT)\ - ../ssa/unwindable_local_ssa$(OBJEXT)\ - ../ssa/ssa_value_set$(OBJEXT) \ - ../functions/summary$(OBJEXT) \ - ../functions/get_function$(OBJEXT) \ - ../functions/path_util$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../domains/fixed_point$(OBJEXT) \ - ../domains/ssa_fixed_point$(OBJEXT) \ - ../domains/tpolyhedra_domain$(OBJEXT) \ - ../domains/predabs_domain$(OBJEXT) \ - ../domains/equality_domain$(OBJEXT) \ - ../domains/domain$(OBJEXT) \ - ../domains/util$(OBJEXT) \ - ../domains/incremental_solver$(OBJEXT) \ - ../domains/strategy_solver_base$(OBJEXT) \ - ../domains/strategy_solver_predabs$(OBJEXT) \ - ../domains/strategy_solver_equality$(OBJEXT) \ - ../domains/strategy_solver_enumeration$(OBJEXT) \ - ../domains/strategy_solver_binsearch$(OBJEXT) \ - ../domains/strategy_solver_binsearch2$(OBJEXT) \ - ../domains/strategy_solver_binsearch3$(OBJEXT) \ - ../domains/template_generator_base$(OBJEXT) \ - ../domains/template_generator_summary$(OBJEXT) \ - ../domains/template_generator_callingcontext$(OBJEXT) \ - ../domains/ssa_analyzer$(OBJEXT) \ - ../domains/predicate$(OBJEXT) \ - ../domains/template_generator_ranking$(OBJEXT) \ - ../domains/ranking_solver_enumeration$(OBJEXT) \ - ../domains/linrank_domain$(OBJEXT) \ - ../domains/lexlinrank_solver_enumeration$(OBJEXT) \ - ../domains/lexlinrank_domain$(OBJEXT) -# ../domains/solver_enumeration$(OBJEXT) -# ../domains/strategy_solver_binsearch2$(OBJEXT) - -OBJ+=$(DELTA_OBJ) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -CP_CXXFLAGS+= $(SUMMARIZERFLAGS) - -INCLUDES= -I $(CBMC)/src -# \ -# -I $(CUDD)/cudd -I $(CUDD)/obj -I $(CUDD)/mtr -I $(CUDD)/epd - -LIBS = -#LIBS = $(CUDD)/cudd/libcudd.a $(CUDD)/mtr/libmtr.a \ -# $(CUDD)/st/libst.a $(CUDD)/epd/libepd.a $(CUDD)/util/libutil.a - -#CP_CXXFLAGS +=-g -DSHOW_CALLING_CONTEXTS - -# $(CUDD)/obj/libobj.a - -CLEANFILES = summarizer$(EXEEXT) $(DELTA_OBJ) - -all: 2ls$(EXEEXT) - -ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) - OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) - CP_CXXFLAGS += -DHAVE_CPP -endif - -ifneq ($(wildcard $(CBMC)/src/java/Makefile),) - OBJ += $(CBMC)/src/java/java$(LIBEXT) - CXXFLAGS += -DHAVE_JAVA -endif - -############################################################################### - -2ls$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/summarizer/array_abstraction.cpp b/src/summarizer/array_abstraction.cpp deleted file mode 100644 index 438c59ac2..000000000 --- a/src/summarizer/array_abstraction.cpp +++ /dev/null @@ -1,2053 +0,0 @@ -/*******************************************************************\ - -Module: String Abstraction - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include "array_abstraction.h" - -#define DEBUG - -#ifdef DEBUG -#include -#endif - - - - -/*******************************************************************\ - -Function: array_abstractiont::build_wrap - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_wrap(const exprt &object, exprt &dest, bool write) -{ - // debugging - return build(object, dest, write); -} - -/*******************************************************************\ - -Function: array_abstractiont::is_ptr_string_struct - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::is_ptr_string_struct(const typet &type) const -{ - return type.id()==ID_pointer && - type_eq(type.subtype(), string_struct, ns); -} - -static inline bool is_ptr_argument(const typet &type) -{ - return type.id()==ID_pointer; -} - -/*******************************************************************\ - -Function: array_abstraction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_programt &dest) -{ - array_abstractiont array_abstraction(symbol_table, message_handler); - array_abstraction(dest); -} - -/*******************************************************************\ - -Function: array_abstraction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_functionst &dest) -{ - array_abstractiont array_abstraction(symbol_table, message_handler); - array_abstraction(dest); -} - -/*******************************************************************\ - -Function: array_abstractiont::array_abstractiont - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -array_abstractiont::array_abstractiont( - symbol_tablet &_symbol_table, - message_handlert &_message_handler): - message_streamt(_message_handler), - arg_suffix("#strarg"), - sym_suffix("#str$fcn"), - symbol_table(_symbol_table), - ns(_symbol_table), - temporary_counter(0) -{ - struct_typet s; - - s.components().resize(3); - - s.components()[0].set_name("is_zero"); - s.components()[0].set_pretty_name("is_zero"); - s.components()[0].type()=build_type(IS_ZERO); - - s.components()[1].set_name("length"); - s.components()[1].set_pretty_name("length"); - s.components()[1].type()=build_type(LENGTH); - - s.components()[2].set_name("size"); - s.components()[2].set_pretty_name("size"); - s.components()[2].type()=build_type(SIZE); - - string_struct=s; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_type - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -typet array_abstractiont::build_type(whatt what) -{ - typet type; - - switch(what) - { - case IS_ZERO: type=bool_typet(); break; - case LENGTH: type=size_type(); break; - case SIZE: type=size_type(); break; - } - - return type; -} - -/*******************************************************************\ - -Function: array_abstractiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::operator()(goto_functionst &dest) -{ - Forall_goto_functions(it, dest) - { - // don't instrument our internal functions - if(has_prefix(id2string(it->first), CPROVER_PREFIX)) - continue; - - sym_suffix="#str$"+id2string(it->first); - add_str_arguments(it->first, it->second); - abstract(it->second.body); - current_args.clear(); - } - - // do we have a main? - goto_functionst::function_mapt::iterator - m_it=dest.function_map.find(dest.entry_point()); - - if(m_it!=dest.function_map.end()) - { - goto_programt &main=m_it->second.body; - - // do initialization - initialization.destructive_append(main); - main.swap(initialization); - initialization.clear(); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::operator()(goto_programt &dest) -{ - abstract(dest); - - // do initialization - initialization.destructive_append(dest); - dest.swap(initialization); - initialization.clear(); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_str_arguments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::add_str_arguments( - const irep_idt & name, - goto_functionst::goto_functiont &fct) -{ - symbol_tablet::symbolst::iterator sym_entry=symbol_table.symbols.find(name); - assert(sym_entry!=symbol_table.symbols.end()); - symbolt &fct_symbol=sym_entry->second; - - code_typet::parameterst ¶meters= - to_code_type(fct.type).parameters(); - code_typet::parameterst str_args; - - for(code_typet::parameterst::iterator - it=parameters.begin(); - it!=parameters.end(); - ++it) - { - const typet &abstract_type=build_abstraction_type(it->type()); - if(abstract_type.is_nil()) - continue; - - const irep_idt &identifier=it->get_identifier(); - if(identifier=="") continue; // ignore - - add_argument(str_args, fct_symbol, abstract_type, - id2string(it->get_base_name())+arg_suffix, - id2string(identifier)+arg_suffix); - - current_args.insert(identifier); - } - - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct.type).return_type()); - if(!abstract_ret_type.is_nil()) - { - add_argument(str_args, fct_symbol, abstract_ret_type, - "$return_value_str_abst"+arg_suffix, - abstract_ret_val_name(fct_symbol)); - } - - parameters.insert(parameters.end(), str_args.begin(), str_args.end()); - code_typet::parameterst &symb_parameters= - to_code_type(fct_symbol.type).parameters(); - symb_parameters.insert(symb_parameters.end(), str_args.begin(), str_args.end()); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_argument - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::add_argument( - code_typet::parameterst &str_args, - const symbolt &fct_symbol, - const typet &type, - const irep_idt &base_name, - const irep_idt &identifier) -{ - str_args.push_back(code_typet::parametert(type)); - str_args.back().add_source_location()=fct_symbol.location; - str_args.back().set_base_name(base_name); - str_args.back().set_identifier(identifier); - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=str_args.back().source_location(); - new_symbol.name=str_args.back().get_identifier(); - new_symbol.module=fct_symbol.module; - new_symbol.base_name=str_args.back().get_base_name(); - new_symbol.mode=fct_symbol.mode; - new_symbol.pretty_name=str_args.back().get_base_name(); - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::abstract(goto_programt &dest) -{ - locals.clear(); - - Forall_goto_program_instructions(it, dest) - it=abstract(dest, it); - - if(locals.empty()) return; - - // go over it again for the newly added locals - declare_define_locals(dest); - locals.clear(); -} - -/*******************************************************************\ - -Function: array_abstractiont::declare_define_locals - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::declare_define_locals(goto_programt &dest) -{ - typedef hash_map_cont - available_declst; - available_declst available_decls; - - Forall_goto_program_instructions(it, dest) - if(it->is_decl()) - { - // same name may exist several times due to inlining, make sure the first - // declaration is used - available_decls.insert(std::make_pair( - to_code_decl(it->code).get_identifier(), it)); - } - - // declare (and, if necessary, define) locals - for(localst::const_iterator l_it=locals.begin(); - l_it!=locals.end(); - ++l_it) - { - goto_programt::targett ref_instr=dest.instructions.begin(); - bool has_decl=false; - - available_declst::const_iterator entry=available_decls.find(l_it->first); - - if(available_declst::const_iterator(available_decls.end())!=entry) - { - ref_instr=entry->second; - has_decl=true; - } - - goto_programt tmp; - make_decl_and_def(tmp, ref_instr, l_it->second, l_it->first); - - if(has_decl) ++ref_instr; - dest.insert_before_swap(ref_instr, tmp); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::make_decl_and_def - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::make_decl_and_def(goto_programt &dest, - goto_programt::targett ref_instr, - const irep_idt &identifier, - const irep_idt &source_sym) -{ - const symbolt &symbol=ns.lookup(identifier); - symbol_exprt sym_expr=symbol.symbol_expr(); - - goto_programt::targett decl1=dest.add_instruction(); - decl1->make_decl(); - decl1->source_location=ref_instr->source_location; - decl1->function=ref_instr->function; - decl1->code=code_declt(sym_expr); - decl1->code.add_source_location()=ref_instr->source_location; - - exprt val=symbol.value; - // initialize pointers with suitable objects - if(val.is_nil()) - { - const symbolt &orig=ns.lookup(source_sym); - val=make_val_or_dummy_rec(dest, ref_instr, symbol, ns.follow(orig.type)); - } - - // may still be nil (structs, then assignments have been done already) - if(val.is_not_nil()) - { - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(sym_expr, val); - assignment1->code.add_source_location()=ref_instr->source_location; - } -} - -/*******************************************************************\ - -Function: array_abstractiont::make_val_or_dummy_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::make_val_or_dummy_rec(goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, const typet &source_type) -{ - const typet &eff_type=ns.follow(symbol.type); - - if(type_eq(eff_type, string_struct, ns)) - { - symbol_exprt sym_expr=add_dummy_symbol_and_value( - dest, ref_instr, symbol, irep_idt(), - eff_type, source_type); - - return sym_expr; - } - else if(eff_type.id()==ID_union || - (eff_type.id()==ID_struct && !type_eq(eff_type, string_struct, ns))) - { - const struct_union_typet &su_source=to_struct_union_type(source_type); - const struct_union_typet::componentst &s_components= - su_source.components(); - const struct_union_typet &struct_union_type=to_struct_union_type(eff_type); - const struct_union_typet::componentst &components= - struct_union_type.components(); - unsigned seen=0; - - struct_union_typet::componentst::const_iterator it2=components.begin(); - for(struct_union_typet::componentst::const_iterator - it=s_components.begin(); - it!=s_components.end() && it2!=components.end(); - ++it) - { - if(it->get_name()!=it2->get_name()) - continue; - - const typet &eff_sub_type=ns.follow(it2->type()); - if(eff_sub_type.id() == ID_pointer || - eff_sub_type.id() == ID_array || - eff_sub_type.id() == ID_struct || - eff_sub_type.id() == ID_union) - { - symbol_exprt sym_expr=add_dummy_symbol_and_value( - dest, ref_instr, symbol, it2->get_name(), - it2->type(), ns.follow(it->type())); - - member_exprt member(symbol.symbol_expr(), it2->get_name(), it2->type()); - - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(member, sym_expr); - assignment1->code.add_source_location()=ref_instr->source_location; - } - - ++seen; - ++it2; - } - - assert(components.size()==seen); - } - - return nil_exprt(); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_dummy_symbol_and_value - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -symbol_exprt array_abstractiont::add_dummy_symbol_and_value( - goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, - const irep_idt &component_name, - const typet &type, - const typet &source_type) -{ - std::string suffix="$strdummy"; - if(!component_name.empty()) - suffix="#"+id2string(component_name)+suffix; - - irep_idt dummy_identifier=id2string(symbol.name)+suffix; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=ref_instr->source_location; - new_symbol.name=dummy_identifier; - new_symbol.module=symbol.module; - new_symbol.base_name=id2string(symbol.base_name)+suffix; - new_symbol.mode=symbol.mode; - new_symbol.pretty_name=id2string( - symbol.pretty_name.empty()?symbol.base_name:symbol.pretty_name)+suffix; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_exprt sym_expr=new_symbol.symbol_expr(); - - // make sure it is declared before the recursive call - goto_programt::targett decl=dest.add_instruction(); - decl->make_decl(); - decl->source_location=ref_instr->source_location; - decl->function=ref_instr->function; - decl->code=code_declt(sym_expr); - decl->code.add_source_location()=ref_instr->source_location; - - // set the value - may be nil - if(source_type.id()==ID_array && - type_eq(type, string_struct, ns)) - { - new_symbol.value=struct_exprt(string_struct); - new_symbol.value.operands().resize(3); - new_symbol.value.op0()=build_unknown(IS_ZERO, false); - new_symbol.value.op1()=build_unknown(LENGTH, false); - new_symbol.value.op2()=to_array_type(source_type).size().id()==ID_infinity? - build_unknown(SIZE, false):to_array_type(source_type).size(); - make_type(new_symbol.value.op2(), build_type(SIZE)); - } - - if(new_symbol.value.is_not_nil()) - { - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(sym_expr, new_symbol.value); - assignment1->code.add_source_location()=ref_instr->source_location; - } - - symbol_table.move(new_symbol); - - return sym_expr; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract( - goto_programt &dest, - goto_programt::targett it) -{ - switch(it->type) - { - case ASSIGN: - it=abstract_assign(dest, it); - break; - - case GOTO: - it=read_rec(it->guard, dest, it); - break; - case ASSERT: - case ASSUME: - it=read_rec(it->guard, dest, it); - if(has_string_macros(it->guard)) - replace_string_macros(it->guard, false, it->source_location); - break; - - case FUNCTION_CALL: - abstract_function_call(dest, it); - break; - - case RETURN: - it=abstract_return(dest, it); - break; - - case END_FUNCTION: - case START_THREAD: - case END_THREAD: - case ATOMIC_BEGIN: - case ATOMIC_END: - case DECL: - case DEAD: - case CATCH: - case THROW: - case SKIP: - case OTHER: - case LOCATION: - break; - case NO_INSTRUCTION_TYPE: - assert(false); - break; - } - - return it; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_assign( - goto_programt &dest, - goto_programt::targett target) -{ - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt &rhs=assign.rhs(); - - if(has_string_macros(lhs)) - { - replace_string_macros(lhs, true, target->source_location); - move_lhs_arithmetic(lhs, rhs); - } - - if(has_string_macros(rhs)) - { - replace_string_macros(rhs, false, target->source_location); - } - - target=read_rec(rhs, dest, target); - - const typet &type=ns.follow(lhs.type()); - if(type.id()==ID_pointer || type.id()==ID_array) - return abstract_pointer_assign(dest, target); - else if(is_char_type(type)) - return abstract_char_assign(dest, target); - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_function_call - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::abstract_function_call( - goto_programt &dest, - goto_programt::targett target) -{ - code_function_callt &call=to_code_function_call(target->code); - code_function_callt::argumentst &arguments=call.arguments(); - code_function_callt::argumentst str_args; - - const symbolt &fct_symbol=ns.lookup(call.function().get(ID_identifier)); - const code_typet::parameterst &formal_params= - to_code_type(fct_symbol.type).parameters(); - - code_function_callt::argumentst::const_iterator it1=arguments.begin(); - for(code_typet::parameterst::const_iterator it2=formal_params.begin(); - it2!=formal_params.end(); - it2++, it1++) - { - const typet &abstract_type=build_abstraction_type(it2->type()); - if(abstract_type.is_nil()) continue; - - if(it1==arguments.end()) - { - err_location(target->source_location); - throw "function call: not enough arguments " + id2string(call.function().get(ID_identifier)) + " " - + from_expr(ns, "", *it2); - } - - str_args.push_back(exprt()); - // if function takes void*, build for *it1 will fail if actual parameter - // is of some other pointer type; then just introduce an unknown - if(build_wrap(*it1, str_args.back(), false)) - str_args.back()=build_unknown(abstract_type, false); - // array -> pointer translation - if(str_args.back().type().id()==ID_array && - abstract_type.id()==ID_pointer) - { - assert(type_eq(str_args.back().type().subtype(), - abstract_type.subtype(), ns)); - - index_exprt idx(str_args.back(), gen_zero(index_type())); - // disable bounds check on that one - idx.set("bounds_check", false); - - str_args.back()=idx; - } - - - if(!is_ptr_argument(abstract_type)) - str_args.back()=str_args.back(); - } - - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct_symbol.type).return_type()); - if(!abstract_ret_type.is_nil()) - { - const exprt &lhs = call.lhs(); - exprt new_lhs; - - if(lhs.is_nil() || - build_wrap(lhs, new_lhs, false)) - str_args.push_back(null_pointer_exprt( - is_ptr_argument(abstract_ret_type)? - to_pointer_type(abstract_ret_type): - pointer_typet(abstract_ret_type))); - else - { - assert(type_eq(new_lhs.type(), - abstract_ret_type, ns)); - - //if(is_ptr_argument(abstract_ret_type)) - str_args.push_back(new_lhs); - //else - // str_args.push_back(address_of_exprt(new_lhs)); - } - } - - arguments.insert(arguments.end(), str_args.begin(), str_args.end()); -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_return - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_return( - goto_programt &dest, - goto_programt::targett target) -{ - code_returnt &ret=to_code_return(target->code); - - if(!ret.has_return_value()) - return target; - - exprt &retval=ret.return_value(); - - replace_string_macros(retval, false, target->source_location); - - const symbolt &fct_symbol=ns.lookup(target->function); - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct_symbol.type).return_type()); - if(abstract_ret_type.is_nil()) - return target; - - irep_idt identifier=abstract_ret_val_name(fct_symbol); - const symbolt &str_symbol=ns.lookup(identifier); - symbol_exprt sym_expr=str_symbol.symbol_expr(); - - goto_programt::instructiont is_null; - is_null.function=target->function; - is_null.source_location=target->source_location; - dest.insert_before_swap(target, is_null); - goto_programt::targett next=target; - ++next; - target->make_goto(next, equal_exprt(sym_expr, - null_pointer_exprt(to_pointer_type(sym_expr.type())))); - - exprt new_retval; - // may fail if returning a constant like NULL - if(build_wrap(retval, new_retval, false)) - new_retval=build_unknown(abstract_ret_type, false); - - if(is_ptr_argument(abstract_ret_type)) - return value_assignments(dest, next, sym_expr, new_retval); - else - { - exprt lhs_deref=dereference_exprt(sym_expr, - sym_expr.type().subtype()); - - return value_assignments(dest, next, lhs_deref, new_retval); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_ret_val_name - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -irep_idt array_abstractiont::abstract_ret_val_name(const symbolt &fct) -{ - return id2string(fct.module)+ - "::"+id2string(fct.base_name)+ - "::$return_value_str_abst"+arg_suffix; -} - -/*******************************************************************\ - -Function: array_abstractiont::has_string_macros - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::has_string_macros(const exprt &expr) -{ - if(expr.id()=="is_zero_string" || - expr.id()=="zero_string_length" || - expr.id()=="buffer_size") - return true; - - forall_operands(it, expr) - if(has_string_macros(*it)) - return true; - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::replace_string_macros - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::replace_string_macros( - exprt &expr, - bool lhs, - const source_locationt &location) -{ - if(expr.id()=="is_zero_string") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), IS_ZERO, lhs, location); - expr.swap(tmp); - } - else if(expr.id()=="zero_string_length") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), LENGTH, lhs, location); - expr.swap(tmp); - } - else if(expr.id()=="buffer_size") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), SIZE, false, location); - expr.swap(tmp); - } - else - Forall_operands(it, expr) - replace_string_macros(*it, lhs, location); -} - -/*******************************************************************\ - -Function: array_abstractiont::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build( - const exprt &pointer, - whatt what, - bool write, - const source_locationt &location) -{ - // take care of pointer typecasts now - if(pointer.id()==ID_typecast) - { - // cast from another pointer type? - assert(pointer.operands().size()==1); - if(pointer.op0().type().id()!=ID_pointer) - return build_unknown(what, write); - - // recursive call - return build(pointer.op0(), what, write, location); - } - - exprt str_struct; - if(build_wrap(pointer, str_struct, write)) assert(false); - - exprt result=member(str_struct, what); - - if(what==LENGTH || what==SIZE) - { - // adjust for offset - exprt pointer_offset(ID_pointer_offset, size_type()); - pointer_offset.copy_to_operands(pointer); - if(pointer_offset.is_not_nil() && - !pointer_offset.is_zero()) - result=minus_exprt(result, pointer_offset); - } - - return result; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_abstraction_type - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const typet& array_abstractiont::build_abstraction_type(const typet &type) -{ - const typet &eff_type=ns.follow(type); - abstraction_types_mapt::const_iterator map_entry= - abstraction_types_map.find(eff_type); - if(map_entry!=abstraction_types_map.end()) - return map_entry->second; - - abstraction_types_mapt tmp; - tmp.swap(abstraction_types_map); - build_abstraction_type_rec(eff_type, tmp); - - abstraction_types_map.swap(tmp); - map_entry=tmp.find(eff_type); - assert(map_entry!=tmp.end()); - return abstraction_types_map.insert( - std::make_pair(eff_type, map_entry->second)).first->second; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_abstraction_type_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const typet& array_abstractiont::build_abstraction_type_rec(const typet &type, - const abstraction_types_mapt &known) -{ - const typet &eff_type=ns.follow(type); - abstraction_types_mapt::const_iterator known_entry=known.find(eff_type); - if(known_entry!=known.end()) - return known_entry->second; - - ::std::pair< abstraction_types_mapt::iterator, bool > map_entry( - abstraction_types_map.insert(::std::make_pair( - eff_type, nil_typet()))); - if(!map_entry.second) - return map_entry.first->second; - - if(eff_type.id()==ID_array || eff_type.id()==ID_pointer) - { - map_entry.first->second=string_struct; - - /* - // char* or void* or char[] - if(is_char_type(eff_type.subtype()) || - eff_type.subtype().id() == ID_empty) - map_entry.first->second=string_struct; - else - { - const typet& subt=build_abstraction_type_rec(eff_type.subtype(), known); - if(!subt.is_nil()) - { - if(eff_type.id()==ID_array) - map_entry.first->second= - array_typet(subt, to_array_type(eff_type).size()); - else - map_entry.first->second= - pointer_typet(subt); - } - } - */ - } - else if(eff_type.id()==ID_struct || eff_type.id()==ID_union) - { - const struct_union_typet &struct_union_type=to_struct_union_type(eff_type); - const struct_union_typet::componentst &components= - struct_union_type.components(); - - struct_union_typet::componentst new_comp; - for(struct_union_typet::componentst::const_iterator - it=components.begin(); - it!=components.end(); - it++) - { - if(it->get_anonymous()) continue; - typet subt=build_abstraction_type_rec(it->type(), known); - if(subt.is_nil()) continue; // also precludes structs with pointers to the same datatype - - new_comp.push_back(struct_union_typet::componentt()); - new_comp.back().set_name(it->get_name()); - new_comp.back().set_pretty_name(it->get_pretty_name()); - new_comp.back().type()=subt; - } - if(!new_comp.empty()) - { - struct_union_typet t(eff_type.id()); - t.components().swap(new_comp); - map_entry.first->second=t; - } - } - - return map_entry.first->second; -} - -/*******************************************************************\ - -Function: array_abstractiont::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build(const exprt &object, exprt &dest, bool write) -{ - const typet &abstract_type=build_abstraction_type(object.type()); - if(abstract_type.is_nil()) { - return true; - } - - if(object.id()==ID_typecast) - { - if(build(to_typecast_expr(object).op(), dest, write)) - { - return true; - } - - return !(type_eq(dest.type(), abstract_type, ns) || - (dest.type().id()==ID_array && - abstract_type.id()==ID_pointer && - type_eq(dest.type().subtype(), abstract_type.subtype(), ns))); - } - - if(object.id()==ID_string_constant) - { - mp_integer str_len=strlen(object.get(ID_value).c_str()); - return build_symbol_constant(str_len, str_len+1, dest); - } - - if(object.id()==ID_array && is_char_type(object.type().subtype())) - return build_array(to_array_expr(object), dest, write); - - // other constants aren't useful - if(object.is_constant()) - return true; - - if(object.id()==ID_symbol) - return build_symbol(to_symbol_expr(object), dest); - - if(object.id()==ID_if) - return build_if(to_if_expr(object), dest, write); - - if(object.id()==ID_member) - { - const member_exprt &o_mem=to_member_expr(object); - dest=member_exprt(exprt(), o_mem.get_component_name(), abstract_type); - return build_wrap(o_mem.struct_op(), dest.op0(), write); - } - - if(object.id()==ID_dereference) - { - const dereference_exprt &o_deref=to_dereference_expr(object); - dest=dereference_exprt(exprt(), abstract_type); - return build_wrap(o_deref.pointer(), dest.op0(), write); - } - - if(object.id()==ID_index) - { - const index_exprt &o_index=to_index_expr(object); - dest=index_exprt(exprt(), o_index.index(), abstract_type); - return build_wrap(o_index.array(), dest.op0(), write); - } - - // handle pointer stuff - if(object.type().id()==ID_pointer) - { - return build_pointer(object, dest, write); - } - - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_if - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_if(const if_exprt &o_if, - exprt &dest, bool write) -{ - if_exprt new_if(o_if.cond(), exprt(), exprt()); - - // recursive calls - bool op1_err=build_wrap(o_if.true_case(), new_if.true_case(), write); - bool op2_err=build_wrap(o_if.false_case(), new_if.false_case(), write); - if(op1_err && op2_err) return true; - // at least one of them gave proper results - if(op1_err) - { - new_if.type()=new_if.false_case().type(); - new_if.true_case()=build_unknown(new_if.type(), write); - } - else if(op2_err) - { - new_if.type()=new_if.true_case().type(); - new_if.false_case()=build_unknown(new_if.type(), write); - } - else - new_if.type()=new_if.true_case().type(); - - dest.swap(new_if); - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_array - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_array(const array_exprt &object, - exprt &dest, bool write) -{ - assert(is_char_type(object.type().subtype())); - - // writing is invalid - if(write) return true; - - const exprt &a_size=to_array_type(object.type()).size(); - mp_integer size; - // don't do anything, if we cannot determine the size - if (to_integer(a_size, size)) return true; - assert(size==object.operands().size()); - - exprt::operandst::const_iterator it=object.operands().begin(); - for(mp_integer i=0; iis_zero()) - return build_symbol_constant(i, size, dest); - - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_pointer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_pointer(const exprt &object, - exprt &dest, bool write) -{ - assert(object.type().id()==ID_pointer); - - pointer_arithmetict ptr(object); - if(ptr.pointer.id()==ID_address_of) - { - const address_of_exprt &a=to_address_of_expr(ptr.pointer); - - if(a.object().id()==ID_index) - return build_wrap(to_index_expr(a.object()).array(), dest, write); - - // writing is invalid - if(write) return true; - - if(build_wrap(a.object(), dest, write)) return true; - //dest=address_of_exprt(dest); - return false; - } - else if(ptr.pointer.id()==ID_symbol && - is_char_type(object.type().subtype())) - // recursive call; offset will be handled by pointer_offset in SIZE/LENGTH - // checks - return build_wrap(ptr.pointer, dest, write); - - // we don't handle other pointer arithmetic - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build_unknown(whatt what, bool write) -{ - typet type=build_type(what); - - if(write) - return exprt("NULL-object", type); - - exprt result; - - switch(what) - { - case IS_ZERO: - result=false_exprt(); - break; - - case LENGTH: - case SIZE: - result=side_effect_expr_nondett(type); - break; - } - - return result; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build_unknown(const typet &type, bool write) -{ - if(write) - return exprt("NULL-object", type); - - // create an uninitialized dummy symbol - // because of a lack of contextual information we can't build a nice name - // here, but moving that into locals should suffice for proper operation - irep_idt identifier="$tmp::nondet_str#str$"+i2string(++temporary_counter); - // ensure decl and initialization - locals[identifier]=identifier; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.name=identifier; - new_symbol.module="$tmp"; - new_symbol.base_name=identifier; - new_symbol.mode=ID_C; - new_symbol.pretty_name=identifier; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); - - return ns.lookup(identifier).symbol_expr(); -} - -/*******************************************************************\ - -Function: array_abstractiont::build_symbol - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_symbol(const symbol_exprt &sym, exprt &dest) -{ - const symbolt &symbol=ns.lookup(sym.get_identifier()); - - const typet &abstract_type=build_abstraction_type(symbol.type); - assert(!abstract_type.is_nil()); - - irep_idt identifier=""; - - if(current_args.find(symbol.name)!=current_args.end()) - identifier=id2string(symbol.name)+arg_suffix; - else - { - identifier=id2string(symbol.name)+sym_suffix; - if(symbol_table.symbols.find(identifier)==symbol_table.symbols.end()) - build_new_symbol(symbol, identifier, abstract_type); - } - - const symbolt &str_symbol=ns.lookup(identifier); - dest=str_symbol.symbol_expr(); - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_new_symbol - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::build_new_symbol(const symbolt &symbol, - const irep_idt &identifier, const typet &type) -{ - if(!symbol.is_static_lifetime) - locals[symbol.name]=identifier; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=symbol.location; - new_symbol.name=identifier; - new_symbol.module=symbol.module; - new_symbol.base_name=id2string(symbol.base_name)+sym_suffix; - new_symbol.mode=symbol.mode; - new_symbol.pretty_name=id2string( - symbol.pretty_name.empty()?symbol.base_name:symbol.pretty_name)+sym_suffix; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=symbol.is_static_lifetime; - new_symbol.is_thread_local=symbol.is_thread_local; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); - - if(symbol.is_static_lifetime) - { - goto_programt::targett dummy_loc=initialization.add_instruction(); - dummy_loc->source_location=symbol.location; - make_decl_and_def(initialization, dummy_loc, identifier, symbol.name); - initialization.instructions.erase(dummy_loc); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::build_symbol_constant - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_symbol_constant(const mp_integer &zero_length, - const mp_integer &buf_size, exprt &dest) -{ - irep_idt base="$string_constant_str_"+integer2string(zero_length) - +"_"+integer2string(buf_size); - irep_idt identifier="array_abstraction::"+id2string(base); - - if(symbol_table.symbols.find(identifier)== - symbol_table.symbols.end()) - { - symbolt new_symbol; - new_symbol.type=string_struct; - new_symbol.value.make_nil(); - new_symbol.name=identifier; - new_symbol.base_name=base; - new_symbol.mode=ID_C; - new_symbol.pretty_name=base; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=true; - new_symbol.is_thread_local=false; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=false; - - { - struct_exprt value(string_struct); - value.operands().resize(3); - - value.op0()=true_exprt(); - value.op1()=from_integer(zero_length, build_type(LENGTH)); - value.op2()=from_integer(buf_size, build_type(SIZE)); - - // initialization - goto_programt::targett assignment1=initialization.add_instruction(); - assignment1->make_assignment(); - assignment1->code=code_assignt(new_symbol.symbol_expr(), value); - } - - symbol_table.move(new_symbol); - } - - dest=symbol_exprt(identifier, string_struct); - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::move_lhs_arithmetic - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::move_lhs_arithmetic(exprt &lhs, exprt &rhs) -{ - if(lhs.id()==ID_minus) - { - // move op1 to rhs - exprt rest=lhs.op0(); - rhs=plus_exprt(rhs, lhs.op1()); - rhs.type()=lhs.type(); - lhs=rest; - } -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_pointer_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_pointer_assign( - goto_programt &dest, - goto_programt::targett target) -{ - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt rhs=assign.rhs(); - exprt *rhsp=&(assign.rhs()); - - while(rhsp->id()==ID_typecast) - rhsp=&(rhsp->op0()); - - const typet& abstract_type=build_abstraction_type(lhs.type()); - if(abstract_type.is_nil()) return target; - - exprt new_lhs, new_rhs; - if(build_wrap(lhs, new_lhs, true)) return target; - - bool unknown=(abstract_type!=build_abstraction_type(rhsp->type()) || - build_wrap(rhs, new_rhs, false)); - if(unknown) - new_rhs=build_unknown(abstract_type, false); - - if(!unknown) - { - goto_programt::instructiont assignment; - assignment.make_assignment(); - assignment.source_location=target->source_location; - assignment.function=target->function; - assignment.code=code_assignt(new_lhs, new_rhs); - assignment.code.add_source_location()=target->source_location; - dest.insert_before_swap(target, assignment); - ++target; - - return target; - } - else - { - return value_assignments(dest, target, new_lhs, new_rhs); - } -} - - -goto_programt::targett array_abstractiont::bounds_check( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &index) -{ - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - goto_programt tmp; - goto_programt::targett assertion0=tmp.add_instruction(); - exprt upper_bound=binary_relation_exprt(typecast_exprt(index, size.type()), ID_lt, size); - assertion0->make_assertion(upper_bound); - assertion0->source_location=target->source_location; - assertion0->source_location.set("property", "array"); - assertion0->source_location.set("comment", "upper bound for index expression"); - - goto_programt::targett assertion1=tmp.add_instruction(); - exprt lower_bound(binary_relation_exprt(typecast_exprt(index, size.type()), ID_ge, gen_zero(build_type(SIZE)))); - - assertion1->make_assertion(lower_bound); - assertion1->source_location=target->source_location; - assertion1->source_location.set("property", "array"); - assertion1->source_location.set("comment", "lower bound for index expression"); - - dest.insert_before_swap(target, tmp); - ++target; - ++target; - - return target; -} - - -goto_programt::targett array_abstractiont::read_rec( - const exprt &src, - goto_programt &dest, - goto_programt::targett target) -{ - if(src.id()==ID_symbol) - { - } - else if(src.id()==ID_index) - { - assert(src.operands().size()==2); - const index_exprt index_expr=to_index_expr(src); - target=read_rec(src.op0(), dest, target); - target=read_rec(src.op1(), dest, target); - - exprt tmp; - - if(!build_wrap(index_expr.array(), tmp, false)) - { - target=bounds_check(dest, target, tmp, src.op1()); - } - } - else if(src.id()==ID_dereference) - { - pointer_arithmetict ptr(to_dereference_expr(src).pointer()); - - exprt tmp; - - if(!build_wrap(ptr.pointer, tmp, false)) - { - exprt size=member(tmp, SIZE); - assert(size.is_not_nil()); - - exprt object=to_dereference_expr(src).pointer(); - - if(object.id()==ID_plus) - { - exprt index=object.op1(); - - target=bounds_check(dest, target, tmp, index); - } - } - } - else if(src.id()==ID_member) - { - member_exprt tmp=to_member_expr(src); - target=read_rec(tmp.struct_op(), dest, target); - } - else if(src.id()==ID_string_constant) - { - } - else if(src.id()==ID_label) - { - } - else if(src.id()==ID_byte_extract_big_endian || - src.id()==ID_byte_extract_little_endian) - { - assert(src.operands().size()==2); - target=read_rec(src.op0(), dest, target); - target=read_rec(src.op1(), dest, target); - } - else if(src.id()==ID_if) - { - if_exprt if_expr=to_if_expr(src); - target=read_rec(if_expr.true_case(), dest, target); - target=read_rec(if_expr.false_case(), dest, target); - target=read_rec(if_expr.cond(), dest, target); - - } - else if(src.id()==ID_typecast) - { - target=read_rec(src.op0(), dest, target); - } - else { - - forall_operands(it, src) - { - target=read_rec(*it, dest, target); - } - } - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_char_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_char_assign( - goto_programt &dest, - goto_programt::targett target) -{ - - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt *rhsp=&(assign.rhs()); - - while(rhsp->id()==ID_typecast) - rhsp=&(rhsp->op0()); - - // BW - if(lhs.id()==ID_index) - { - - const index_exprt &i_lhs=to_index_expr(lhs); - - exprt new_lhs; - if(!build_wrap(i_lhs.array(), new_lhs, true)) - { - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - exprt index=i_lhs.index(); - - target=bounds_check(dest, target, new_lhs, index); - return target; - } - } - else if(lhs.id()==ID_dereference) - { - pointer_arithmetict ptr(to_dereference_expr(lhs).pointer()); - exprt new_lhs; - if(!build_wrap(ptr.pointer, new_lhs, true)) - { - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - exprt object=to_dereference_expr(lhs).pointer(); - - if(object.id()==ID_plus) - { - exprt index=object.op1(); - - target=bounds_check(dest, target, new_lhs, index); - } - } - } - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::char_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::char_assign( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &lhs, - const exprt &rhs) -{ - goto_programt tmp; - - const exprt i1=member(new_lhs, IS_ZERO); - assert(i1.is_not_nil()); - - goto_programt::targett assignment1=tmp.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=target->source_location; - assignment1->function=target->function; - assignment1->code=code_assignt(i1, true_exprt()); - assignment1->code.add_source_location()=target->source_location; - - goto_programt::targett assignment2=tmp.add_instruction(); - assignment2->make_assignment(); - assignment2->source_location=target->source_location; - assignment2->function=target->function; - assignment2->code=code_assignt(lhs, rhs); - assignment2->code.add_source_location()=target->source_location; - - move_lhs_arithmetic( - assignment2->code.op0(), - assignment2->code.op1()); - - dest.insert_before_swap(target, tmp); - ++target; - ++target; - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs) -{ - if(rhs.id()==ID_if) - return value_assignments_if(dest, target, lhs, to_if_expr(rhs)); - - assert(type_eq(lhs.type(), rhs.type(), ns)); - - if(lhs.type().id()==ID_array) - { - const exprt &a_size=to_array_type(lhs.type()).size(); - mp_integer size; - // don't do anything, if we cannot determine the size - if (to_integer(a_size, size)) return target; - for(mp_integer i=0; iget_name().empty()); - - target=value_assignments(dest, target, - member_exprt(lhs, it->get_name(), it->type()), - member_exprt(rhs, it->get_name(), it->type())); - } - } - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments_if - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments_if( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const if_exprt& rhs) -{ - goto_programt tmp; - - goto_programt::targett goto_else=tmp.add_instruction(GOTO); - goto_programt::targett goto_out=tmp.add_instruction(GOTO); - goto_programt::targett else_target=tmp.add_instruction(SKIP); - goto_programt::targett out_target=tmp.add_instruction(SKIP); - - goto_else->function=target->function; - goto_else->source_location=target->source_location; - goto_else->make_goto(else_target, rhs.cond()); - goto_else->guard.make_not(); - - goto_out->function=target->function; - goto_out->source_location=target->source_location; - goto_out->make_goto(out_target, true_exprt()); - - else_target->function=target->function; - else_target->source_location=target->source_location; - - out_target->function=target->function; - out_target->source_location=target->source_location; - - value_assignments(tmp, goto_out, lhs, rhs.true_case()); - value_assignments(tmp, else_target, lhs, rhs.false_case()); - - goto_programt::targett last=target; - ++last; - dest.insert_before_swap(target, tmp); - --last; - - return last; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments_string_struct - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments_string_struct( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs) -{ - // copy all the values - goto_programt tmp; - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, IS_ZERO), - member(rhs, IS_ZERO)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, LENGTH), - member(rhs, LENGTH)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, SIZE), - member(rhs, SIZE)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - goto_programt::targett last=target; - ++last; - dest.insert_before_swap(target, tmp); - --last; - - return last; -} - -/*******************************************************************\ - -Function: array_abstractiont::member - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::member(const exprt &a, whatt what) -{ - if(a.is_nil()) return a; - assert(type_eq(a.type(), string_struct, ns) || - is_ptr_string_struct(a.type())); - - member_exprt result(build_type(what)); - result.struct_op()=a.type().id()==ID_pointer? - dereference_exprt(a, string_struct):a; - - switch(what) - { - case IS_ZERO: result.set_component_name("is_zero"); break; - case SIZE: result.set_component_name("size"); break; - case LENGTH: result.set_component_name("length"); break; - } - - return result; -} - diff --git a/src/summarizer/array_abstraction.h b/src/summarizer/array_abstraction.h deleted file mode 100644 index c1c203d44..000000000 --- a/src/summarizer/array_abstraction.h +++ /dev/null @@ -1,205 +0,0 @@ -/*******************************************************************\ - -Module: String Abstraction - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GOTO_PROGRAMS_array_abstraction_H -#define CPROVER_GOTO_PROGRAMS_array_abstraction_H - -#include -#include -#include -#include - -#include - -/*******************************************************************\ - - Class: array_abstractiont - - Purpose: - -\*******************************************************************/ - -class array_abstractiont:public message_streamt -{ -public: - array_abstractiont( - symbol_tablet &_symbol_table, - message_handlert &_message_handler); - - void operator()(goto_programt &dest); - void operator()(goto_functionst &dest); - -protected: - const std::string arg_suffix; - std::string sym_suffix; - symbol_tablet &symbol_table; - namespacet ns; - unsigned temporary_counter; - - typedef ::std::map< typet, typet > abstraction_types_mapt; - abstraction_types_mapt abstraction_types_map; - - ::std::set< irep_idt > current_args; - - static bool has_string_macros(const exprt &expr); - - void replace_string_macros( - exprt &expr, - bool lhs, - const source_locationt &location); - - void move_lhs_arithmetic(exprt &lhs, exprt &rhs); - - bool is_char_type(const typet &type) const - { - if(type.id()==ID_symbol) - return is_char_type(ns.follow(type)); - - /* - if(type.id()!=ID_signedbv && - type.id()!=ID_unsignedbv) - return false; - - return to_bitvector_type(type).get_width()==config.ansi_c.char_width; - - */ - return to_bitvector_type(type).get_width()==config.ansi_c.char_width - || to_bitvector_type(type).get_width()==config.ansi_c.int_width; - } - - inline bool is_ptr_string_struct(const typet &type) const; - - void make_type(exprt &dest, const typet &type) - { - if(dest.is_not_nil() && - ns.follow(dest.type())!=ns.follow(type)) - dest.make_typecast(type); - } - - goto_programt::targett abstract(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_assign(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_pointer_assign(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_char_assign(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett char_assign( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &lhs, - const exprt &rhs); - - void abstract_function_call(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett abstract_return(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett value_assignments(goto_programt &dest, - goto_programt::targett it, - const exprt& lhs, const exprt& rhs); - - goto_programt::targett value_assignments_if( - goto_programt &dest, - goto_programt::targett target, - const exprt &lhs, const if_exprt &rhs); - - goto_programt::targett value_assignments_string_struct( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs); - - typedef enum { IS_ZERO, LENGTH, SIZE } whatt; - - static typet build_type(whatt what); - exprt build( - const exprt &pointer, - whatt what, - bool write, - const source_locationt &location); - - bool build(const exprt &object, exprt &dest, bool write); - bool build_wrap(const exprt &object, exprt &dest, bool write); - bool build_if(const if_exprt &o_if, exprt &dest, bool write); - bool build_array(const array_exprt &object, exprt &dest, bool write); - bool build_symbol(const symbol_exprt &sym, exprt &dest); - bool build_symbol_constant(const mp_integer &zero_length, - const mp_integer &buf_size, exprt &dest); - - exprt build_unknown(whatt what, bool write); - exprt build_unknown(const typet &type, bool write); - const typet& build_abstraction_type(const typet &type); - const typet& build_abstraction_type_rec(const typet &type, - const abstraction_types_mapt &known); - bool build_pointer(const exprt &object, exprt &dest, bool write); - void build_new_symbol(const symbolt &symbol, - const irep_idt &identifier, const typet &type); - - exprt member(const exprt &a, whatt what); - irep_idt abstract_ret_val_name(const symbolt &fct); - - typet string_struct; - goto_programt initialization; - - typedef hash_map_cont localst; - localst locals; - - void abstract(goto_programt &dest); - - void add_str_arguments( - const irep_idt& name, - goto_functionst::goto_functiont &fct); - - void add_argument( - code_typet::parameterst &str_args, - const symbolt &fct_symbol, - const typet &type, - const irep_idt &base_name, - const irep_idt &identifier); - - void make_decl_and_def(goto_programt &dest, goto_programt::targett ref_instr, - const irep_idt &identifier, const irep_idt &source_sym); - - exprt make_val_or_dummy_rec(goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, const typet &source_type); - - symbol_exprt add_dummy_symbol_and_value( - goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, - const irep_idt &component_name, - const typet &type, - const typet &source_type); - - void declare_define_locals(goto_programt &dest); - - // BW - goto_programt::targett read_rec( - const exprt &src, - goto_programt &dest, - goto_programt::targett target); - - // BW - goto_programt::targett bounds_check( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &index); -}; - -// keep track of length of strings - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_programt &dest); - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_functionst &dest); - -#endif diff --git a/src/summarizer/function_signature.cpp b/src/summarizer/function_signature.cpp deleted file mode 100644 index de70c11b0..000000000 --- a/src/summarizer/function_signature.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/*******************************************************************\ - -Module: Signature of a Function - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../ssa/local_ssa.h" -#include "function_signature.h" - -/*******************************************************************\ - -Function: update_function_signature - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void update_function_signature( - const local_SSAt &SSA, - class jsont &dest) -{ - jsont &j_signature=dest["signature"]; - jsont &j_reads=j_signature["reads"]; - jsont &j_modifies=j_signature["modifies"]; - - j_signature.kind=jsont::J_OBJECT; - j_reads=jsont::json_array(); - j_modifies=jsont::json_array(); - - std::set modifies; - std::set reads; - - for(assignmentst::assignment_mapt::const_iterator - a_it=SSA.assignments.assignment_map.begin(); - a_it!=SSA.assignments.assignment_map.end(); - a_it++) - { - for(assignmentst::objectst::const_iterator - o_it=a_it->second.begin(); - o_it!=a_it->second.end(); - o_it++) - { - modifies.insert(o_it->get_identifier()); - } - } - - for(ssa_objectst::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - reads.insert(o_it->get_identifier()); - } - - for(std::set::const_iterator it=reads.begin(); - it!=reads.end(); - it++) - { - j_reads.array.push_back(jsont::json_string(id2string(*it))); - } - - for(std::set::const_iterator it=modifies.begin(); - it!=modifies.end(); - it++) - { - j_modifies.array.push_back(jsont::json_string(id2string(*it))); - } - -} - diff --git a/src/summarizer/function_signature.h b/src/summarizer/function_signature.h deleted file mode 100644 index 16a7eaf09..000000000 --- a/src/summarizer/function_signature.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Signature of a Function - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H -#define CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H - -#include - -void update_function_signature(const class local_SSAt &, jsont &); - -#endif - diff --git a/src/summarizer/instrument_goto.cpp b/src/summarizer/instrument_goto.cpp deleted file mode 100644 index 7cd4b4f3f..000000000 --- a/src/summarizer/instrument_goto.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/*******************************************************************\ - -Module: Instrument Goto Program with Inferred Information - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include "instrument_goto.h" - - - -local_SSAt::locationt find_loop_by_guard(const local_SSAt &SSA, - const symbol_exprt &guard) -{ - std::string gstr = id2string(guard.get_identifier()); - unsigned pos1 = gstr.find("#")+1; - unsigned pos2 = gstr.find("%",pos1); - unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); - - local_SSAt::nodest::const_iterator n_it =SSA.nodes.begin(); - - for(; n_it != SSA.nodes.end(); n_it++) - { - if(n_it->location->location_number == n) { - // find end of loop - break; - } - } - - if(n_it->loophead==SSA.nodes.end()) - return n_it->location; - else - return n_it->loophead->location; -} - - -void instrument_gotot::instrument_instruction( - const exprt &expr, - goto_programt &dest, - goto_programt::targett &target) -{ - - goto_programt::targett where=target; - - std::cout << "target " << target->type << " : " << target->source_location << std::endl; - - for(;;++where) - { - if(where->is_goto() && where->get_target()==target) - break; - } - - - goto_programt tmp; - - goto_programt::targett assumption=tmp.add_instruction(); - assumption->make_assumption(expr); - assumption->source_location=target->source_location; - assumption->source_location.set_comment("invariant generated by 2LS"); - - dest.insert_before_swap(where, tmp); - - #ifdef DEBUG - std::cout << "instrumenting instruction " << std::endl; - #endif - - - //dest.update(); -} - -extern -void purify_identifiers(exprt &expr); - - -void instrument_gotot::instrument_body( - const local_SSAt &SSA, - const exprt &expr, - goto_functionst::goto_functiont &function -) -{ - //expected format (/\_j g_j) => inv - const exprt &impl = expr.op0(); - exprt inv = expr.op1(); //copy - - std::cout << "Invariant " << from_expr(inv) << std::endl; - - purify_identifiers(inv); - - local_SSAt::locationt loc; - if(impl.id()==ID_symbol) - { - loc = find_loop_by_guard(SSA,to_symbol_expr(impl)); - } - else if(impl.id()==ID_and) - { - assert(impl.op0().id()==ID_symbol); - loc = find_loop_by_guard(SSA,to_symbol_expr(impl.op0())); - - } - else assert(false); - - - Forall_goto_program_instructions(it, function.body) - if(it==loc) - { - - instrument_instruction(inv, function.body, it); - break; - } -} - - - -void instrument_gotot::instrument_function( - const irep_idt &function_name, - goto_functionst::goto_functiont &function) -{ - #ifdef DEBUG - std::cout << "instrumenting function " << function_name << std::endl; - #endif - - if(!summary_db.exists(function_name)) - return; - - const summaryt summary=summary_db.get(function_name); - - if(!ssa_db.exists(function_name)) - return; - - const local_SSAt &SSA=ssa_db.get(function_name); - - if(summary.fw_invariant.is_nil()) return; - if(summary.fw_invariant.is_true()) return; - - //expected format /\_i g_i => inv_i - if(summary.fw_invariant.id()==ID_implies) - { - instrument_body(SSA, summary.fw_invariant, function); - } - else if(summary.fw_invariant.id()==ID_and) - { - for(unsigned i=0; i::function_mapt - function_mapt; - - function_mapt - &function_map=goto_functions.function_map; - - for(function_mapt::iterator - fit=function_map.begin(); - fit!=function_map.end(); - ++fit) - { - instrument_function(fit->first, fit->second); - } - - goto_model.goto_functions.update(); -} diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp deleted file mode 100644 index beb1b3516..000000000 --- a/src/summarizer/preprocessing_util.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "summarizer_parse_options.h" - -/*******************************************************************\ - -Function: summarizer_parse_optionst::inline_main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parse_optionst::inline_main(goto_modelt &goto_model) -{ - goto_programt &main = goto_model.goto_functions.function_map[ID__start].body; - goto_programt::targett target = main.instructions.begin(); - while(target!=main.instructions.end()) - { - if(target->is_function_call()) - { - const code_function_callt &code_function_call= - to_code_function_call(target->code); - irep_idt fname = code_function_call.function().get(ID_identifier); - - debug() << "Inlining " << fname << eom; - - goto_programt tmp; - tmp.copy_from(goto_model.goto_functions.function_map[fname].body); - (--tmp.instructions.end())->make_skip(); - goto_model.goto_functions.function_map.erase(fname); - - goto_programt::targett next_target(target); - target->make_skip(); - next_target++; - main.instructions.splice(next_target, tmp.instructions); - target=next_target; - } - else target++; - } - - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: summarizer_parse_optionst::propagate_constants - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parse_optionst::propagate_constants(goto_modelt &goto_model) -{ - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - constant_propagator_ait(f_it->second,ns); - } -} - -/*******************************************************************\ - -Function: summarizer_parse_optionst::nondet_locals - - Inputs: - - Outputs: - - Purpose: explicitly assign a nondet_symbol to local variables - this is required by the unwinder, which would be unable - to recognise in which scope variables have been declared - -\*******************************************************************/ - -void summarizer_parse_optionst::nondet_locals(goto_modelt &goto_model) -{ - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(i_it->is_decl()) - { - const code_declt& decl = to_code_decl(i_it->code); - side_effect_expr_nondett nondet(decl.symbol().type()); - goto_programt::targett t = f_it->second.body.insert_after(i_it); - t->make_assignment(); - code_assignt c(decl.symbol(),nondet); - t->code.swap(c); - t->source_location = i_it->source_location; - } - } - } - goto_model.goto_functions.update(); -} - -/*******************************************************************\ - -Function: goto_unwind - - Inputs: - - Outputs: - - Purpose: unwind all loops - -\*******************************************************************/ - -void summarizer_parse_optionst::goto_unwind(goto_modelt &goto_model, unsigned k) -{ - - typedef std::vector > loopst; - - Forall_goto_functions(f_it, goto_model.goto_functions) - { - goto_programt &body=f_it->second.body; - - loopst loops; - Forall_goto_program_instructions(i_it, body) - { - if(i_it->is_backwards_goto()) - { - goto_programt::targett loop_head = i_it->get_target(); - goto_programt::targett loop_exit = i_it; - bool has_goto_into_loop = false; - - goto_programt::targett it = loop_head; - if(it!=loop_exit) it++; - for(; it!=loop_exit; it++) - { - for( std::set::iterator - s_it = it->incoming_edges.begin(); - s_it!=it->incoming_edges.end(); ++s_it) - { - if((*s_it)->is_goto() && - (*s_it)->location_number < loop_head->location_number) - { - has_goto_into_loop = true; - break; - } - } - if(has_goto_into_loop) break; - } - if(has_goto_into_loop) - { - status() << "Unwinding jump into loop" << eom; - loops.push_back(loopst::value_type(++loop_exit,loop_head)); - } - } - } - - for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) - { - std::vector iteration_points; - unwind(body,l_it->second,l_it->first,k,iteration_points); - assert(iteration_points.size()==2); - goto_programt::targett t=body.insert_before(l_it->first); - t->make_goto(); - t->targets.push_back(iteration_points.front()); - } - } - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: remove_multiple_dereferences - - Inputs: - - Outputs: - - Purpose: temporary fix to circumvent ssa_dereference problem - -\*******************************************************************/ - -void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen) -{ - if(expr.id()==ID_member) - { - member_exprt &member_expr = to_member_expr(expr); - if(member_expr.compound().id()==ID_dereference) - { - dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); - remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); - if(deref_seen) - { - symbolt new_symbol; - new_symbol.type=member_expr.type(); - new_symbol.name="$deref"+i2string(var_counter++); - new_symbol.base_name=new_symbol.name; - new_symbol.pretty_name=new_symbol.name; - goto_model.symbol_table.add(new_symbol); - goto_programt::targett t_new = body.insert_before(t); - t_new->make_assignment(); - t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); - expr = new_symbol.symbol_expr(); - for(std::set::iterator t_it = - t->incoming_edges.begin(); - t_it != t->incoming_edges.end(); ++t_it) - { - (*t_it)->targets.clear(); - (*t_it)->targets.push_back(t_new); - } - body.compute_location_numbers(); - body.compute_target_numbers(); - body.compute_incoming_edges(); - } - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); -} - -void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) -{ - unsigned var_counter = 0; - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(i_it->is_goto()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - i_it->guard, - var_counter, false); - } - else if(i_it->is_assign()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).lhs(), - var_counter, false); - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).rhs(), - var_counter, false); - } - } - } -} diff --git a/src/summarizer/ssa_db.cpp b/src/summarizer/ssa_db.cpp deleted file mode 100644 index 144d8fd6c..000000000 --- a/src/summarizer/ssa_db.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/*******************************************************************\ - -Module: Storage for Function SSAs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "ssa_db.h" diff --git a/src/summarizer/ssa_db.h b/src/summarizer/ssa_db.h deleted file mode 100644 index faa5b772c..000000000 --- a/src/summarizer/ssa_db.h +++ /dev/null @@ -1,72 +0,0 @@ -/*******************************************************************\ - -Module: Storage for Function SSAs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_LOCAL_SSA_DB_H -#define CPROVER_LOCAL_SSA_DB_H - -#include - -#include "../ssa/local_ssa.h" -#include "../ssa/unwindable_local_ssa.h" -#include "../domains/incremental_solver.h" -#include - -class ssa_dbt -{ -public: - typedef irep_idt function_namet; - typedef std::map functionst; - typedef std::map solverst; - - explicit ssa_dbt(const optionst &_options) - : options(_options) - { } - - ~ssa_dbt() - { - for(functionst::iterator it = store.begin(); - it != store.end(); it++) - delete it->second; - for(solverst::iterator it = the_solvers.begin(); - it != the_solvers.end(); it++) - delete it->second; - } - - local_SSAt &get(const function_namet &function_name) const - { return *store.at(function_name); } - - incremental_solvert &get_solver(const function_namet &function_name) - { - solverst::iterator it = the_solvers.find(function_name); - if(it!=the_solvers.end()) return *(it->second); - the_solvers[function_name] = - incremental_solvert::allocate(store.at(function_name)->ns, - options.get_bool_option("refine")); - return *the_solvers.at(function_name); - } - - functionst &functions() { return store; } - solverst &solvers() { return the_solvers; } - - bool exists(const function_namet &function_name) const - { return store.find(function_name)!=store.end(); } - - void create(const function_namet &function_name, - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) - { - store[function_name] = new unwindable_local_SSAt(goto_function,ns); - } - - protected: - const optionst &options; - functionst store; - solverst the_solvers; -}; - -#endif diff --git a/src/summarizer/summarizer.cpp b/src/summarizer/summarizer.cpp deleted file mode 100644 index 04c170e0f..000000000 --- a/src/summarizer/summarizer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -#include "function_signature.h" -#include "summary_db.h" -#include "summarizer.h" - -/*******************************************************************\ - -Function: summarizert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::operator()(const goto_modelt &goto_model) -{ - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - summarize(goto_model, f_it); -} - -/*******************************************************************\ - -Function: summarizert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::operator()( - const goto_modelt &goto_model, - const irep_idt &id) -{ - // analyze the given function only - - goto_functionst::function_mapt::const_iterator f_it= - goto_model.goto_functions.function_map.find(id); - - if(f_it==goto_model.goto_functions.function_map.end()) - throw "function not found"; - - summarize(goto_model, f_it); -} - -/*******************************************************************\ - -Function: summarizert::summarize - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::summarize( - const goto_modelt &goto_model, - const goto_functionst::function_mapt::const_iterator f_it) -{ - status() << "** Analyzing " << f_it->first << messaget::eom; - - summary_dbt summary_db; - - summary_db.set_message_handler(get_message_handler()); - summary_db.read(id2string(f_it->first)); - - const namespacet ns(goto_model.symbol_table); - - // build SSA - progress() << "Building SSA" << messaget::eom; - local_SSAt SSA(f_it->second, ns); - - // simplify, if requested - if(simplify) - { - progress() << "Simplifying" << messaget::eom; - ::simplify(SSA, ns); - } - - - // fixed-point for loops - #if 0 - progress() << "Fixed-point" << messaget::eom; - ssa_fixed_point(SSA); - #endif - - update_function_signature(SSA, summary_db.summary); - - summary_db.write(); -} - -/*******************************************************************\ - -Function: summarizert::report_statistics() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::report_statistics() -{ -} - diff --git a/src/summarizer/summarizer.h b/src/summarizer/summarizer.h deleted file mode 100644 index f4cc72785..000000000 --- a/src/summarizer/summarizer.h +++ /dev/null @@ -1,45 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_H -#define CPROVER_SUMMARIZER_H - -#include -#include - -#include - -#include "../ssa/local_ssa.h" - -class summarizert:public messaget -{ -public: - inline summarizert(): - simplify(false), - fixed_point(false) - { - } - - bool simplify, fixed_point; - - void operator()(const goto_modelt &); - void operator()(const goto_modelt &, const irep_idt &); - - // statistics - absolute_timet start_time; - time_periodt sat_time; - -protected: - void report_statistics(); - - void summarize( - const goto_modelt &, - const goto_functionst::function_mapt::const_iterator); -}; - -#endif diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h deleted file mode 100644 index 6b215b06c..000000000 --- a/src/summarizer/summarizer_base.h +++ /dev/null @@ -1,104 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Base - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BASE_H -#define CPROVER_SUMMARIZER_BASE_H - -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_baset : public messaget -{ - public: - explicit summarizer_baset(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - options(_options), - summary_db(_summary_db), - ssa_db(_ssa_db), - ssa_unwinder(_ssa_unwinder), - ssa_inliner(_ssa_inliner), - solver_instances(0), - solver_calls(0), - summaries_used(0), - termargs_computed(0) - {} - - typedef summaryt::predicatet preconditiont; - typedef irep_idt function_namet; - typedef local_SSAt function_bodyt; - typedef std::map preconditionst; - typedef ssa_dbt::functionst functionst; - typedef functionst::value_type functiont; - - virtual void summarize(); - virtual void summarize(const function_namet &entry_function); - - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } - unsigned get_number_of_summaries_used() { return summaries_used; } - unsigned get_number_of_termargs_computed() { return termargs_computed; } - - protected: - optionst &options; - summary_dbt &summary_db; - ssa_dbt &ssa_db; - ssa_unwindert &ssa_unwinder; - ssa_inlinert &ssa_inliner; - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) - { assert(false); } - - bool check_call_reachable(const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt& precondition, - bool forward); - - virtual exprt compute_calling_context( - const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool forward); - - virtual bool check_precondition(const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator node, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool context_sensitive); - - void get_assertions(const local_SSAt &SSA, - exprt::operandst &assertions); - - bool check_end_reachable(const function_namet &function_name, - local_SSAt &SSA, - const exprt &cond); - - //statistics - unsigned solver_instances; - unsigned solver_calls; - unsigned summaries_used; - unsigned termargs_computed; -}; - - -#endif diff --git a/src/summarizer/summarizer_bw.cpp b/src/summarizer/summarizer_bw.cpp deleted file mode 100644 index d6f7bd1d0..000000000 --- a/src/summarizer/summarizer_bw.cpp +++ /dev/null @@ -1,510 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizer_bwt::summarize() - - Inputs: - - Outputs: - - Purpose: analyze only functions reachable in a previous forward analysis - -\*******************************************************************/ - -void summarizer_bwt::summarize() -{ - status() << "\nBackward analysis..." << eom; - - exprt postcondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); - it!=ssa_db.functions().end(); it++) - { - status() << "\nSummarizing function " << it->first << eom; - if(summary_db.exists(it->first) && - summary_db.get(it->first).bw_precondition.is_nil()) - compute_summary_rec(it->first,postcondition,false); - else status() << "Skipping function " << it->first << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_bwt::summarize() - - Inputs: - - Outputs: - - Purpose: summarize from given entry point - -\*******************************************************************/ - -void summarizer_bwt::summarize(const function_namet &function_name) -{ - status() << "\nBackward analysis..." << eom; - - exprt postcondition = true_exprt(); //initial calling context - - status() << "\nSummarizing function " << function_name << eom; - if(summary_db.exists(function_name)) - { - compute_summary_rec(function_name,postcondition,true); - } - else status() << "Skipping function " << function_name << eom; -} - - -/*******************************************************************\ - -Function: summarizer_bwt::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - const summaryt &old_summary = summary_db.get(function_name); - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,old_summary, - postcondition,context_sensitive, - options.get_bool_option("sufficient")); - - status() << "Analyzing function " << function_name << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.bw_postcondition = postcondition; - - if(!options.get_bool_option("havoc")) - { - do_summary(function_name,SSA,old_summary,summary,context_sensitive); - } - - // store summary in db - summary_db.put(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } - -} - -/*******************************************************************\ - -Function: summarizer_bwt::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::do_summary(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive) -{ - bool sufficient = options.get_bool_option("sufficient"); - status() << "Computing preconditions" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - template_generator_summaryt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,false); - - exprt::operandst c; - c.push_back(old_summary.fw_precondition); - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,c); //backward summaries - collect_postconditions(function_name, SSA, summary, postcond,sufficient); - if(!sufficient) - { - c.push_back(conjunction(postcond)); - } - else //sufficient - { - c.push_back(not_exprt(conjunction(postcond))); - } - - if(!template_generator.out_vars().empty()) - { - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver,SSA,conjunction(c),template_generator); - analyzer.get_result(summary.bw_transformer,template_generator.inout_vars()); - analyzer.get_result(summary.bw_invariant,template_generator.loop_vars()); - analyzer.get_result(summary.bw_precondition,template_generator.out_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - } - else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - { - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << conjunction(c); - exprt result = true_exprt(); - if(solver()==decision_proceduret::D_UNSATISFIABLE) result = false_exprt(); - solver.pop_context(); - summary.bw_transformer = result; - summary.bw_invariant = result; - summary.bw_precondition = result; - } - - if(sufficient) - { - summary.bw_transformer = not_exprt(summary.bw_transformer); - summary.bw_invariant = not_exprt(summary.bw_invariant); - summary.bw_precondition = not_exprt(summary.bw_precondition); - } - - if(context_sensitive && !summary.bw_postcondition.is_true()) - { - summary.bw_transformer = - implies_exprt(summary.bw_postcondition,summary.bw_transformer); - summary.bw_invariant = - implies_exprt(summary.bw_postcondition,summary.bw_invariant); - summary.bw_precondition = - implies_exprt(summary.bw_postcondition,summary.bw_precondition); - } -} - -/*******************************************************************\ - -Function: summarizer_bwt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); - n_it != SSA.nodes.begin(); ) - { - n_it--; - - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!sufficient && - !check_call_reachable(function_name,SSA,n_it,f_it,postcondition,false)) - { - continue; - } - - if(!check_postcondition(function_name,SSA,n_it,f_it, - postcondition,context_sensitive)) - { - exprt postcondition_call = true_exprt(); - if(context_sensitive) - postcondition_call = compute_calling_context2( - function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,postcondition_call,context_sensitive); - summaries_used++; - } - } - } -} - - -/*******************************************************************\ - -Function: summarizer_bwt::collect_postconditions() - - Inputs: - - Outputs: - - Purpose: collects postconditions where precondition inference starts from - -\*******************************************************************/ - -void summarizer_bwt::collect_postconditions( - const function_namet &function_name, - const local_SSAt &SSA, - const summaryt &summary, - exprt::operandst &postconditions, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator - a_it = n_it->assertions.begin(); - a_it != n_it->assertions.end(); a_it++) - { - postconditions.push_back(*a_it); - } - } - /* if(termination) - { - if(!summary.termination_argument.is_nil()) - postconditions.push_back(summary.termination_argument); - }*/ - - exprt guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - if(!sufficient) - postconditions.push_back(and_exprt(guard,summary.bw_postcondition)); - else - postconditions.push_back(implies_exprt(guard,summary.bw_postcondition)); -} - -/*******************************************************************\ - -Function: summarizer_bwt::check_postcondition() - - Inputs: - - Outputs: - - Purpose: returns false if the summary needs to be recomputed - -\******************************************************************/ - -bool summarizer_bwt::check_postcondition( - const function_namet &function_name, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool context_sensitive) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Checking precondition of " << fname << eom; - - bool precondition_holds = false; - exprt assertion; - - if(!summary_db.exists(fname)) return true; //nothing to do - - summaryt summary = summary_db.get(fname); - - if(summary.bw_precondition.is_nil()) return false; //there is work to do - - if(!context_sensitive || - summary.fw_precondition.is_true()) //precondition trivially holds - { - status() << "Precondition trivially holds, replacing by summary." - << eom; - summaries_used++; - precondition_holds = true; - } - else - { - assertion = summary.bw_precondition; - - //getting globals at call site - local_SSAt::var_sett cs_globals_in; - SSA.get_globals(n_it->location,cs_globals_in); - - ssa_inliner.rename_to_caller(f_it,summary.params, - cs_globals_in,summary.globals_in,assertion); - - debug() << "precondition assertion: " << - from_expr(SSA.ns,"",assertion) << eom; - - precondition_holds = false; - } - - if(precondition_holds) return true; - - assert(!assertion.is_nil()); - - // postcondition check - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - solver << SSA; - - solver.new_context(); - solver << SSA.get_enabling_exprs(); - - solver << precondition; - solver << ssa_inliner.get_summaries(SSA); - - //add postcondition - solver << not_exprt(assertion); - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: { - precondition_holds = false; - - status() << "Precondition does not hold, need to recompute summary." << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { - precondition_holds = true; - - status() << "Precondition holds, replacing by summary." << eom; - summaries_used++; - - break; } - default: assert(false); break; - } - - return precondition_holds; -} - -/*******************************************************************\ - -Function: summarizer_bwt::compute_calling_context2() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bwt::compute_calling_context2( - const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Computing calling context for function " << fname << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //analyze - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - - template_generator_callingcontextt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,n_it,f_it,false); - - // collect globals at call site - std::map - cs_globals_out; - SSA.get_globals(n_it->location,cs_globals_out[f_it],false); - - exprt::operandst c; - c.push_back(old_summary.fw_precondition); - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,c); //backward summaries - old_summary.bw_postcondition = postcondition; //that's a bit awkward - collect_postconditions(function_name, SSA, old_summary, postcond,sufficient); - if(!sufficient) - { - c.push_back(conjunction(postcond)); - } - else //sufficient - { - c.push_back(not_exprt(conjunction(postcond))); - } - - analyzer(solver,SSA,conjunction(c),template_generator); - - // set preconditions - local_SSAt &fSSA = ssa_db.get(fname); - - exprt postcondition_call; - analyzer.get_result(postcondition_call, - template_generator.callingcontext_vars()); - - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_out[f_it],fSSA.globals_out, - postcondition_call); - - if(sufficient && - !postcondition_call.is_true()) //TODO: this should actually be handled by ssa_analyzer using a "guard-reachabiliity-only" analysis if template is empty - { - postcondition_call = not_exprt(postcondition_call); - } - - debug() << "Backward calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", postcondition_call) << eom; - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - - return postcondition_call; -} - - diff --git a/src/summarizer/summarizer_bw.h b/src/summarizer/summarizer_bw.h deleted file mode 100644 index 3d2dfcd81..000000000 --- a/src/summarizer/summarizer_bw.h +++ /dev/null @@ -1,81 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_H -#define CPROVER_SUMMARIZER_BW_H - -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -#include "summarizer_base.h" - -class summarizer_bwt : public summarizer_baset -{ - public: - explicit summarizer_bwt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - virtual void summarize(); - virtual void summarize(const function_namet &entry_function); - - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive); - - void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient); - - virtual void do_summary(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive); - - virtual bool check_postcondition(const function_namet &function_name, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator node, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool context_sensitive); - - virtual void collect_postconditions(const function_namet &function_name, - const local_SSAt &SSA, - const summaryt &summary, - exprt::operandst &postconditions, - bool sufficient); - - virtual exprt compute_calling_context2(const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient); - -}; - - -#endif diff --git a/src/summarizer/summarizer_bw_term.cpp b/src/summarizer/summarizer_bw_term.cpp deleted file mode 100644 index ac2878a34..000000000 --- a/src/summarizer/summarizer_bw_term.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis with Termination - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw_term.h" -#include "summarizer_fw_term.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -#define MAX_PRECONDITION_DISJUNCTS 5 -#define MAX_BOOTSTRAP_ATTEMPTS 20 - -/*******************************************************************\ - -Function: summarizer_bw_termt::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::compute_summary_rec( - const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - const summaryt &old_summary = summary_db.get(function_name); - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,old_summary, - postcondition,context_sensitive, - false); - - status() << "Analyzing function " << function_name << eom; - - bool has_loops = false; - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - if(n_it->loophead != SSA.nodes.end()) { has_loops = true; break; } - } - - debug() << "function " << - (has_loops ? "has loops" : "does not have loops") << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.bw_postcondition = postcondition; - - do_nontermination(function_name,SSA,old_summary,summary); - if(!options.get_bool_option("havoc") && - summary.terminates!=NO) - { - if(!has_loops) - { - do_summary(function_name,SSA,old_summary,summary,context_sensitive); - } - else - { - do_summary_term(function_name,SSA,old_summary,summary,context_sensitive); - } - } - - // store summary in db - summary_db.put(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_bw_termt::do_nontermination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::do_nontermination( - const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - cond.push_back(old_summary.fw_invariant); - cond.push_back(old_summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - ssa_inliner.get_summaries(SSA,false,cond,cond); //backward summaries - - if(!check_end_reachable(function_name,SSA,conjunction(cond))) - { - status() << "Function never terminates" << eom; - summary.bw_transformer = false_exprt(); - summary.bw_precondition = false_exprt(); - summary.terminates = NO; - } -} - -/*******************************************************************\ - -Function: summarizer_bw_termt::do_summary_term() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::do_summary_term(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive) -{ - status() << "Computing preconditions for termination" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //templates for ranking functions - template_generator_rankingt template_generator1( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator1.set_message_handler(get_message_handler()); - template_generator1(solver.next_domain_number(),SSA,true); - - //templates for backward summary - template_generator_summaryt template_generator2( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator2.set_message_handler(get_message_handler()); - template_generator2(solver.next_domain_number(),SSA,false); - - exprt::operandst bindings; - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,bindings); //backward summaries - collect_postconditions(function_name, SSA, summary, postcond,true); - - //prepare solver - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << old_summary.fw_precondition; - solver << old_summary.fw_invariant; - solver << ssa_inliner.get_summaries(SSA); //forward summaries - solver << conjunction(bindings); //bindings for backward summaries - -#if 0 - for(unsigned i=0; iget(*it))); - } - precondition = conjunction(c); - debug() << "bootstrap model for precondition: " - << from_expr(SSA.ns,"",precondition) << eom; - solver.pop_context(); - } - else // whole precondition space covered - { - solver.pop_context(); - break; - } - - termination_argument = - compute_termination_argument(SSA,precondition, - solver,template_generator1); - - if(summarizer_fw_termt::check_termination_argument( - termination_argument)==YES) - { - return true; - } - - solver.new_context(); - checked_candidates.push_back(precondition); - solver << not_exprt(disjunction(checked_candidates)); //next one, please! - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_bw_termt:::compute_termination_argument() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_termt::compute_termination_argument( - local_SSAt &SSA, - const exprt &precondition, - incremental_solvert &solver, - template_generator_rankingt &template_generator) -{ - // compute ranking functions - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver, SSA, precondition, template_generator); - exprt termination_argument; - analyzer.get_result(termination_argument, - template_generator.all_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - termargs_computed++; - - return termination_argument; -} - -/*******************************************************************\ - -Function: summarizer_bw_termt:::compute_precondition() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_termt::compute_precondition(local_SSAt &SSA, - summaryt &summary, - const exprt::operandst &postconditions, - incremental_solvert &solver, - template_generator_summaryt &template_generator, - bool context_sensitive) -{ - exprt postcond = not_exprt(conjunction(postconditions)); - - //compute backward summary - exprt bw_transformer, bw_invariant, bw_precondition; - if(!template_generator.out_vars().empty()) - { - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver,SSA,postcond,template_generator); - analyzer.get_result(bw_transformer,template_generator.inout_vars()); - analyzer.get_result(bw_invariant,template_generator.loop_vars()); - analyzer.get_result(bw_precondition,template_generator.out_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - } - else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - { - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << postcond; - exprt result = true_exprt(); - if(solver()==decision_proceduret::D_UNSATISFIABLE) result = false_exprt(); - solver.pop_context(); - bw_transformer = result; - bw_invariant = result; - bw_precondition = result; - } - - bw_transformer = not_exprt(bw_transformer); - bw_invariant = not_exprt(bw_invariant); - bw_precondition = not_exprt(bw_precondition); - - if(context_sensitive && !summary.bw_postcondition.is_true()) - { - bw_transformer = implies_exprt(summary.bw_postcondition,bw_transformer); - bw_invariant = implies_exprt(summary.bw_postcondition,bw_invariant); - bw_precondition = implies_exprt(summary.bw_postcondition,bw_precondition); - } - - //join // TODO: should go into summaryt - if(summary.bw_transformer.is_nil()) - { - summary.bw_transformer = bw_transformer; - summary.bw_invariant = bw_invariant; - summary.bw_precondition = bw_precondition; - } - else - { - summary.bw_transformer = or_exprt(summary.bw_transformer,bw_transformer); - summary.bw_invariant = or_exprt(summary.bw_invariant,bw_invariant); - summary.bw_precondition = or_exprt(summary.bw_precondition,bw_precondition); - } - - return bw_precondition; -} - - diff --git a/src/summarizer/summarizer_bw_term.h b/src/summarizer/summarizer_bw_term.h deleted file mode 100644 index d8097338d..000000000 --- a/src/summarizer/summarizer_bw_term.h +++ /dev/null @@ -1,77 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis with Termination - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_TERM_H -#define CPROVER_SUMMARIZER_BW_TERM_H - -#include -#include -#include - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_ranking.h" - -#include "ssa_db.h" -#include "summarizer_bw.h" - -class summarizer_bw_termt : public summarizer_bwt -{ - public: - explicit summarizer_bw_termt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_bwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive); - - void do_summary_term(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive); - - void do_nontermination(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary); - - bool bootstrap_preconditions( - local_SSAt &SSA, - summaryt &summary, - incremental_solvert &solver, - template_generator_rankingt &template_generator1, - template_generator_summaryt &template_generator2, - exprt &termination_argument); - - exprt compute_termination_argument( - local_SSAt &SSA, - const exprt &precondition, - incremental_solvert &solver, - template_generator_rankingt &template_generator); - - exprt compute_precondition(local_SSAt &SSA, - summaryt &summary, - const exprt::operandst &postconditions, - incremental_solvert &solver, - template_generator_summaryt &template_generator, - bool context_sensitive); - -}; - - -#endif diff --git a/src/summarizer/summarizer_fw.cpp b/src/summarizer/summarizer_fw.cpp deleted file mode 100644 index 70ebf3bfe..000000000 --- a/src/summarizer/summarizer_fw.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_fw.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -//#define SHOW_WHOLE_RESULT - -/*******************************************************************\ - -Function: summarizert::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); //TODO: make const - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,precondition,context_sensitive); - - status() << "Analyzing function " << function_name << eom; - -#if 0 - { - std::ostringstream out; - out << "Function body for " << function_name << - " to be analyzed: " << std::endl; - for(local_SSAt::nodest::iterator n = SSA.nodes.begin(); - n!=SSA.nodes.end(); n++) - { - if(!n->empty()) n->output(out,SSA.ns); - } - out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) - << "\n"; - debug() << out.str() << eom; - } -#endif - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.fw_precondition = precondition; - - if(!options.get_bool_option("havoc")) - { - do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); - } - - -#if 0 - if(!options.get_bool_option("competition-mode")) - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary.output(out,SSA.ns); - status() << out.str() << eom; - } -#endif - - // store summary in db - summary_db.put(function_name,summary); - - if(!options.get_bool_option("competition-mode")) - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_fwt::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::do_summary(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary, - exprt cond, - bool context_sensitive) -{ - status() << "Computing summary" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //analyze - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - - template_generator_summaryt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,true); - - exprt::operandst conds; - conds.reserve(5); - conds.push_back(cond); - conds.push_back(summary.fw_precondition); - conds.push_back(ssa_inliner.get_summaries(SSA)); - -#ifdef REUSE_INVARIANTS - if(summary_db.exists(function_name)) //reuse existing invariants - { - const exprt &old_inv = summary_db.get(function_name).fw_invariant; - exprt inv = ssa_unwinder.get(function_name).rename_invariant(old_inv); - conds.push_back(inv); - -#if 0 - std::ostringstream out; - out << "(original inv)" << from_expr(SSA.ns,"",old_inv) << "\n"; - debug() << out.str() << eom; - out << "(renamed inv)" << from_expr(SSA.ns,"",inv)<<"\n"; - debug() << out.str() << eom; -#endif - } -#endif - - cond = conjunction(conds); - - analyzer(solver,SSA,cond,template_generator); - analyzer.get_result(summary.fw_transformer,template_generator.inout_vars()); - analyzer.get_result(summary.fw_invariant,template_generator.loop_vars()); - -#ifdef SHOW_WHOLE_RESULT - // to see all the custom template values - exprt whole_result; - analyzer.get_result(whole_result,template_generator.all_vars()); - debug() << "whole result: " << from_expr(SSA.ns,"",whole_result) << eom; -#endif - - if(context_sensitive && !summary.fw_precondition.is_true()) - { - summary.fw_transformer = - implies_exprt(summary.fw_precondition,summary.fw_transformer); - summary.fw_invariant = - implies_exprt(summary.fw_precondition,summary.fw_invariant); - } - - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); -} - -/*******************************************************************\ - -Function: summarizer_fwt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, const exprt &precondition, - bool context_sensitive) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - { - continue; - } - - if(!check_precondition(function_name,SSA,n_it,f_it, - precondition,context_sensitive)) - { - exprt precondition_call = true_exprt(); - if(context_sensitive) - precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,precondition_call,context_sensitive); - summaries_used++; - } - } - } -} - - diff --git a/src/summarizer/summarizer_fw.h b/src/summarizer/summarizer_fw.h deleted file mode 100644 index 221ab3023..000000000 --- a/src/summarizer/summarizer_fw.h +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FW_H -#define CPROVER_SUMMARIZER_FW_H - -#include -#include - -#include "summarizer_base.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fwt : public summarizer_baset -{ - public: - explicit summarizer_fwt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive); - - void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const exprt &precondition, - bool context_sensitive); - - void do_summary(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary, - exprt cond, //additional constraints - bool forward); -}; - - -#endif diff --git a/src/summarizer/summarizer_fw_contexts.cpp b/src/summarizer/summarizer_fw_contexts.cpp deleted file mode 100644 index 0a78d403e..000000000 --- a/src/summarizer/summarizer_fw_contexts.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis with Calling Context output - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "summarizer_fw_contexts.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -/*******************************************************************\ - -Function: summarizer_fw_contextst::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_contextst::summarize() -{ - exprt precondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); - it!=ssa_db.functions().end(); it++) - { - if(excluded_functions.find(it->first)!=excluded_functions.end()) - continue; - status() << "\nSummarizing function " << it->first << eom; - if(!summary_db.exists(it->first) || - summary_db.get(it->first).mark_recompute) - compute_summary_rec(it->first,precondition,false); - else status() << "Summary for function " << it->first << - " exists already" << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_fw_contextst::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_contextst::inline_summaries( - const function_namet &function_name, - local_SSAt &SSA, const exprt &precondition, - bool context_sensitive) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - { - continue; - } - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(excluded_functions.find(fname)!=excluded_functions.end()) - { - exprt precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - // output calling context - switch(ui) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml_cc("calling-context"); - xml_cc.set_attribute("function",id2string(fname)); - xml_cc.set_attribute("goto_location", - i2string(n_it->location->location_number)); - - //location - const source_locationt &source_location = - n_it->location->source_location; - xmlt xml_location; - if(source_location.is_not_nil() && source_location.get_file()!="") - xml_location=xml(source_location); - if(xml_location.name!="") - xml_cc.new_element().swap(xml_location); - - //argument ranges - xmlt xml_args("argument-ranges"); - assert(precondition_call.operands().size()%2==0); - for(unsigned i=0;i -#include -#include - -#include "summarizer_fw.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fw_contextst : public summarizer_fwt -{ - public: - explicit summarizer_fw_contextst(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_fwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner), - ui(ui_message_handlert::PLAIN) - { - if(_options.get_bool_option("xml-ui")) - ui = ui_message_handlert::XML_UI; - - optionst::value_listt _excluded_functions = - _options.get_list_option("do-not-analyze-functions"); - excluded_functions.insert(_excluded_functions.begin(), - _excluded_functions.end()); - - } - - virtual void summarize(); - - protected: - language_uit::uit ui; // use gui format - std::set excluded_functions; - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const exprt &precondition, - bool context_sensitive); -}; - - -#endif diff --git a/src/summarizer/summarizer_fw_term.cpp b/src/summarizer/summarizer_fw_term.cpp deleted file mode 100644 index cb9481308..000000000 --- a/src/summarizer/summarizer_fw_term.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_fw_term.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizert::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::compute_summary_rec( - const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) -{ - if(options.get_bool_option("competition-mode") && - summary_db.exists(ID__start) && - summary_db.get(ID__start).terminates==NO) - { - return; - } - - local_SSAt &SSA = ssa_db.get(function_name); - - // recursively compute summaries for function calls - threevalt calls_terminate = YES; - bool has_function_calls = false; - inline_summaries(function_name,SSA,precondition,context_sensitive, - calls_terminate,has_function_calls); - - status() << "Analyzing function " << function_name << eom; - - { - std::ostringstream out; - out << "Function body for " << function_name << - " to be analyzed: " << std::endl; - for(local_SSAt::nodest::iterator n = SSA.nodes.begin(); - n!=SSA.nodes.end(); n++) - { - if(!n->empty()) n->output(out,SSA.ns); - } - out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) - << "\n"; - debug() << out.str() << eom; - } - - bool has_loops = false; - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - if(n_it->loophead != SSA.nodes.end()) { has_loops = true; break; } - } - - debug() << "function " << - (has_function_calls ? "has function calls" : - "does not have function calls") << eom; - debug() << "function calls terminate: " << - threeval2string(calls_terminate) << eom; - debug() << "function " << - (has_loops ? "has loops" : "does not have loops") << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.fw_precondition = precondition; - summary.terminates = UNKNOWN; - - //compute summary - if(!options.get_bool_option("havoc")) - { - //We are not allowed to assume the assertions here, - // otherwise we might cut off all terminating executions - // and classify the program as non-terminating. - do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); - } - - //check termination - status() << "Computing termination argument for " << function_name << eom; - // check non-termination if we haven't analyzed this function yet, - // otherwise the termination status is UNKNOWN anyways - if(!summary_db.exists(function_name)) - { - do_nontermination(function_name,SSA,summary); - } - if(summary.terminates==UNKNOWN) - { - if(!has_loops && !has_function_calls) - { - status() << "Function trivially terminates" << eom; - summary.terminates = YES; - } - else if(!has_loops && has_function_calls && calls_terminate==YES) - { - status() << "Function terminates" << eom; - summary.terminates = YES; - } - else if(has_function_calls && calls_terminate!=YES) - { - summary.terminates = calls_terminate; - } - else if(has_loops && - (!has_function_calls || - (has_function_calls && calls_terminate==YES))) - { - do_termination(function_name,SSA,summary); - } - } - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary.output(out,SSA.ns); - status() << out.str() << eom; - } - - // store summary in db - summary_db.put(function_name,summary); -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, exprt precondition, - bool context_sensitive, - threevalt &calls_terminate, - bool &has_function_calls) -{ - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - - exprt::operandst c; - c.push_back(precondition); - get_assertions(SSA,c); //assertions as assumptions - precondition = conjunction(c); - - if(!options.get_bool_option("competition-mode") && - !check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - continue; - - has_function_calls = true; - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(!check_precondition(function_name,SSA,n_it,f_it, - precondition,context_sensitive)) - { - exprt precondition_call = true_exprt(); - if(context_sensitive) - precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,precondition_call,context_sensitive); - summaries_used++; - } - - //get information about callee termination - if(summary_db.exists(fname) && summary_db.get(fname).terminates!=YES) - { - calls_terminate = UNKNOWN; //cannot propagate NO because call reachability might be over-approximating - break; - } - } - } -} - -/*******************************************************************\ - -Function: summarizer_baset::do_nontermination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::do_nontermination( - const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - if(!summary.fw_invariant.is_nil()) cond.push_back(summary.fw_invariant); - if(!summary.fw_precondition.is_nil()) cond.push_back(summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - - if(!check_end_reachable(function_name,SSA,conjunction(cond))) - { - status() << "Function never terminates normally" << eom; - if(summary.fw_precondition.is_true()) summary.fw_transformer = false_exprt(); - else summary.fw_transformer = implies_exprt(summary.fw_precondition,false_exprt()); - summary.terminates = NO; - } -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::do_termination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::do_termination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - if(!summary.fw_invariant.is_nil()) cond.push_back(summary.fw_invariant); - if(!summary.fw_precondition.is_nil()) cond.push_back(summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - - status() << "Synthesizing ranking function to prove termination" << eom; - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - template_generator_rankingt template_generator1( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator1.set_message_handler(get_message_handler()); - template_generator1(solver.next_domain_number(),SSA,true); - - if(template_generator1.all_vars().empty()) return; //nothing to do - - get_assertions(SSA,cond); //add assertions as assumptions - - // compute ranking functions - ssa_analyzert analyzer1; - analyzer1.set_message_handler(get_message_handler()); - analyzer1(solver,SSA,conjunction(cond),template_generator1); - analyzer1.get_result(summary.termination_argument, - template_generator1.all_vars()); - - //extract information whether a ranking function was found for all loops - summary.terminates = check_termination_argument(summary.termination_argument); - if(!summary.fw_precondition.is_true()) - summary.termination_argument = implies_exprt(summary.fw_precondition, - summary.termination_argument); - - //statistics - solver_instances += analyzer1.get_number_of_solver_instances(); - solver_calls += analyzer1.get_number_of_solver_calls(); - termargs_computed++; -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::check_termination_argument() - - Inputs: - - Outputs: - - Purpose: checks whether a termination argument implies termination - -\******************************************************************/ - -threevalt summarizer_fw_termt::check_termination_argument(exprt expr) -{ - if(expr.is_false()) return YES; - // should be of the form /\_i g_i => R_i - if(expr.id()==ID_and) - { - threevalt result = YES; - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) - { - if(it->is_true()) result = UNKNOWN; - if(it->id()==ID_implies) - { - if(it->op1().is_true()) result = UNKNOWN; - } - } - return result; - } - else - { - if(expr.id()==ID_implies) - { - if(expr.op1().is_true()) return UNKNOWN; - } - else return !expr.is_true() ? YES : UNKNOWN; - } - return YES; -} - diff --git a/src/summarizer/summarizer_fw_term.h b/src/summarizer/summarizer_fw_term.h deleted file mode 100644 index c5dc7f611..000000000 --- a/src/summarizer/summarizer_fw_term.h +++ /dev/null @@ -1,61 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FW_TERM_H -#define CPROVER_SUMMARIZER_FW_TERM_H - -#include -#include - -#include "summarizer_fw.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fw_termt : public summarizer_fwt -{ - public: - explicit summarizer_fw_termt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_fwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - static threevalt check_termination_argument(exprt expr); - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive); - - void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - exprt precondition, - bool context_sensitive, - threevalt &calls_terminate, - bool &has_function_calls); - - void do_termination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary); - - void do_nontermination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary); - - }; - - -#endif diff --git a/src/summarizer/summarizer_main.cpp b/src/summarizer/summarizer_main.cpp deleted file mode 100644 index 6bafefc6a..000000000 --- a/src/summarizer/summarizer_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "summarizer_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - summarizer_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - summarizer_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/summarizer/summarizer_parseoptions.cpp b/src/summarizer/summarizer_parseoptions.cpp deleted file mode 100644 index 62d3050d3..000000000 --- a/src/summarizer/summarizer_parseoptions.cpp +++ /dev/null @@ -1,1372 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Command Line Options Processing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array_abstraction.h" - -#include - -#include - -#include -#include "version.h" - -#include "summarizer_parseoptions.h" -#include "summary_db.h" -#include "summary_checker.h" -#include "../ssa/split_loopheads.h" -#include "show.h" - -//#define ANONYMOUS - -/*******************************************************************\ - -Function: summarizer_parseoptionst::summarizer_parseoptionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -summarizer_parseoptionst::summarizer_parseoptionst(int argc, const char **argv): - parseoptions_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit("2LS " SUMMARIZER_VERSION, cmdline) -{ -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::eval_verbosity - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::eval_verbosity() -{ - // this is our default verbosity - int v=messaget::M_STATISTICS; - - if(cmdline.isset("verbosity")) - { - v=unsafe_string2int(cmdline.get_value("verbosity")); - if(v<0) - v=0; - else if(v>10) - v=10; - } - - ui_message_handler.set_verbosity(v); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::get_command_line_options - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::get_command_line_options(optionst &options) -{ - if(config.set(cmdline)) - { - usage_error(); - exit(1); - } - - if(cmdline.isset("debug-level")) - options.set_option("debug-level", cmdline.get_value("debug-level")); - - if(cmdline.isset("unwindset")) - options.set_option("unwindset", cmdline.get_value("unwindset")); - - if(cmdline.isset("unwind")) - options.set_option("unwind", cmdline.get_value("unwind")); - - if(cmdline.isset("inline-partial")) - options.set_option("inline-partial", cmdline.get_value("inline-partial")); - - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check overflow on floats - if(cmdline.isset("float-overflow-check")) - options.set_option("float-overflow-check", true); - else - options.set_option("float-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // check for memory leaks - if(cmdline.isset("memory-leak-check")) - options.set_option("memory-leak-check", true); - else - options.set_option("memory-leak-check", false); - - // check assertions - if(cmdline.isset("no-assertions")) - options.set_option("assertions", false); - else - options.set_option("assertions", true); - - // use assumptions - if(cmdline.isset("no-assumptions")) - options.set_option("assumptions", false); - else - options.set_option("assumptions", true); - - // magic error label - if(cmdline.isset("error-label")) - options.set_option("error-label", cmdline.get_value("error-label")); - - // use incremental assertion checks - if(cmdline.isset("non-incremental")) - options.set_option("incremental", false); - else - options.set_option("incremental", true); - - // compute preconditions - if(cmdline.isset("preconditions")) - options.set_option("preconditions", true); - - // compute necessary/sufficient preconditions - if(cmdline.isset("sufficient")) - options.set_option("sufficient", true); - - // compute ranking functions - if(cmdline.isset("termination")) - { - options.set_option("termination", true); - options.set_option("sufficient", true); - } - - if(cmdline.isset("monolithic-ranking-function")) - { - options.set_option("monolithic-ranking-function", true); - } - else options.set_option("monolithic-ranking-function", false); - - if(cmdline.isset("lexicographic-ranking-function")) - { - options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); - } - else options.set_option("lexicographic-ranking-function",3); - - if(cmdline.isset("max-inner-ranking-iterations")) - { - options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); - } - else options.set_option("max-inner-ranking-iterations",20); - - // do k-induction refinement - if(cmdline.isset("k-induction")) - options.set_option("k-induction", true); - - // do incremental bmc - if(cmdline.isset("incremental-bmc")) - { - options.set_option("incremental-bmc", true); - options.set_option("inline", true); - options.set_option("havoc", true); - if(options.get_unsigned_int_option("unwind")==0) - options.set_option("unwind",UINT_MAX); - } - - // check for spuriousness of assertion failures - if(cmdline.isset("no-spurious-check")) - options.set_option("spurious-check", false); - else - options.set_option("spurious-check", true); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int summarizer_parseoptionst::doit() -{ - if(cmdline.isset("version")) - { -#ifndef ANONYMOUS - std::cout << SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << std::endl; -#else - std::cout << SUMMARIZER_VERSION << std::endl; -#endif - return 0; - } - - // - // command line options - // - - optionst options; - get_command_line_options(options); - - eval_verbosity(); - - // - // Print a banner - // -#ifndef ANONYMOUS - status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; -#else - status() << "2LS version " SUMMARIZER_VERSION << eom; -#endif - - register_language(new_ansi_c_language); - register_language(new_cpp_language); - - goto_modelt goto_model; - - if(get_goto_program(options, goto_model)) - return 6; - - // options for various debug outputs - - if(cmdline.isset("show-ssa")) - { - bool simplify=!cmdline.isset("no-simplify"); - show_ssa(goto_model, simplify, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-fixed-points")) - { - bool simplify=!cmdline.isset("no-simplify"); - show_fixed_points(goto_model, simplify, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-stats")) - { - show_stats(goto_model, std::cout); - return 7; - } - - if(cmdline.isset("show-defs")) - { - show_defs(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-assignments")) - { - show_assignments(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-guards")) - { - show_guards(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-invariants")) - { - options.set_option("show-invariants", true); - } - - if(cmdline.isset("context-sensitive")) - { - options.set_option("context-sensitive", true); - status() << "Context-sensitive analysis from " << - goto_model.goto_functions.entry_point() << eom; - } - - if(cmdline.isset("arrays")) - { - options.set_option("arrays", true); - status() << "Do not ignore array contents" << eom; - } - - if(cmdline.isset("havoc")) - { - options.set_option("havoc", true); - status() << "Havocking loops and function calls"; - } - else if(cmdline.isset("equalities")) - { - options.set_option("equalities", true); - status() << "Using equalities domain "; - } - else - { - if(cmdline.isset("zones")) - { - options.set_option("zones", true); - status() << "Using zones domain "; - } - else if(cmdline.isset("octagons")) - { - options.set_option("octagons", true); - status() << "Using octagons domain "; - } - else //if(cmdline.isset("intervals")) //default - { - options.set_option("intervals", true); - status() << "Using intervals domain "; - } - - if(cmdline.isset("enum-solver")) - { - options.set_option("enum-solver", true); - status() << "with enumeration solver"; - } - else //if(cmdline.isset("binsearch-solver")) //default - { - options.set_option("binsearch-solver", true); - status() << "with binary search solver"; - } - } - status() << eom; - - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("enum-solver")) - { - warning() << "Option --enum-solver ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("binsearch-solver")) - { - warning() << "Option --binsearch-solver ignored" << eom; - } - if(cmdline.isset("havoc") && - cmdline.isset("equalities")) - { - warning() << "Option --equalities ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("intervals")) - { - warning() << "Option --intervals ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("zones")) - { - warning() << "Option --zones ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("octagons")) - { - warning() << "Option --octagons ignored" << eom; - } - - try - { - summary_checkert summary_checker(options); - - summary_checker.set_message_handler(get_message_handler()); - summary_checker.simplify=!cmdline.isset("no-simplify"); - summary_checker.fixed_point=!cmdline.isset("no-fixed-point"); - - if(cmdline.isset("show-vcc")) - { - std::cout << "VERIFICATION CONDITIONS:\n\n"; - summary_checker.show_vcc=true; - summary_checker(goto_model); - return 0; - } - - // do actual analysis - int retval; - switch(summary_checker(goto_model)) - { - case property_checkert::PASS: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - report_success(); - retval = 0; - break; - - case property_checkert::FAIL: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - report_failure(); - retval = 10; - break; - - case property_checkert::UNKNOWN: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - if(options.get_bool_option("preconditions")) return 0; - report_unknown(); - retval = 5; - break; - - default: - return 8; - } - - return retval; - } - - catch(const std::string error_msg) - { - error() << error_msg << messaget::eom; - return 8; - } - - catch(const char *error_msg) - { - error() << error_msg << messaget::eom; - return 8; - } - - #if 0 - // let's log some more statistics - debug() << "Memory consumption:" << messaget::endl; - memory_info(debug()); - debug() << eom; - #endif -} - - - -void summarizer_parseoptionst::type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns) -{ - - if(type.id()==ID_symbol) - type_stats_rec(ns.follow(type), stats, ns); - - if(type.id()==ID_pointer || type.id()==ID_array) - { - stats.has_array=true; - - const typet &subtype=ns.follow(type.subtype()); - - if(subtype.id()==ID_signedbv || - subtype.id()==ID_unsignedbv) - { - stats.has_string=(to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); - } - } - - if(type.has_subtypes()) - { - forall_subtypes(it, type) - { - type_stats_rec(*it, stats, ns); - } - } -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::expr_stats_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - - -void summarizer_parseoptionst::expr_stats_rec( - const exprt &expr, - expr_statst &stats) -{ - - if(expr.id()==ID_side_effect) - { - const side_effect_exprt &side_effect_expr=to_side_effect_expr(expr); - const irep_idt &statement=side_effect_expr.get_statement(); - - if(statement==ID_malloc) - { - stats.has_malloc=true; - } - else if(statement==ID_nondet) - { - // done in statet:instantiate_rec - } - } - - if(expr.id()==ID_symbol ) - { - - } - - if(expr.has_operands()) - { - forall_operands(it, expr) - { - expr_stats_rec(*it, stats); - } - } -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::show_stats - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) -{ - - const namespacet ns(goto_model.symbol_table); - - expr_statst stats; - - unsigned nr_instructions=0; - unsigned nr_functions=0; - unsigned nr_loops=0; - - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body_available) continue; - - ++nr_functions; - - const goto_programt &goto_program=f_it->second.body; - -#if 0 - statistics() << "function size of " << f_it->first << ": " - << goto_program.instructions.size() << eom; -#endif - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) - { - nr_instructions++; - const goto_programt::instructiont &instruction=*i_it; - - if(i_it->is_backwards_goto()) nr_loops++; - - switch(instruction.type) - { - case ASSIGN: - { - const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); - } - break; - case ASSUME: - expr_stats_rec(instruction.guard, stats); - break; - case ASSERT: - expr_stats_rec(instruction.guard, stats); - break; - case GOTO: - expr_stats_rec(instruction.guard, stats); - break; - - case DECL: - // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - - break; - - default: - // skip - break; - } // switch - } // forall instructions - } // forall functions - - out << " =============== STATS =============== " << std::endl; - out << " nr of instructions: " << nr_instructions << std::endl; - out << " nr of functions: " << nr_functions << std::endl; - out << " nr of loops: " << nr_loops << std::endl; - out << " malloc: " << (stats.has_malloc ? "YES" : "NO") << std::endl; - out << " arrays: " << (stats.has_array ? "YES" : "NO") << std::endl; - out << " strings: " << (stats.has_string ? "YES" : "NO") << std::endl; -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::set_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::set_properties(goto_modelt &goto_model) -{ - try - { - if(cmdline.isset("property")) - ::set_properties(goto_model, cmdline.get_values("property")); - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::get_goto_program - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::get_goto_program( - const optionst &options, - goto_modelt &goto_model) -{ - if(cmdline.args.size()==0) - { - error() << "Please provide a program to verify" << eom; - return true; - } - - try - { - if(cmdline.args.size()==1 && - is_goto_binary(cmdline.args[0])) - { - status() << "Reading GOTO program from file" << eom; - - if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) - return true; - - config.ansi_c.set_from_symbol_table(goto_model.symbol_table); - - if(cmdline.isset("show-symbol-table")) - { - show_symbol_table(); - return true; - } - - irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(goto_model.symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) - { - error() << "The goto binary has no entry point; please complete linking" << eom; - return true; - } - } - else if(cmdline.isset("show-parse-tree")) - { - if(cmdline.args.size()!=1) - { - error() << "Please give one source file only" << eom; - return true; - } - - std::string filename=cmdline.args[0]; - - #ifdef _MSC_VER - std::ifstream infile(widen(filename).c_str()); - #else - std::ifstream infile(filename.c_str()); - #endif - - if(!infile) - { - error() << "failed to open input file `" << filename << "'" << eom; - return true; - } - - languaget *language=get_language_from_filename(filename); - - if(language==NULL) - { - error() << "failed to figure out type of file `" << filename << "'" << eom; - return true; - } - - language->set_message_handler(get_message_handler()); - - status("Parsing", filename); - - if(language->parse(infile, filename)) - { - error() << "PARSING ERROR" << eom; - return true; - } - - language->show_parse(std::cout); - return true; - } - else - { - - if(parse()) return true; - if(typecheck()) return true; - if(final()) return true; - - // we no longer need any parse trees or language files - clear_parse(); - - if(cmdline.isset("show-symbol-table")) - { - show_symbol_table(); - return true; - } - - irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) - { - error() << "No entry point; please provide a main function" << eom; - return true; - } - - status() << "Generating GOTO Program" << eom; - - goto_convert(symbol_table, goto_model, ui_message_handler); - } - - // finally add the library - status() << "Adding CPROVER library" << eom; - link_to_library(goto_model, ui_message_handler); - - if(process_goto_program(options, goto_model)) - return true; - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::process_goto_program - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::process_goto_program( - const optionst &options, - goto_modelt &goto_model) -{ - try - { - // do partial inlining - if(cmdline.isset("inline-partial")) - { - unsigned limit = options.get_unsigned_int_option("inline-partial"); - status() << "Performing partial inlining (" << limit << ")" << eom; - goto_partial_inline(goto_model, ui_message_handler, limit/2); - //TODO: where is limit multiplied by 2??? - - //remove inlined functions - Forall_goto_functions(f_it, goto_model.goto_functions) - if(f_it->first!=ID_main && - f_it->second.body.instructions.size()<=2*(limit/2)) - { - f_it->second.body_available=false; - f_it->second.body.clear(); - } - } - - // add generic checks - status() << "Generic Property Instrumentation" << eom; - goto_check(options, goto_model); - - status() << "Function Pointer Removal" << eom; - remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); - - // remove returns (must be done after function pointer removal) - remove_returns(goto_model); - - split_loopheads(goto_model); - - // recalculate numbers, etc. - goto_model.goto_functions.update(); - - // add loop ids - goto_model.goto_functions.compute_loop_numbers(); - - // if we aim to cover, replace - // all assertions by false to prevent simplification - if(cmdline.isset("cover-assertions")) - make_assertions_false(goto_model); - - // show it? - if(cmdline.isset("show-loops")) - { - show_loop_ids(get_ui(), goto_model); - return true; - } - - // now do full inlining, if requested - if(cmdline.isset("inline")) - { - status() << "Performing full inlining" << eom; - goto_inline(goto_model, ui_message_handler); - } - - //inline __CPROVER_initialize and main - if(cmdline.isset("inline-main")) - { - inline_main(goto_model); - } - - // do array abstraction - if(cmdline.isset("array-abstraction")) - { - status() << "Performing array abstraction" << eom; - array_abstraction(goto_model.symbol_table, ui_message_handler, - goto_model.goto_functions); - } - - label_properties(goto_model); - - if(cmdline.isset("show-properties")) - { - show_properties(goto_model, get_ui()); - return true; - } - - if(set_properties(goto_model)) - return true; - - // show it? - if(cmdline.isset("show-goto-functions")) - { - const namespacet ns(goto_model.symbol_table); - goto_model.goto_functions.output(ns, std::cout); - return true; - } - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::inline_main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::inline_main(goto_modelt &goto_model) -{ - goto_programt &main = goto_model.goto_functions.function_map[ID_main].body; - goto_programt::targett target = main.instructions.begin(); - while(target!=main.instructions.end()) - { - if(target->is_function_call()) - { - const code_function_callt &code_function_call= - to_code_function_call(target->code); - irep_idt fname = code_function_call.function().get(ID_identifier); - - debug() << "Inlining " << fname << eom; - - goto_programt tmp; - tmp.copy_from(goto_model.goto_functions.function_map[fname].body); - (--tmp.instructions.end())->make_skip(); - goto_model.goto_functions.function_map.erase(fname); - - goto_programt::targett next_target(target); - target->make_skip(); - next_target++; - main.instructions.splice(next_target, tmp.instructions); - target=next_target; - } - else target++; - } - - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_properties( - const goto_modelt &goto_model, - const property_checkert::property_mapt &property_map) -{ - for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); - it!=property_map.end(); - it++) - { - /* if(it->first=="") //TODO: some properties do not show up in initialize_property_map - continue; */ - - if(get_ui()==ui_message_handlert::XML_UI) - { - xmlt xml_result("result"); - xml_result.set_attribute("property", id2string(it->first)); - xml_result.set_attribute("status", property_checkert::as_string(it->second.result)); - std::cout << xml_result << "\n"; - } - else - { - status() << "[" << it->first << "] " - << it->second.location->source_location.get_comment() - << ": " - << property_checkert::as_string(it->second.result) - << eom; - } - - if(cmdline.isset("show-trace") && - it->second.result==property_checkert::FAIL) - show_counterexample(goto_model, it->second.error_trace); - } - - if(!cmdline.isset("property")) - { - status() << eom; - - unsigned failed=0; - unsigned unknown=0; - - for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); - it!=property_map.end(); - it++) - { - if(it->second.result==property_checkert::UNKNOWN) - unknown++; - if(it->second.result==property_checkert::FAIL) - failed++; - } - - status() << "** " << unknown - << " of " << property_map.size() << " unknown" - << eom; - status() << "** " << failed - << " of " << property_map.size() << " failed" - << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_success - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_success() -{ - result() << "VERIFICATION SUCCESSFUL" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="SUCCESS"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::show_counterexample - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::show_counterexample( - const goto_modelt &goto_model, - const goto_tracet &error_trace) -{ - const namespacet ns(goto_model.symbol_table); - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - std::cout << std::endl << "Counterexample:" << std::endl; - show_goto_trace(std::cout, ns, error_trace); - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml; - convert(ns, error_trace, xml); - std::cout << xml << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_failure - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_failure() -{ - result() << "VERIFICATION FAILED" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="FAILURE"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_unknown() -{ - result() << "VERIFICATION INCONCLUSIVE" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="UNKNOWN"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void summarizer_parseoptionst::help() -{ -#ifdef ANONYMOUS - std::cout << - "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2014 "; -#else - std::cout << - "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2014 * *\n" - "* * (based on CBMC " CBMC_VERSION " "; -#endif - std::cout << "(" << (sizeof(void *)*8) << "-bit version))"; - - std::cout << " * *\n"; - - std::cout << -#ifndef ANONYMOUS - "* * Daniel Kroening * *\n" - "* * University of Oxford * *\n" - "* * kroening@kroening.com * *\n" - "\n" -#endif - "Usage: Purpose:\n" - "\n" - " summarizer [-?] [-h] [--help] show help\n" - " summarizer file.c ... source file names\n" - "\n" - "Frontend options:\n" - " -I path set include path (C/C++)\n" - " -D macro define preprocessor macro (C/C++)\n" - " --preprocess stop after preprocessing\n" - " --16, --32, --64 set width of int\n" - " --LP64, --ILP64, --LLP64,\n" - " --ILP32, --LP32 set width of int, long and pointers\n" - " --little-endian allow little-endian word-byte conversions\n" - " --big-endian allow big-endian word-byte conversions\n" - " --unsigned-char make \"char\" unsigned by default\n" - " --show-stats show statistics about program\n" - " --show-parse-tree show parse tree\n" - " --show-symbol-table show symbol table\n" - " --show-goto-functions show goto program\n" - " --arch set architecture (default: " - << configt::this_architecture() << ")\n" - " --os set operating system (default: " - << configt::this_operating_system() << ")\n" - #ifdef _WIN32 - " --gcc use GCC as preprocessor\n" - #endif - " --no-arch don't set up an architecture\n" - " --no-library disable built-in abstract C library\n" - " --round-to-nearest IEEE floating point rounding mode (default)\n" - " --round-to-plus-inf IEEE floating point rounding mode\n" - " --round-to-minus-inf IEEE floating point rounding mode\n" - " --round-to-zero IEEE floating point rounding mode\n" - "\n" - "Program instrumentation options:\n" - " --bounds-check enable array bounds checks\n" - " --pointer-check enable pointer checks\n" - " --array-abstraction use array and string abstraction for bounds checks\n" - " --div-by-zero-check enable division by zero checks\n" - " --memory-leak-check enable memory leak checks\n" - " --signed-overflow-check enable arithmetic over- and underflow checks\n" - " --unsigned-overflow-check enable arithmetic over- and underflow checks\n" - " --nan-check check floating-point for NaN\n" - " --error-label label check that label is unreachable\n" - " --show-properties show the properties\n" - " --no-assertions ignore user assertions\n" - " --no-assumptions ignore user assumptions\n" - " --inline inline all functions into main\n" - " --inline-partial nr inline functions smaller than the given nr of instructions\n" - "\n" - "Backend options:\n" - " --termination compute ranking functions to prove termination\n" - " --k-induction use k-induction\n" - " --incremental-bmc use incremental-bmc\n" - " --preconditions compute preconditions\n" - " --sufficient sufficient preconditions (default: necessary)\n" - " --context-sensitive context-sensitive analysis from entry point\n" - " --havoc havoc loops and function calls\n" - " --intervals use interval domain\n" - " --equalities use equalities and disequalities domain\n" - " --zones use zone domain\n" - " --octagons use octagon domain\n" - " --enum-solver use solver based on model enumeration\n" - " --binsearch-solver use solver based on binary search\n" - " --arrays do not ignore array contents\n" - " --lexicographic-ranking-function n (default n=3)\n" - " --monolithic-ranking-function\n" - " --max-inner-ranking-iterations n (default n=20)\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - "\n"; -} diff --git a/src/summarizer/summarizer_parseoptions.h b/src/summarizer/summarizer_parseoptions.h deleted file mode 100644 index 031717956..000000000 --- a/src/summarizer/summarizer_parseoptions.h +++ /dev/null @@ -1,128 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Parsing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_PARSEOPTIONS_H -#define CPROVER_SUMMARIZER_PARSEOPTIONS_H - -#include -#include - -#include - -class goto_modelt; -class optionst; - -#include "summary_checker.h" - -#define SUMMARIZER_OPTIONS \ - "(xml-ui)" \ - "(function):" \ - "D:I:" \ - "(depth):(context-bound):(unwind):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)(memory-leak-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)" \ - "(float-overflow-check)(nan-check)" \ - "(array-abstraction)" \ - "(non-incremental)" \ - "(no-assertions)(no-assumptions)" \ - "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ - "(little-endian)(big-endian)" \ - "(error-label):(verbosity):(no-library)" \ - "(version)" \ - "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)(gcc)" \ - "(ppc-macos)(unsigned-char)" \ - "(havoc)(intervals)(zones)(octagons)(equalities)"\ - "(enum-solver)(binsearch-solver)(arrays)"\ - "(string-abstraction)(no-arch)(arch):(floatbv)(fixedbv)" \ - "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ - "(inline)(inline-main)(inline-partial):" \ - "(context-sensitive)(termination)" \ - "(lexicographic-ranking-function):(monolithic-ranking-function)" \ - "(max-inner-ranking-iterations):" \ - "(preconditions)(sufficient)" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-fixed-points)(show-stats)" \ - "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ - "(show-invariants)" \ - "(property):(all-properties)(k-induction)(incremental-bmc)" \ - "(no-spurious-check)" \ - "(no-simplify)(no-fixed-point)" \ - "(no-unwinding-assertions)(no-propagation)" - // the last line is for CBMC-regression testing only - -class summarizer_parseoptionst: - public parseoptions_baset, - public language_uit -{ -public: - virtual int doit(); - virtual void help(); - - summarizer_parseoptionst(int argc, const char **argv); - summarizer_parseoptionst( - int argc, - const char **argv, - const std::string &extra_options); - -protected: - void get_command_line_options(optionst &options); - - bool get_goto_program( - const optionst &options, - goto_modelt &goto_model); - - bool process_goto_program( - const optionst &options, - goto_modelt &goto_model); - - bool set_properties(goto_modelt &); - - void report_success(); - void report_failure(); - void report_unknown(); - - void report_properties( - const goto_modelt &, - const summary_checkert::property_mapt &); - - void show_counterexample( - const goto_modelt &, - const class goto_tracet &); - - struct expr_statst { - bool has_malloc; - bool has_string; - bool has_array; - bool has_pointer; - - expr_statst() - : has_malloc(false) - , has_string(false) - , has_array(false) - , has_pointer(false) - {} - }; - - void type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns); - - void expr_stats_rec( - const exprt &expr, - expr_statst &stats); - - void show_stats( - const goto_modelt &, - std::ostream &); - - void eval_verbosity(); - - void inline_main(goto_modelt &goto_model); -}; - -#endif diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp deleted file mode 100644 index 4107f5014..000000000 --- a/src/summarizer/summary.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/*******************************************************************\ - -Module: Summary - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary.h" -#include "../domains/util.h" - -#include - -//#define PRETTY_PRINT - -/*******************************************************************\ - -Function: summaryt::output() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summaryt::output(std::ostream &out, const namespacet &ns) const -{ - out << "params: "; - for(summaryt::var_listt::const_iterator it = params.begin(); - it != params.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "globals_in: "; - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "globals_out: "; - for(summaryt::var_sett::const_iterator it = globals_out.begin(); - it != globals_out.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "forward precondition: " - << (fw_precondition.is_nil() ? "not computed" : - from_expr(ns,"",fw_precondition)) << std::endl; - out << "forward transformer: " - << (fw_transformer.is_nil() ? "not computed" : - from_expr(ns,"",fw_transformer)) << std::endl; - out << "forward invariant: " - << (fw_invariant.is_nil() ? "not computed" : - from_expr(ns,"",fw_invariant)) << std::endl; - out << "backward precondition: " - << (bw_precondition.is_nil() ? "not computed" : - from_expr(ns,"",bw_precondition)) << std::endl; - out << "backward postcondition: " - << (bw_postcondition.is_nil() ? "not computed" : - from_expr(ns,"",bw_postcondition)) << std::endl; - out << "backward transformer: " - << (bw_transformer.is_nil() ? "not computed" : - from_expr(ns,"",bw_transformer)) << std::endl; - out << "backward invariant: " - << (bw_invariant.is_nil() ? "not computed" : - from_expr(ns,"",bw_invariant)) << std::endl; - out << "termination argument: "; - if(termination_argument.is_nil()) out << "not computed"; - else -#if PRETTY_PRINT - pretty_print_termination_argument(out,ns,termination_argument); -#else - out << from_expr(ns,"",termination_argument) << std::endl; -#endif - out << std::endl; - out << "terminates: " << threeval2string(terminates) << std::endl; -} - -/*******************************************************************\ - -Function: summaryt::join() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summaryt::combine_and(exprt &olde, const exprt &newe) -{ - if(olde.is_nil()) - { - olde = newe; - } - else - { - if(newe.is_nil()) return; - olde = and_exprt(olde,newe); - } -} - -void summaryt::combine_or(exprt &olde, const exprt &newe) -{ - if(olde.is_nil()) - { - olde = newe; - } - else - { - if(newe.is_nil()) return; - olde = or_exprt(olde,newe); - } -} - -void summaryt::join(const summaryt &new_summary) -{ - assert(params == new_summary.params); - assert(globals_in == new_summary.globals_in); - assert(globals_out == new_summary.globals_out); - combine_or(fw_precondition,new_summary.fw_precondition); - combine_and(fw_transformer,new_summary.fw_transformer); - combine_and(fw_invariant,new_summary.fw_invariant); - combine_and(bw_precondition,new_summary.bw_precondition); - combine_or(bw_postcondition,new_summary.bw_postcondition); - combine_and(bw_transformer,new_summary.bw_transformer); - combine_and(bw_invariant,new_summary.bw_invariant); - combine_and(termination_argument,new_summary.termination_argument); - switch(new_summary.terminates) - { - case YES: - break; - case NO: terminates=NO; - break; - case UNKNOWN: - if(terminates!=NO) terminates=UNKNOWN; - break; - default: assert(false); - } -} - -/*******************************************************************\ - -Function: threeval2string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string threeval2string(threevalt v) -{ - switch(v) - { - case YES: return "yes"; - case NO: return "no"; - case UNKNOWN: return "unknown"; - } - assert(false); -} diff --git a/src/summarizer/summary_checker.cpp b/src/summarizer/summary_checker.cpp deleted file mode 100644 index c44c05f57..000000000 --- a/src/summarizer/summary_checker.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include -#include - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_build_goto_trace.h" -#include "../domains/ssa_fixed_point.h" - -#include "summary_checker.h" - -/*******************************************************************\ - -Function: summary_checkert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checkert::operator()( - const goto_modelt &goto_model) -{ - return check_properties(goto_model); -} - -/*******************************************************************\ - -Function: summary_checkert::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -summary_checkert::resultt summary_checkert::check_properties( - const goto_modelt &goto_model) -{ - // properties - initialize_property_map(goto_model.goto_functions); - - const namespacet ns(goto_model.symbol_table); - - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body.has_assertion()) continue; - check_properties(f_it, ns); - } - - for(property_mapt::const_iterator - p_it=property_map.begin(); p_it!=property_map.end(); p_it++) - if(p_it->second.result==FAIL) - return property_checkert::FAIL; - - return property_checkert::PASS; -} - -/*******************************************************************\ - -Function: summary_checkert::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::check_properties( - const goto_functionst::function_mapt::const_iterator f_it, - const namespacet &ns) -{ - status() << "Analyzing " << f_it->first << messaget::eom; - - // build SSA - local_SSAt SSA(f_it->second, ns); - - // simplify, if requested - if(simplify) - { - status() << "Simplifying" << messaget::eom; - ::simplify(SSA, ns); - } - - // fixed-point for loops - status() << "Fixed-point" << messaget::eom; - ssa_fixed_point(SSA); - - status() << "Checking properties" << messaget::eom; - - const goto_programt &goto_program=f_it->second.body; - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) - { - if(!i_it->is_assert()) - continue; - - const source_locationt &source_location=i_it->source_location; - irep_idt property_id=source_location.get_property_id(); - - if(show_vcc) - { - do_show_vcc(SSA, i_it); - continue; - } - - // solver - satcheckt satcheck; - bv_pointerst solver(SSA.ns, satcheck); - solver.unbounded_array=bv_pointerst::U_AUTO; - - satcheck.set_message_handler(get_message_handler()); - solver.set_message_handler(get_message_handler()); - - // give SSA to solver - solver << SSA; - - // give negation of property to solver - exprt negated_property=not_exprt(SSA.assertion(i_it)); - - solver << negated_property; - - property_statust &property_status=property_map[property_id]; - - // solve - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - property_status.result=FAIL; - build_goto_trace(SSA, solver, property_status.error_trace); - break; - - case decision_proceduret::D_UNSATISFIABLE: - property_status.result=PASS; - break; - - case decision_proceduret::D_ERROR: - default: - property_status.result=ERROR; - throw "error from decision procedure"; - } - } -} - -/*******************************************************************\ - -Function: summary_checkert::report_statistics() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::report_statistics() -{ -} - -/*******************************************************************\ - -Function: summary_checkert::do_show_vcc - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::do_show_vcc( - const local_SSAt &SSA, - const goto_programt::const_targett i_it) -{ - std::cout << i_it->source_location << "\n"; - std::cout << i_it->source_location.get_comment() << "\n"; - - std::list ssa_constraints; - ssa_constraints << SSA; - - unsigned i=1; - for(std::list::const_iterator c_it=ssa_constraints.begin(); - c_it!=ssa_constraints.end(); - c_it++, i++) - std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; - - std::cout << "|--------------------------\n"; - - exprt property=SSA.assertion(i_it); - - std::cout << "{1} " << from_expr(SSA.ns, "", property) << "\n"; - - std::cout << "\n"; -} - diff --git a/src/summarizer/summary_checker.h b/src/summarizer/summary_checker.h deleted file mode 100644 index 4c8d32da7..000000000 --- a/src/summarizer/summary_checker.h +++ /dev/null @@ -1,51 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARY_CHECKER_H -#define CPROVER_SUMMARY_CHECKER_H - -#include - -#include - -#include "../ssa/local_ssa.h" - -class summary_checkert:public property_checkert -{ -public: - inline summary_checkert(): - show_vcc(false), - simplify(false), - fixed_point(false) - { - } - - bool show_vcc, simplify, fixed_point; - irep_idt function_to_check; - - virtual resultt operator()(const goto_modelt &); - - // statistics - absolute_timet start_time; - time_periodt sat_time; - -protected: - void report_statistics(); - - void do_show_vcc( - const local_SSAt &, - const goto_programt::const_targett); - - resultt check_properties(const goto_modelt &); - - void check_properties( - const goto_functionst::function_mapt::const_iterator f_it, - const namespacet &); -}; - -#endif diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp deleted file mode 100644 index bfce6c21d..000000000 --- a/src/summarizer/summary_checker_ai.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker for AI - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary_checker_ai.h" - -/*******************************************************************\ - -Function: summary_checker_ait::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_ait::operator()( - const goto_modelt &goto_model) -{ - const namespacet ns(goto_model.symbol_table); - - SSA_functions(goto_model,ns); - - ssa_unwinder.init(false,false); - - unsigned unwind = options.get_unsigned_int_option("unwind"); - if(unwind>0) - { - status() << "Unwinding" << messaget::eom; - - ssa_unwinder.init_localunwinders(); - - ssa_unwinder.unwind_all(unwind); - } - - // properties - initialize_property_map(goto_model.goto_functions); - - bool preconditions = options.get_bool_option("preconditions"); - bool termination = options.get_bool_option("termination"); - bool trivial_domain = options.get_bool_option("havoc"); - if(!trivial_domain || termination) - { - //forward analysis - summarize(goto_model,true,termination); - } - if(!trivial_domain && preconditions) - { - //backward analysis - summarize(goto_model,false,termination); - } - - if(preconditions) - { - report_statistics(); - report_preconditions(); - return property_checkert::UNKNOWN; - } - - if(termination) - { - report_statistics(); - return report_termination(); - } - -#ifdef SHOW_CALLINGCONTEXTS - if(options.get_bool_option("show-calling-contexts")) - return property_checkert::UNKNOWN; -#endif - - property_checkert::resultt result = check_properties(); - report_statistics(); - return result; -} - - -/*******************************************************************\ - -Function: summary_checker_ait::report_preconditions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_ait::report_preconditions() -{ - result() << eom; - result() << "** " << (options.get_bool_option("sufficient") ? - "Sufficient" : "Necessary") - << " preconditions: " << eom; - ssa_dbt::functionst &functions = ssa_db.functions(); - for(ssa_dbt::functionst::iterator it = functions.begin(); - it != functions.end(); it++) - { - exprt precondition; - bool computed = summary_db.exists(it->first); - if(computed) precondition = summary_db.get(it->first).bw_precondition; - if(precondition.is_nil()) computed = false; - result() << eom << "[" << it->first << "]: " - << (!computed ? "not computed" : - from_expr(it->second->ns, "", precondition)) << eom; - } -} - -/*******************************************************************\ - -Function: summary_checker_ait::report_termination - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_ait::report_termination() -{ - result() << eom; - result() << "** Termination: " << eom; - bool not_computed = true; - bool all_terminate = true; - bool one_nonterminate = false; - ssa_dbt::functionst &functions = ssa_db.functions(); - for(ssa_dbt::functionst::iterator it = functions.begin(); - it != functions.end(); it++) - { - threevalt terminates = YES; - bool computed = summary_db.exists(it->first); - if(computed) - { - terminates = summary_db.get(it->first).terminates; - not_computed = false; - } - all_terminate = all_terminate && (terminates==YES); - one_nonterminate = one_nonterminate || (terminates==NO); - result() << "[" << it->first << "]: " - << (!computed ? "not computed" : threeval2string(terminates)) << eom; - } - if(not_computed) return property_checkert::UNKNOWN; - if(all_terminate) return property_checkert::PASS; - if(one_nonterminate) return property_checkert::FAIL; - return property_checkert::UNKNOWN; -} diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp deleted file mode 100644 index fe8684af1..000000000 --- a/src/summarizer/summary_checker_base.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Checker Base - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_build_goto_trace.h" -#include "../domains/ssa_analyzer.h" -#include "../ssa/ssa_unwinder.h" -#include - -#include "show.h" -#include "instrument_goto.h" - -#include "summary_checker_base.h" - -#include "summarizer_fw.h" -#include "summarizer_fw_term.h" -#include "summarizer_bw.h" -#include "summarizer_bw_term.h" - -#ifdef SHOW_CALLING_CONTEXTS -#include "summarizer_fw_contexts.h" -#endif - -/*******************************************************************\ - -Function: summary_checker_baset::SSA_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::SSA_functions(const goto_modelt &goto_model, const namespacet &ns) -{ - // compute SSA for all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - status() << "Computing SSA of " << f_it->first << messaget::eom; - - ssa_db.create(f_it->first, f_it->second, ns); - local_SSAt &SSA = ssa_db.get(f_it->first); - - // simplify, if requested - if(simplify) - { - status() << "Simplifying" << messaget::eom; - ::simplify(SSA, ns); - } - - SSA.output(debug()); debug() << eom; - } - - // properties - initialize_property_map(goto_model.goto_functions); -} - -/*******************************************************************\ - -Function: summary_checker_baset::summarize - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::summarize(const goto_modelt &goto_model, - bool forward, - bool termination) -{ - summarizer_baset *summarizer = NULL; - -#ifdef SHOW_CALLING_CONTEXTS - if(options.get_bool_option("show-calling-contexts")) - summarizer = new summarizer_fw_contextst( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - else -#endif - { - if(forward && !termination) - summarizer = new summarizer_fwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(forward && termination) - summarizer = new summarizer_fw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(!forward && !termination) - summarizer = new summarizer_bwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(!forward && termination) - summarizer = new summarizer_bw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - } - assert(summarizer != NULL); - - summarizer->set_message_handler(get_message_handler()); - - if(!options.get_bool_option("context-sensitive") && - options.get_bool_option("all-functions")) - summarizer->summarize(); - else - summarizer->summarize(goto_model.goto_functions.entry_point()); - - //statistics - solver_instances += summarizer->get_number_of_solver_instances(); - solver_calls += summarizer->get_number_of_solver_calls(); - summaries_used += summarizer->get_number_of_summaries_used(); - termargs_computed += summarizer->get_number_of_termargs_computed(); - - delete summarizer; -} -/*******************************************************************\ - -Function: summary_checker_baset::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -summary_checker_baset::resultt summary_checker_baset::check_properties() -{ - // analyze all the functions - for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) - { - status() << "Checking properties of " << f_it->first << messaget::eom; - -#if 0 - //for debugging - show_ssa_symbols(*f_it->second,std::cerr); -#endif - - check_properties(f_it); - - if(options.get_bool_option("show-invariants")) - { - if(!summary_db.exists(f_it->first)) continue; - show_invariants(*(f_it->second),summary_db.get(f_it->first),result()); - result() << eom; - } - } - - summary_checker_baset::resultt result = property_checkert::PASS; - for(property_mapt::const_iterator - p_it=property_map.begin(); p_it!=property_map.end(); p_it++) - { - if(p_it->second.result==FAIL) - return property_checkert::FAIL; - if(p_it->second.result==UNKNOWN) - result = property_checkert::UNKNOWN; - } - - return result; -} - -/*******************************************************************\ - -Function: summary_checker_baset::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::check_properties( - const ssa_dbt::functionst::const_iterator f_it) -{ - unwindable_local_SSAt &SSA = *f_it->second; - - bool all_properties = options.get_bool_option("all-properties"); - - SSA.output_verbose(debug()); debug() << eom; - - // incremental version - - // solver - incremental_solvert &solver = ssa_db.get_solver(f_it->first); - solver.set_message_handler(get_message_handler()); - - // give SSA to solver - solver << SSA; - SSA.mark_nodes(); - - solver.new_context(); - - exprt enabling_expr = SSA.get_enabling_exprs(); - solver << enabling_expr; - - // invariant, calling contexts - if(summary_db.exists(f_it->first)) - { - solver << summary_db.get(f_it->first).fw_invariant; - solver << summary_db.get(f_it->first).fw_precondition; - } - - //callee summaries - solver << ssa_inliner.get_summaries(SSA); - - //freeze loop head selects - exprt::operandst loophead_selects = - get_loophead_selects(f_it->first,SSA,*solver.solver); - //check whether loops have been fully unwound - exprt::operandst loop_continues = - get_loop_continues(f_it->first,SSA,*solver.solver); - bool fully_unwound = - is_fully_unwound(loop_continues,loophead_selects,solver); - status() << "Loops " << (fully_unwound ? "" : "not ") - << "fully unwound" << eom; - - cover_goals_extt cover_goals( - SSA,solver,loophead_selects,property_map, - !fully_unwound && options.get_bool_option("spurious-check"), - all_properties, - options.get_bool_option("show-trace") || - options.get_option("graphml-cex")!="" || - options.get_option("json-cex")!=""); - -#if 0 - debug() << "(C) " << from_expr(SSA.ns,"",enabling_expr) << eom; -#endif - - const goto_programt &goto_program=SSA.goto_function.body; - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) - { - if(!i_it->is_assert()) - continue; - - const source_locationt &location=i_it->source_location; - irep_idt property_id = location.get_property_id(); - - if(i_it->guard.is_true()) - { - property_map[property_id].result=PASS; - continue; - } - - //do not recheck properties that have already been decided - if(property_map[property_id].result!=UNKNOWN) continue; - - if(property_id=="") //TODO: some properties do not show up in initialize_property_map - continue; - - std::list assertion_nodes; - SSA.find_nodes(i_it,assertion_nodes); - - unsigned property_counter = 0; - for(std::list::const_iterator - n_it=assertion_nodes.begin(); - n_it!=assertion_nodes.end(); - n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator - a_it=(*n_it)->assertions.begin(); - a_it!=(*n_it)->assertions.end(); - a_it++, property_counter++) - { - exprt property=*a_it; - - if(simplify) - property=::simplify_expr(property, SSA.ns); - -#if 0 - std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; -#endif - - property_map[property_id].location = i_it; - cover_goals.goal_map[property_id].conjuncts.push_back(property); - } - } - } - - for(cover_goals_extt::goal_mapt::const_iterator - it=cover_goals.goal_map.begin(); - it!=cover_goals.goal_map.end(); - it++) - { - // Our goal is to falsify a property. - // The following is TRUE if the conjunction is empty. - literalt p=!solver.convert(conjunction(it->second.conjuncts)); - cover_goals.add(p); - } - - status() << "Running " << solver.solver->decision_procedure_text() << eom; - - cover_goals(); - - //set all non-covered goals to PASS except if we do not try - // to cover all goals and we have found a FAIL - if(all_properties || cover_goals.number_covered()==0) - { - std::list::const_iterator g_it= - cover_goals.goals.begin(); - for(cover_goals_extt::goal_mapt::const_iterator - it=cover_goals.goal_map.begin(); - it!=cover_goals.goal_map.end(); - it++, g_it++) - { - if(!g_it->covered) property_map[it->first].result=PASS; - } - } - - solver.pop_context(); - - debug() << "** " << cover_goals.number_covered() - << " of " << cover_goals.size() << " failed (" - << cover_goals.iterations() << " iterations)" << eom; -} - -/*******************************************************************\ - -Function: summary_checker_baset::report_statistics() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::report_statistics() -{ - for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) - { - incremental_solvert &solver = ssa_db.get_solver(f_it->first); - unsigned calls = solver.get_number_of_solver_calls(); - if(calls>0) solver_instances++; - solver_calls += calls; - } - statistics() << "** statistics: " << eom; - statistics() << " number of solver instances: " << solver_instances << eom; - statistics() << " number of solver calls: " << solver_calls << eom; - statistics() << " number of summaries used: " - << summaries_used << eom; - statistics() << " number of termination arguments computed: " - << termargs_computed << eom; - statistics() << eom; -} - -/*******************************************************************\ - -Function: summary_checker_baset::do_show_vcc - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::do_show_vcc( - const local_SSAt &SSA, - const goto_programt::const_targett i_it, - const local_SSAt::nodet::assertionst::const_iterator &a_it) -{ - std::cout << i_it->source_location << "\n"; - std::cout << i_it->source_location.get_comment() << "\n"; - - std::list ssa_constraints; - ssa_constraints << SSA; - - unsigned i=1; - for(std::list::const_iterator c_it=ssa_constraints.begin(); - c_it!=ssa_constraints.end(); - c_it++, i++) - std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; - - std::cout << "|--------------------------\n"; - - std::cout << "{1} " << from_expr(SSA.ns, "", *a_it) << "\n"; - - std::cout << "\n"; -} - -/*******************************************************************\ - -Function: summary_checker_baset::get_loophead_selects - - Inputs: - - Outputs: - - Purpose: returns the select guards at the loop heads - in order to check whether a countermodel is spurious - -\*******************************************************************/ - -exprt::operandst summary_checker_baset::get_loophead_selects( - const irep_idt &function_name, - const local_SSAt &SSA, prop_convt &solver) -{ - //TODO: this should be provided by unwindable_local_SSA - exprt::operandst loophead_selects; - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - symbol_exprt lsguard = SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); - ssa_unwinder.get(function_name).unwinder_rename(lsguard,*n_it,true); - loophead_selects.push_back(not_exprt(lsguard)); - solver.set_frozen(solver.convert(lsguard)); - } - literalt loophead_selects_literal = solver.convert(conjunction(loophead_selects)); - if(!loophead_selects_literal.is_constant()) - solver.set_frozen(loophead_selects_literal); - -#if 0 - std::cout << "loophead_selects: " << from_expr(SSA.ns,"",conjunction(loophead_selects)) << std::endl; -#endif - - return loophead_selects; -} - -/*******************************************************************\ - -Function: summary_checker_baset::get_loop_continues - - Inputs: - - Outputs: - - Purpose: returns the loop continuation guards at the end of the - loops in order to check whether we can unroll further - -\*******************************************************************/ - -exprt::operandst summary_checker_baset::get_loop_continues( - const irep_idt &function_name, - const local_SSAt &SSA, prop_convt &solver) -{ - //TODO: this should be provided by unwindable_local_SSA - exprt::operandst loop_continues; - - ssa_unwinder.get(function_name).loop_continuation_conditions(loop_continues); - if(loop_continues.size()==0) - { - //TODO: this should actually be done transparently by the unwinder - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - symbol_exprt guard = SSA.guard_symbol(n_it->location); - symbol_exprt cond = SSA.cond_symbol(n_it->location); - loop_continues.push_back(and_exprt(guard,cond)); - } - } - -#if 0 - std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; -#endif - - return loop_continues; -} - -/*******************************************************************\ - -Function: summary_checker_baset::is_fully_unwound - - Inputs: - - Outputs: - - Purpose: checks whether the loops have been fully unwound - -\*******************************************************************/ - -bool summary_checker_baset::is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver) -{ - solver.new_context(); - solver << and_exprt(conjunction(loophead_selects), - disjunction(loop_continues)); - - solver_calls++; //statistics - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - solver.pop_context(); - return false; - break; - - case decision_proceduret::D_UNSATISFIABLE: - solver.pop_context(); - solver << conjunction(loophead_selects); - return true; - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } -} - -/*******************************************************************\ - -Function: summary_checker_baset::is_spurious - - Inputs: - - Outputs: - - Purpose: checks whether a countermodel is spurious - -\*******************************************************************/ - -bool summary_checker_baset::is_spurious(const exprt::operandst &loophead_selects, - incremental_solvert &solver) -{ - //check loop head choices in model - bool invariants_involved = false; - for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); - l_it != loophead_selects.end(); l_it++) - { - if(solver.get(l_it->op0()).is_true()) - { - invariants_involved = true; - break; - } - } - if(!invariants_involved) return false; - - // force avoiding paths going through invariants - solver << conjunction(loophead_selects); - - solver_calls++; //statistics - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - return false; - break; - - case decision_proceduret::D_UNSATISFIABLE: - return true; - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } -} - -/*******************************************************************\ - -Function: summary_checker_baset::instrument_and_output - - Inputs: - - Outputs: - - Purpose: instruments the code with the inferred information - and outputs it to a goto-binary - -\*******************************************************************/ - -void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) -{ - instrument_gotot instrument_goto(options,ssa_db,summary_db); - instrument_goto(goto_model); - std::string filename = options.get_option("instrument-output"); - status() << "Writing instrumented goto-binary " << filename << eom; - write_goto_binary(filename, - goto_model.symbol_table, - goto_model.goto_functions, get_message_handler()); -} diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp deleted file mode 100644 index 03ec90edb..000000000 --- a/src/summarizer/summary_checker_kind.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Checker for k-induction - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary_checker_kind.h" - -#define GIVE_UP_INVARIANTS 7 - -/*******************************************************************\ - -Function: summary_checker_kindt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_kindt::operator()( - const goto_modelt &goto_model) -{ - const namespacet ns(goto_model.symbol_table); - - SSA_functions(goto_model,ns); - - ssa_unwinder.init(true,false); - - property_checkert::resultt result = property_checkert::UNKNOWN; - unsigned max_unwind = options.get_unsigned_int_option("unwind"); - status() << "Max-unwind is " << max_unwind << eom; - ssa_unwinder.init_localunwinders(); - - for(unsigned unwind = 0; unwind<=max_unwind; unwind++) - { - status() << "Unwinding (k=" << unwind << ")" << eom; - summary_db.mark_recompute_all(); //TODO: recompute only functions with loops - ssa_unwinder.unwind_all(unwind); - - result = check_properties(); - if(result == property_checkert::UNKNOWN && - !options.get_bool_option("havoc") && - (unwind