From a9d762673a3b999dcbdc1080ae9d25d165977c79 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 30 Dec 2017 21:10:25 +0100 Subject: [PATCH 001/182] fix small linter issue in opam --- opam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opam b/opam index 6c17005d..b7f0bff2 100644 --- a/opam +++ b/opam @@ -12,10 +12,10 @@ build-doc: [ [make "doc"] ] install: [ - [make "DOCDIR=%{msat:doc}%" "install"] + [make "DOCDIR=%{doc}%" "install"] ] remove: [ - [make "DOCDIR=%{msat:doc}%" "uninstall"] + [make "DOCDIR=%{doc}%" "uninstall"] ] depends: [ "ocamlfind" {build} From c8321b4098104b339fd9e031b2a454c4838d596c Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Fri, 26 Jan 2018 13:49:38 +0100 Subject: [PATCH 002/182] Added ICFP presentation in README --- README.md | 6 +++--- articles/icfp_2017.pdf | Bin 0 -> 155520 bytes 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 articles/icfp_2017.pdf diff --git a/README.md b/README.md index 8e1d42b7..403ab049 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # MSAT [![Build Status](https://travis-ci.org/Gbury/mSAT.svg?branch=master)](https://travis-ci.org/Gbury/mSAT) MSAT is an OCaml library that features a modular SAT-solver and some -extensions (including SMT). +extensions (including SMT), derived from [Alt-Ergo Zero](http://cubicle.lri.fr/alt-ergo-zero). - -It derives from [Alt-Ergo Zero](http://cubicle.lri.fr/alt-ergo-zero). +It was presented at [ICFP 2017](https://icfp17.sigplan.org/event/ocaml-2017-papers-msat-an-ocaml-sat-solver), +using a [poster](https://github.com/Gbury/mSAT/blob/master/articles/icfp_2017.pdf) ## COPYRIGHT diff --git a/articles/icfp_2017.pdf b/articles/icfp_2017.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6251453fedffe8d9cedacfec893acb1f0c596dcb GIT binary patch literal 155520 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
    !C`hJ29V zsEmD$+8Za`-5>6<%X6kv_csDF$~qL9ZYFI#R)mlIZTSx{hS z_;ygOzCSAQP(L#dd-Y`XWQp(+!t*>#=G4*_s!^-W?gM^WikqPQDYTMi!`^Z*}oI3Xpssof?yEa6KXZi(`mv|>3^Jd8iG=op-?RbZ_X%i*zRPs&R;DKd96ZRnMfa1Y{@**@@hbZ z^0`5NiaJzJTy@lHB3^epK%VY>=P<(cVa^K*^UoN;X(6phquN@Z-DAlI8jpF!cJKIn zXha?V^)pE`yVo2-O*|rC(`kH#<=T(EUjBfbXi4IBDr^1hk}(J40xRo>TyZIOS$MJu zig~7Mc};hbb68M|EZp0DLE}d0={zQ{Ki@Q|@@=Jm@N#at&6BP*koT(S)i@s_EO(=pV)*8K2LvbkdSn254l)=7fdJC4&8&7*GX23SD zyfAkbfkls~w^jM8m)NPr@xo(~L z_+hAbSkt1;kpfZ`DukGDb1R=AJ)!hB*4obE6($`%A618Kz|p-iHPS}u>vCtp1f(+d z$Q!N;8@=T}bnA63f5BsVifMEYHo}0(mNO@YrHA{TczwC|_2+AbKn^Dy1(*U;zNc$8 z*o=#sp=HU)Yhblxtm43d!(yZdny!pB= zLYVoAv@9hE3H9)1m?ly`r;qe`g3Rk8nc3M){6v_yn?{`gcD(RRlz;H7b^xBSZXofQ z03QrqEL+a36$$QjMN%ExdDdL%@-=zGZ(<^SQOIGvt7#g}KtEcn&{pu!%ILm)h0Y_| zBQ8CbUUXq~qC$Tk#Dqt4MW&V5oRSYv4Z@V0(s{ey6iw2_rGo#x$)*hJ;j`Z4)B63f zz{x9u5=x(eW(FPXR*QDw*aL?l9cNdjI9@fAZG`@#TPU@3#q3yd?rEOerF+0Lf{h^X zvR3x$x+^l0Dt`;KxcH5P0DcqQLr&#Ok|$rvSQPs0UH8Q12q_^dL^?*&HKF7x7r{y0 zaDOefMzQ>DB7bpfO@(lzB2njt_c2;UJc=z_Szo}l!g&;2ZRe)B((T!U;H~t%vKdT% zqDe3e3%t4+-2AqBzEzhz_^*ziz=?`*Otm#d#6{2q{B~#M&-8Z){}`D?*6PoMux)Rh z(Ftt6_22tcxn&sgQeG)1ZS@|fj|J^_L_OdgsI!i$Y_8u4h_F#cigA5UEJC2LR_y@- z8ID##)RRQJVbfCFl)7uH9iBI~u|e2qr8p}b*Oizd@jBeznALC1+qyq02hlD_c3t)b zyJc(L(^ah{_F!+oHm)LB`*d>Kl(K1;smDpJQM${YosIC^wy)zC^Sj`d+@PMA=FZdN zmgSq6^5^%N5oiGad!NWKGse(F;>U!y?3%&xf7Xlj4ZXK}iaSZ97eZfDz*x zPM2=mpvMb;P!*{&4ZcPX8hFkMM>Cx7eTZN;9l%!FMXL86Pw|k zW=B}|SEYN~5S}T~LWnlurJR<7>Hexm)zYM_26*u2<*#1M<^)-?)Ok*7=4%BPEPsEQ zkla%$mJN96EfcjE^c$tjS?$$!&AEMWl^&wMgQwM=0TGnc^#m4@CJKAT{KX5Vc+`qN z#EU@j$4+49nH;0_;YzK@@l`aUUlDv-7Yg2O!{t5>n6f}$^hs&UsN7_DvtCB$wFTm| zv8)RwYJ;KvI-17Z;$uKmPoM?If*&ymzeW=V9Q0BmQH%!J!ucrg;&$Y*7#B5X)=Rs= zE6;Nf-wo_SI92xULKyAiUr)JE_jL?4sN|i8hbay_{5ATc{@zdtDJ$p#VKVD3Yw&S-_1Ik#;U+>yTNc|~BnA!qMuX}M?TtRgOUl0C)koXg zo9GR>7V0PvB)>#c2xO$+%Tx_Q-fI;p!m0sgzoSt{SWzWx7tmg-o@)MNbyE z*CdlZ?57HrD{aIbfu~-ELiSV?y*yq_9CgqXbIS89nK|tB2FC;Z1oUCTTKBN)4c+Yh zNK;=upQ+nd-rd{ZT*h?r0)nnm4~i-ltXRdT1Q@9${x(Nv}f64uWl@GOo0^uJ>4o&N#>&-}f=2!{ykB z)l7%S^fB&5q*A^LhqO}Lmr3g1Ln%;)5XR||c^-+lqpvdmiUnbAZt(HbTA8wDnT(Do z+o?Q;GtSLx#7^JELc0ec_*R_i$iw5%gSI)jewYi~>0v|qb}N_mnw*^CV|Y86>3>U4 zbgaBz?M^|#vmjEi!zTOg9Zr@|>&DFy6y&Ka&hRYR7^U8y>809uQsKLrC%8vXE0Qok zB%Oo8YisSBQ0J_jExF2OBZ(rDk7}j7BwaF!DhR;k3;T=Pys^+8~_FEx#kkdqP_r{5MuE2{{SsO(!ba~y)IEem1Fd5Yi)&lPq6^JfamU6 z_Woutu7(D3kYTLq)x1+n=h5w5=W~CGv=}~zNY2yvsC#Hfa$2fAFx97r&fg|tb(+7t z7C>Qo!47S_CwQ!p&HYg9 z{8z>wLnyz<%>ATD#5K1&(RUBKkof%Y?9CORQJWLnInhM022qHpvb>*q>IxaZb|pgf zp)`FluJRz8mrY1s+5yMhjx{Iph!KL+hwDgFhImyrL-W z+93@Xji=O_E2B@ya@-}aNt>^HjEMc-c6f7pyJGDBfqWNTaYwQd%c}r7`cU@$=ktDz z9Y4GSFA8G<+N@sPBjAO+P*W6%BCl!%r&7KO4XO0t$KYGFf)AtDu`mv*D+RiusTo+L zN2iQ@)sv92d^!H`@Rcz01CsDzOt{*1EBZ}A&yX?JrQi7k9e%p798EeZqtZD&&!Ft! z0idov!fdtRo{~f8HPtx@O2d!x%y?7`fg5>wdHaT!RnDKaMr1plSq}&pFa>|p@Fg%M z;1Pl*f5>hV!;`Us4!GTbiUeb2;x6s&D>@GlCMRn8^vvz#ugN5(ERf4a`hG&z@Fg2@ zQp@zc7Y-M;o&x3nm5`@9Op-0P0+3rAH0-!Cgu|jQFu0;nPHwBQrH; zDFf4M5*XJtk6{Z>Xo11v6285SLBenx2uB6k60Pk1Y%3!0WFxK6%{iaw7^fuW8JbLB z&fk+Nu95gLa;iLIppzFIo57Fl1P^hXkud{u-7h}7Fozir_mYOhf}tCoLtE+qMMT{u zHv`>jHta%}^h{myY=BgCkgezfjwxaTl5Jb{{r>qUy}sko5L+=jtGA>^6@J)+eym)Y z*zXO-Nr1vHEcd}v&{8UQC@Xja*6q^p!)p2%PA|OI3HBTv$7Y=@HA9w6oBUp15cZE4 zeVN1;qU7`6DTlQ zh)C<33Y|Z?N1^nOIc$1?heTMT}*IP+*_UoIe!^+JByc`nZPTUQ=vTk_x|2@ zZ4x_Or*(c1wqG8?52G9xK1c_-`RY4Uv5M8Wc0Db%$k2kt+(`k@BYx7oGnym$_D=B7 zmQL1D%rlKR}M zx8}?)@jCE3k!$sFw}|-+t((t@N-u%MtzSB2x_Ef1qrBb)t+s(0bk2AHVm^UdY-r8pr99@T6^Y(x2d$6}!?cPF?L zlefK+NLFHOq|Z-T0+y6E$rp=QOIpi_OIhFJ>G7*mg#Dw`h;Q(U^c<8V`(JFQOorID zCfs*DsdVJt8?=nj%P;0Zwr>6V?;<={y1<>(tzCY|6YH2b&8kF|a4*qU+|lh}aPm0! z#6@mBY#E!eG(2imikS12%3&b{Uo>&GJ^9i{55|bZ{TI0E2#=WGij2>fPmT5 zKAXfA*tg772JH9iU#<1Df*I}FJ~w;)2$EC7a<>?5%Y1C*at!}PYa}B6*(*I=ZHvjr zJ+e3}fAC3C`^r>*YD80Q)Tb_altd0be(v$6zU@Q>OX0nXB%V7af7BfxZ7k&#d_*Iv zhGO^=hYSqBj&i&+Z0MVNa%T62&*2`v^z9(xV8TG#VQv)(5A5oivSE)I z_@6>)*9CZXuWq98X{V*i+!?qf**`i=C-LJZIIi82$ryt3PeHOMQVTZbX;}(xN;3h! zMt}DyfbI^(G3`ho2u8YbHP}@c&_$n!5@%C-UY3xDxM8wWITDXzRxBSwDI$K(oaAtq zvkwuFis+zr{)yE-ZMmSdu2oAgJowz@ufx{(Yu~m1gWlL%v|Mm9eIw!)!~MHq9k3%C zm~H|7;NXBFmDR2=!B_v8c#T!8ZIe%odJjG*rhTiJ-Meyrs5i5VX^W6^T`vKUrG+~F zSU?$<1L}{cLpfiEcaa){QBBOvSah~K``;03w$dggX>&j-8Kw*8nwOE{8hfVm5XsvK z4VQwCJce9RAQPYGGWHrQGc?5I&Gl>8SGEy`>2eA9*dG>uT+Qv%A_3zp!N#Zy7$Rp> zp;Kc&jXqYo!YTVDFE|5$v-=j2EW{l#9qZD1!e%<|Q?*f1A`*iOS84NtFFj~u@RhGx zwq_%r{6MKE`EfnR7Az3eR5S|C*+Nj2 zt90W438QN3`9ca`M8M#(fC6SuzWgT`Erf!)KDQ$k+*VCzR58I;ALg-Dl=LW>kEg_S zN!BR6hu>YB6k8*bQqrbE7IJSt90U?T!au_dOninmzHLFKG4i{zA?5So zyIE<;X6UrZ{pI$7EtBNPOoZiQ7Kaan)7Hb^b2o-CcT7U>Y`QNC{0t0c2wU!I2ne~c z@R;ski7<1v=%pRghBQ=CFp!!D*pS#akC^zS^;cyf6qz-%ML63rX1!)qO8g4A3O|(B zQIGB)+M|dZV@FqY>6Uyx2glpomX2-jNw7G27ejo*_NkSj=I4h*G|Z$z@NVMDr|aYh zP5v4kbuz{M+o@3-;fFyHN~Vx`M~ZO?&*Ydn1BsDXM?z1C%Bq~ zi~j6lLq-omIp@*YWfs0Z4a7hT@rjV-!gj<+npVXS=p>XGxkSXX_~*CrqE#<-gNdAY zdjTelLuTfi6v|>Min2AZwSfdIsj;s%L2=<+B;j+H#WIS)jHxA|$2?5o=3j^Q-fr9v zHGnq){$TZ3Er7~x7$YluqW370C_GCpfoJEFf$oCcOWHRD9z%Qe7h*baX7Oo*UZWyW z{GZuPHNkc}EwcqDP7XJ?izf(OAd28_vR?bTjA=VVG3jqCJPW64NA2W8AVUb zRvRF6Sj*bjEJ1%3MSX}pi7LLFPnZ~0+OQHR1a3Yv%(w?N)=T;5>GmHum=AsDyV?i$ zFzr-W7B0E&qAIU&j#?h3A@a((3K1V?Ve0h!&ey;gJWNZxN)^zS8uMZWp%BX~<28fm zA#{U+tAmLB(@HXh6H%zW)M!HtayA-MY63cRvjzj#E}oQB*f^D$wf2PBhg_x`LDj2w z1K}2)`55`jk)YO&cILCC?0mI^V0%ZlmzGs4fmsW#d!eZ2$Rdm4_tW&`ltJv>lLRiq zC|yNHXKLt2OtZX+ViN^kX1hq2KE&3_cZs}O;b=q!ykE8gb{$rhAl)VzXjc`UPo#B> zesp-CTfM4PoU&Waxu#sqRwE0cPc&M0mny+3H%0I=4lA(ExnySRN@;u3AQN=4^pTQ1 z+wGHl??A6yCdi*+f7~)CC%=(l*1fZNn@0bPSrP7eDUSZj_6W_~%vd+sG&r)r_2|UB z)#3=h1=2%4h;wo}{_J-2U@ z2hzF?x2_m^0inPMIT@uF@^k$5)If!ZB(A;CH7y~OtA)MJW^c@qtjL;2pl${6niNyh zXQE2uOQM^q@gl2R?rip-w*^4p!<=+=)W4_z4NyJ(BqF7`bWJoHBZ+Nn^`hcG)`7n1Sfvi8#dsQQpt95V!PrEf5 zBiwfS>HP$=(V`I8C(H_KK2_$g>d3U_io(&nlC!@eitnQ$KTM=}1X$qFDwSI2Po=ye z%$;x-zu!!NQAK2Nc4J9o5uRJ|9A3lqb}F*2^kU%r=4RJY4?uSKpfzDCk?NH9ZvLa+ zedd8fz>J%kizYo+h;JyoJZbsc78ZlFL9cq}!h@-Qwh|FvU;ei;H@GqIYj}T1f#fSb zE#6jB%vdxX|7No6O&&gfp)p?@whx6IW-%PtUA749@1I|0cES@%0 zScx3=4D+Zf;&ET1+2l^B&mHzayk{pe= zd5NEc6noRMU;5GQ`xC`bcVx{_LdfHAS*KMe#)QUJLu7v*aX0yMfos%Bj%Tg+j#K6~ z9VGS-C4-GtDl(1gJxTj-?1$A_Ook#8Oyz+bBnS5+>SguW2{7OST=t~iuE_#=G-|rt zZ)jZKHy2+em#R0*u&aK5wI70}cGInUZ0eXX+x+HEnY@<>MlDgN!9{;$#Z#U8nu1r}Sc%=n zsh%_<-Q-@JEP;u)0~?446$7|`))c1Yke@FQDV1{S#JSdc!aWzR=P}*#p$p9Y?<#%+S#Yoq}|HX+niE&ac@AI}%Nn%`#JSq^z zJ|$CUSey1ZlK36yfxUE$T;8alEl;X&esSoq^yBn&E~p+7IA~1zFm)pZ#760~#e1ub zjUD2j9a4Xdy-CS}>Owk#NKXnRfP1waVA;(#4XE3Okm&plvB31orum z**@QFe30yfyuP6X^{zEq^*}7#F)NGO0)KK(?=ao08p<@?NplCU^Q=1?zE5b7&IJtb zusO3gvC$TG3?Gew^u3Za2E`|2l~U#lf1%zSr@qYxx=O%|%c7)MS#pJl;8oqneZ4oZ+%BhuU8ARo-n>HzaJi>z0c!6I5VWsV}y&xXE0+ z%^olMAM&M`_6eH(`&g>S!b+>h(cy{I;D{IRGe@+rA)2VIzhzp#Rq+tfP0;bh&nIG$ zzQRN`dTmBVLk3#POnX}u8_CJ0D5^RxzHsPe81np(>nz`C7)Qi?g3_N>9aJqgMO3a> zZn1%m#Tel|0o>%U@@!S=@})6EY)G}o^&0wQj>LJLgpQACv2KrYd33w#B zs7Q&2q#5sXW~MCBURlVLn9Fj=c*mBA zwxt0KB^N|X`jn~&hqG3-Khu8S z?FEe^99<1CH(hE=ibAMt z5c^px;p<48VLCw=>jm`1sS)2{_T?$A>{4o#&fMe{FC>CXw)KA{S@igncfNAoaAxAy zmOUd#<|DKHQzc?bCJQlBg8L8I@L-l?)W z28WK5@R8h3J`rO=NHx4YwsE72(`7X%3;!UJ_etv1R$Ke)ozx3Kvs8nvVBukBxkQ1+ z#+gIZucA2E-SODm?zz1^yt6T@K54~vrdMU$DG}(6uOhAOobVG58}*^ zKLJiAipM%y!Y@u&V^=G|AsE;Z)bhk^2BAYImV;=HJJpe3CLb>HOz`0enCbX`v3ZJE zq7Q|kTn?W>g;|g%imaoo6@Y)?NS>fd)zLP)KPpz29lX3vafEVdf1w&xAIJN0zA#9J zKYOdikDBrWeBM*NA44;EkX3wM_Z~Ssr2t+#a@c~&L+kNlk|T-~i8uNAMsemE3!T`J zuxAn)vO?$J1%E+B2bG$HsFjgh-319(9G4{RiNY3NJ|HVQFQRP|txkk=Uc$HelXs%$ z!^9`}3?IL1iyHA8jtGtm9p&|?T=&l%^WzX@o|(grxbZp2%bcR5kZN@G)ay01M7s@Y@ZA8zGjcMa zqTMU3pCb)24qrc>efqWy;2jv$A~I$!%GBE1-{m_{k+ghdqvMMHc#RQ(vG1a^K`9)wMobdy3SseCzx;?t7&$r?r z-FGS`W%eBZFqXhC)f(T`(Wl4hMu6o zCE2DLA?r@)yD=&I;A@DBCzAL2P5>EpnXLGQUnXiufw_icNKkf*)OX$pmgfNS4G~4z z;i}@03#A&CyxSDT1zwZyZ2!qUU#7k_sor0gMj%_}bQX5vLBD8_8fRG4*G5H4X}0H( zO;uWDdL?=9II@N(xpD91dfM^wn_{r1@8y9QeoX1FQs*%TMceSV%d1=kIzt+g3xuxu z^-{*zq|7IYV3Mz)LKFUm4P}*Al&h}Dx=GPPS#oSCfhk*;iw3ijP(UHrJ9XTmrVR_M zWPjRZ0IkGaZ~M+K7(Rv0%yEZ+8j-#1_EL#_2QWOr;5l;c-h4Z}$44g#GA56xsC8Ba z9vO2qWS;EXCZuUBVvTw^_!2cIy`qGbs-uA~KllK0-zFMWcSH6xisF^t6?KVn>C0@B zZ)6HkVCyBX3_6Lp?sOS*Kf`0Y^F^N;JVeuc1hCA{3u=xqCE zb(7JxLqEw5!C>rED~T##gm2}smFt!o+63_PoX}BG_ntmp zxgXPh_bWr1bDCT)Wo3MH%S{pw-$YjW`Rc33`3`plX^STlLAgGB3x&4tjbKh#n^Nv)RM1MyDMI34(!;#U-LCxj4AN7U#*TySytI=w_am~`0Se}sB(gaj| zU0GQR8a9@2`Y{=JBsz3c_)}F%(>I(T#ZsH#kt4W}i=UG`$~OyP=X5_g=y(%-bzt!otAE%8=py2Aqee63w4f9D zsyFzAK^!p>XX|GMQ47tAjjQ5*o?l}aahKg{!R9QdVmj<_l7W!bZl_zjb;zoI7Qvq3 zUJa11ve^i~Og>-#;joP~kyqUdTL>Y5_Mn}3e2nIsz}lx{PbkcH^wMO~@2~dFCBM=C zqCa-wDNAuPBe=hPopDNgsY0S=SdR~p;n+pXY%lAU>bU5|JjLVzecqE6LPSei$xBRD z(C$V3>fo|;I{Y1Jzgk!^cmmwUs>}J@B28yr@_x5I!Fb{Ro`m#>e-VGuY2IO~B5u>i zQ9bYZ7eDIhvHT91)&98)X>Zpadph(!vrsGrJcyb-Xn9=bZ8`6}I7~8Gxcnd1c1)F{ zX2t<|-#_RiaZymK#eKvTq52w7s$B zmM*dz2)hv5ssO?C4fMKY?oezt9bn>*?nEAm>fz+^JK%%Jsv}xptN0Ge*Aa3^hPvFO z41pJSuOH+mu-#r!)TQ@>7S?w${RSv9O^ra@5cP>a<$>yi-VG^pVjCbCOG zJq42#nXL&XpKZ2#0Ct$%eEe+atxHZkP2>Cuu7Dfu9=cL&R;O~89O-h5CQuaqcpS&Q zHK=Z=`9J{6O#2IbIsi#JF76ZLcZ^-0fs&sV-zsSMF=Zv#=W?CWGh$vMO&zB);WpVy z<&L3h+Om6Yb*YZeqj9X0;dbZ0BLpxz9(}FEOtq)jcuecMANpK?A2>Fq4j?TgWtP#u z!9KH$&uAZseF^l*_stva%Cc9>Gz~{lVAXSxW`DYZO5y(YLX5{$J8AWkoFRpb;F5Xs z*WTGi&b!HA3m2q91*WnB{2;;6HSDph-5C;8%*OJl)$`^4t9}uV2WXh7PlSgh6xfVj zBP(Ya*3T3(2g^iC zxI%<@@O1EYZdZ)i^ucyMUcL6eba8C;CF4UR;n?r6a{Qs2oEiAFH*;?4ERlS_FG`yR z#UKIT#dcMVfzB@a^etMsa64xVZerR4*P_Vz&TfK=V-#C!zXhUmu&2b)Vj|tVte8P7 zI!~gt_D(c4*NgwNOXpfy+TAGg$az*d=*1ejlao<8f}Q8?Gn= zF=W+(qpBUYQbRZM4K<{m(5WAEXhqYyPG0PiSQd{}E@7SMn7SJ?*92R`96bC&l1Lhn zyMGz1ouC4U1gKP^Mb$1e3R>mslaL7aBQWdBb(0>!>nr>p~|x)p0Q^ z&L2?8dRAHtwHYism4sPxM&(=R6x?O9TL{R?^w7yzwtVbSh~vR7*$mB>KG_@S*{x>t zCv9dGX^{f69$w$BA~QnQ5K-wK_D?`JQ7m-yvNy6CV50eHpyPGqaA0PvGRZfVqi8}4 z0c%g=lliIK8q=<1pz|hACg?11z6ETp8;*UKx7ggf2F!?B<^%sYxLUmAN2+h(ZIjHz z8hJ-yaB{CAB!=6acnW!=qY3Fg{h3nqg2o(oR#b)ziL!g5<$KA{ktB;U@0wfp59^Ya zMC@=E{(t?GtbP741+~$fWhLUREa#r_afy0q2Vw_vi!W&&l7!qz%sIKuM%dM^p2(rx z4@-g*CWSmlta@^~TiHih;q@?233uAA%X*i` zRl14xi7PExecpN->&nNZQSpPawv;ZF872v}w#_+~-x=vQ`cbAM0k*d86D#OGwY60y zF2>fW@Y3M8_o!@zQc(B!M-=O!#j|BsRkBPV=-6}pkmuTxa>zsOclLFuC>mm820BT=FVdX8@kUi7N8B}|O> zJvQ*t z`{0-AjZMY*s~>`+1EOhgQNQcb%=8JT-J`Mi?QUmJ=id%=%CC$XNPtruissDrHyMBCPE8XxV$TA%`q6=3Z`spii~33DGb<|XPmU5 z3)YY>EE4Msd%6M<^O@yegp`C;m+W15oLQ;9rp1efxbb))p+s-r6ndmkS-jo3P_AP{ z8ZPuE%p_^j8iT^gO{{d{nTW0~vYyJVV8Jh1lIreF6B4ji@Tm46H(~+z<`Ucv!-YZ$ z7o*0<4dl^F@^J$MJpt>9#W!Y|lPE?kHuIw$5{Ks~T$-lKWMiPS zLyK$q-K`8Bk1si*37jv`pZ0lLF_R6ELPc_-PW&WSLbN#K(w}5=Yg$cdRSt4l8KV_Ae4V9TORJ*l<~XOL@E z#=)*=yuK_?DDvKw^7%KH=o>(Al!Aw@NSx1)qg5B%oFJ}Q$!7W9bZIParf&y*Eps~biUzo_@yLgA< z@WbUYqn(Pi`uwP_EJW1l#dPN!c4)lP=gvRReU^ry%Z@-&DMLbbiW|M_I6;X!2o7J* zf{qLf=%<@YFp7#%5AsF6$Fl%RSKyS}R=aiS;BTTEN20_r?LF_uCVJW4hvGaUu+`<& z6!~Pr;A|=0FOhZE6m`>x1sb@M2#=Q}*rKJTKE;#9ihmag(8D(E)${cu&X&zw-64M5 zrg7y|vu<2L`Da-p`h%W^2!E|(DJw*w0ePkDqxcnNcgaF~7fLUZnTr_C{Hg?WBalf4 zecXobeTnwg3~myaP2)${v*gH81=xApnk*O*o52d3bx^W7=u15SU%%oy>}Nz2-4ejm zLyG0|t=x!?Kee>S=w=qh<2|Tzi`(7K- zMqY&MROCjY{5oe1hQadp;VoR ziU~{1_YSK0_``iMSAL8QXr#E6HT%tfqTSArhfiKQFfMfDlh2XUV88iL{bTOe#AItO z<{CR{@mr?^i>A(xtAZET)4Tez7pz)#&qiW_k`>z%PA5oJ0$%X^R6TQm{550PibjjM zxz7-ja2}4Eb3_cd%zS{?OVH?R+i|KxB=~<#EvP?&huXGCP^BW%I|-7U0JMxO^ytgt zy-u$jx;_k8rw~{$!n#D(&}%3P2}59*4*DE~)ij4dxTJ_$z8}Y)ftDmvA$Wad9b!2M zms6bh2%^`R?g?`Hh+`YDGVc#SF4!4y>HKn<{2q6TJOL!tw*2*!JN3Tr@%g`*FdViV zwf&XrpMLUw83O{fkXC{eo33P-9Wpbw@Wp~8^PD&~sY=+N98vq#iSy6Um$LX@oqQK8 z_1yZuw?08q!aN;MI^!7p^xNgnSnFUYZ^+BH5S1e3e^f{bE$qvg+t-A^M~Ai&-8wI* zQ0y}%aR5N)evFKN5#kN-n@t-4P6@QihIgr1k$w5u&8mYThZ+gi?5@`HL5gP6U1>n0 z`%p*o2a#Q(@eW4Mall%9krE}>e?TTCw%LHZK&mi1=52Y5L@)^AuN&&>p{|F-47Cd_kavgH*I92CmK7uBTd-PranG?`d1b66tgF zb7)bnyE-~ehYKsCtWc$?JuzT;WaW(+*fD!|+_09B2dVRXgwaFZ-1{f3c06=$7!=y{ zOthMSB7b_n-q8@pu*uC;!x!ij{Z;p=yHpcSeXoY6GCn5Yg`3!Fw^q%DGx=Annab-1}&l3_GnEB zYN1D23C|XwuVZ3)sIVk;6hlSU^j!@%IE`4ZzM%}7SmCv56wH<%{VQ545iTGQ5(yL@XiPMLg}No3XnKMRo? zqL3Efn{nOv0Ho9hU>mkFvp*h6sdy*;>{1Z-WH0fOu_P5G(IzWW#|_~U2LbhAZ%SvM zp5?F%+!}WQNF^yXmFAnLT!X7QwGpq6WCIN`TRIU!`ojSwH60F?8c3 zv1YqhOn!{)qm#%cEGh<+jl8MF3rUZkeN?d5J9l+ zO&%KiYj39S`PL@VOi#>~7>Zj*#A(>%xV`<>zR_mRNez0gU{9;&TmU55I^6FO0=n+5 zTo`rR|zESyq&Smz<~=JU_>L3If!YdI|D)FmDC9t9Y!(_zQrGrw2frTuyi z>|k{9w=+&7jhuxhh4vov6G!K|Z%|Xs!vSOlvc4o)MIlkQm85k-Lv9y?Kk?)+GTPQQC3N3#u^N1KaCh{^e6NS>VwoL;^f7BZb> z|LWj2pSw8#pyce;!qFANUPK}5k$TXe$2b^TBAw5MF*YuEhFHMN1+@#+Q45#PDka6M zs341h2O46oet1@^?2)+59l}b%;KGvCBt(%r;g@;@mhR3^_7z|(U_Krb8G;i{2ige< zaQUr0b3AIg`&FNZdInm)BCVNn>V!VxS{>XvteOtq{L|J&n_ZC>`gjli7#eABwHI*^ zj5}$W+g=(>vA=3>XYzS=#=Mn9cEeD$vmUn(G4yg4YZ(79;X+>Jc+Ue4TwE}&1nRk?L_bs8?iO+AJzpSIDHSGZzpjwFOT5mBHx7kUD z2f$7d7<68H95cE-Pc&uW|NvD*Qa^t6J>jNx9-sTFWq8k;su*!l>l79qrAls zI5LAu%-K=B>G- zkz7-)id*QywQp0wlN^7>tEbMHEx~n4QOW02r%`Tev^Q4H4Ogt_gMM0QAvyS8)K5jA z6ZC|;3|E0OPWoDoNUMjsZk~|mQT&F}C{PL;>YXttP)za@AMk3) zsIQI2>?_9icpdt#E6+Ae_cS^P1wCqoSNP*rM`af?`sRevEd+x z%4sg^DhWOCU_~X-I*3_PkvavT$9Mef~~+m%tUlF6UzG&RK8 z6zwNwF5M%5Hp!+&!hr9>LhRK$Vj#MHKD3*g%o#t zelPbY0n9o5I%uDcaNG=H;K0s{dlntgbn#Jp7p_ZP6yHwcvfXn2aYoIxo6->@cTqej z#A}U+*EC@(++%ck(}bGw4y>E?`dAfbXAj*o9>d{zSDAnNOJp3T)uLx&9mhb|s{mW; zz|b>I?%qfNlMsA&+uQ!+`XjbTeQ%8X;zfk<^++JCY1n1}0#^0&`PU}PEZ7@KMqKcN zMg<8>KquKNL|?rm#U^28)~3FfoL0^H&?2WaP-zSNrA<*2*t97NfG$J5Yg@*ZK*Mi9 zBoV@_+{xtywf>LJwsir-Foy6J{l^z+ILOEEF4n-~iHTHuwodLM=F(%tbv%{pRQ}vR zxqI=+^6pTs59l()`*%N5Ac646l)xh749PSb41C3>V3O#1&P3!R|B2HiRMv02@{*Kt z6sGyUG@632$6~IoY|tHgKeQqwp~DHezLZi49{TopI@LIBT5#Mhr0iiMs}e(5Mq|4I z8f;HL`%(vvy)&@ylI3(Y2xfIIGI9)NL;FhbmqZ!`H~xsI$Vr-s7s_2W(^{{0v1QUJ zU7gSZL)gxebHG-sHm~iyjrz8&x27^1%sTByqNj zlP$|LHB1xrFxY$?s$Bh$1XuNq5L_dheBG!AUsqMJ(j|D^thT2+uhPSSx=FjGo{^5= zXMiOz&dLah7ILr&BVicdoujuC0RtE(91yC>yS_BTiq}C{P(dxzYwx;FENzz`+J@tX zI0t^d|3B_uyGyRWDI@s@$o6B$M`g1sL`cXU9S5-VJSm&{ zm|TR;9GRmj46h7Msqpk@RrghcUTeTB5@}0qDE9???sgMPq2dGyS$_jCP+;-vPN?J8 z3)UKwng<}&3Sv6h5`wdk1%fp6SQW^de%tf{<_OeVjn#S>l3Kn1Q{eq+W_r{T&AlDG zL?9y(!}xbS7@R(}XY%PKXOsSo#<;*W1)#7Xh<1;|EkVCf0=&p%@&`A@=zU+$h*MaISp0d4WfW$~w^Q-ngy%<{?yR5WtV8@si) zuQ$d6#7&CoADGA!q}uQ0HjBUPqGovPJO~->2Rv-(oMpzX6D)An6{+9q#=Jk4_Wihh zNMnf($A`Rxp)v9Ixn0Tqk)b5{9m#2T0e#tRh+kgd3M0V@OroE(sr&EonWAV5o$p-|ci)TFR z^~a6{Qd$izYNCzH^SbC6*$C&dbPJ!1x1FNg+n`Qt6RK^EIz@aLUTpSs>Eb?oAHaUQ zxM=2~RMFtvXoQ({U3wkd z^dbh&(P-r3GbQHuUsafbL3gh3v`t1~y6870?F?E|^L%s;GRDY(Ru5EE5N%%;IchzXZ(o zNkANH-a5DLy5lWWQ(AuCm5}djl6z%kfB0Qq>gP>Q5{+UWRiS4O4}e{DvduB-E9>c*F$6uSpaQVkmdyb8k^)6Wa3$Ict?Y5SOjNyI=#900{vD6(lb{b5=H4R zmzFi3hTH?f3ZkO%aea*?G(2F3|ID=ZC(95SIMRp8jEeEV^HKdnUKVYv*}9%mW%>4x zS5GYbw7;FOC)5P%DCB%4YQ%k)sTD3!e(?6|huDuA^JDoLmP0-Gd?TJ%syuhcBl>tB ztfnBRfwf&j7yAs3W2JUz$C`GB$w^>6Ftlc5J5HYe;RzB8DdSf_Bf@Fs@n1=`fk=B; z#lbKv;=!xDtI&;J=Y+;*))^Vd5Iu>on4*JZb2vQn+X#T!86fJnl{6ABVPc51Khx0a-AU} zO;AFNuv(fPw~_$bj~2Zu^UE6TWf#Ss&wXeu)9U7e&!8v4P+w5ZL9nMQLxLn@b}}L? zBZFG^4nBtxBVB7_>Ov&LYMS0AF|CfW&62r~cz}k2SNCR@@^1-f!2+S+npm7u%zN3l zqG};Mu)Fv8BCt+ zBp*WYawJ&nXGt1O!C~CX;>1~; z{tzE&B`x+BU3d^f3_zQhR`KQw)Mc>5WvZ0jEI3HkRjTOO3c$d7{*KzvpxXiBuGF9Z zc{^!^!@DP{RSf-RQ7BardqPO@2Fy&2y)mI|r%TJ?Y1;$g;jNcKrlUGwjJsfsb0jnr zKmV}(+lgb_pF+gl&8ocmpa@D}!kAU$`VMdP;rDt}HM}{Utndx$E4%^!YhTy!_fh13 zLHQ#~^gW8=RDFCvNxtwb_3Fmq%s_zT0AS;n*{7+kB_A!d8MBi z{-MNJFtC`I*yzIh8udJ{*glF{aERqk#2Hmx>^bieH98{##o*@Nn3<^X6_?wqxGbzz zl>f|nORPOK%y+F7t955VWZzVJ3E?(7Lyk2xwS1-G&{hf2>L+&iO-rWku4rWT&XQ2l*N~hQXnOw}-FR0ug z0rBs{JKaSsiwFf8;N*>EO=zGHQLsQ| zW6sKK1jXn#ZbQ?Dg3qgt+0u>GrzyPx+T68>M&s@r>y-;{gl-ah>L zh{jQ7N}YCJ(e=#b5)mRQ6(6)U`U`|sU++?6kXL*PBrZX7p~cNOw+OYSjsuKketw>w zFCh+g?f)KQ!oDK(9wUGNR9Q$RurhZ~I!n+eE;oxH1A+vu33|<+B?<(};m10spf^5^ zF$P4m`U5+*&dIreb;j-0=D$L2vLX->izbMx#o@_amS$%5t^A%yCjc8a$I(=0 zZ(qjPbkU))E>>&2V_=ZYYtb(1VIVB4D@e4gZZbF2G=nDEdsL< zI}jn;(!vh-DHV}dkkClNAsS*RB!dI0kOvh`Mo<3kq5-bvf69~xHA>?9^mXF{`6-fu zrMagmpM++a`)Y>(0`0>#yVVDT0(_g|eJDXc>8?qa*LFrO7(js9%L@zXAn~&!BVnt< z69Y-Bqr0K2tB>{GlccBdfauu)7=E#Go_W9SYDYLVaJ`!r=h0qkL3~xgh>T63np}Xs zB~a?$QWq}@lL;jVT}FSPKFop+b2CzYU?4ex{C`CLR@~mH)g)zQq4i1bPHiBYRohy> zBA=Dro*e>(inXxpB4NrGVu5Y{Oi>)pW zc0Xda-yaR|)>ZgZczF2VD#%cb?2TaG3b%Kr(^FMH4HA!&VUbWY)brxra5-$zP19no zRp$6Q_&*WfO0jz45ukmdKk$8hGjMr7Sc(Rgq=L9Km59p-bG{oCMu2$=FQA0d9}Ip0lbzZf77pg9B}CpOmNk3UbV zS)WWSpG>~2eT#!DBN+Nsw&y@ktSk^dUILz+XWXE`F#uTsxmaJWZ(Ko^I*_O-IDCH5 z&%c=hKPoy~8*31K-wcnT3BH}*w)f*d8wF^DAB$rn8w>FIV5}h}iS_oMSl|yozAkG& zcHx{FniFSQz~euaxj(o?w#Fvs06l{r)O?sv(xlIyc*_$a!xI?R1QthD)-N@=A7f0{ zr5`LeA4^oJy-|-(W5w^rP+I?OV0eX$6i zv0Je%Kgh6osL6Y&sj2zzw;FFWqH7y-BV$t=V0~i`@OiejwYLy5KTJSTw6s8oA+c2K zARXUq#6Vn{mbjy_Xnxg+g%o{jBLwfW)INWK=k9-)zF@sU=b%5M)q$M?d`a1XvgdZg z@C7J735O=`i0^HO!17RkaJ`q3-v77y#`RW?|3Wk@gq$1yisq}J{Ekioc9Q%NydqS2 z`Vl3Zfcq9DWP|?+ROkB_Y)&DRy0e7^{eq01=idu;+2bc7^_zLM9eY)nUc0iLdWyID zw>sY;CX@g85i-mCfTr_jSYDV|!hBMEgkBRqpYcC^HEuy>FYm_f2VN7l!x1W4e*^Ou zZG40J*lqrR`XK%O1ofc}JmlXKgUtTdW5oG$Si$e6^8?K9w*9Z|w*3?E!&~FvJ9E~H zK>Xf&`toC=>uVq)=7|fNSCKwH8vQ{?yzm|aFsaFSBBX#C4E;KMzWr(XiS#?V9lRx6 z2LWNPAz+=_Jp*QqB6RHUdyn2f^hitN{YO*3(|d{Um93492H1X{B0&QA*9iPMI|j%7 zGXwNk6q0Aiu)kG~Vu#|g(a7vXz!pxJo2pd0QO6YA-~#2}<=!L1XI|vau4st2p$px! z0&YPDY?fk+JAHqg$RYaR)%mWwpu2WA@L9;OVwU{1P?YK=$pu9f^ULR?43l}bsQJ^Z zACxjS2dbgfnNO5iwu@AU0>vV^smeI8^JtcdwO~Vwq?4Z5$9hyZ!vfOT%U3u$X6|-h zuOw@E5?Y8b`PFc9JG=oBCw}%rSdqIi(0J3&+kk0w7kx~_=?(lS%^V5?1|Kwn?1!^B zKrKxis&o+XLJdTOtle^f=A$38MAO6zv1?m-bYvWPMOmsKZIHlm{0Yh!H(|(7e39$n z-jz+9(E~C@$sA;D#zi>MV^Pt)Vxbx4e_=}n84-duT zLEQJ?nBxMP4y`Q-Zy*D;Ed|1OQ7dzq;hGS8+fIr2s~oaFzq-_Yq2Ur|A~#Jl9M#h3 z`)eu6Nsx6V(ynf&>Vw~LrZ!#kngQ1gVDJ<@IJ$ixS%w%}J3=e=-F&|8AJmZV+k7u( zk`waS;hkrUPD9*6^of?bSI!nUuLnE(nTjeU&SZi5Ub71oHZD~*+NslAh>7**G zh}=!vl^OrqC*=>tM-;uesc=-+ky2Tx_11IwO_zM^&&Is+6WkL(S>^0v=oTXTvG3)3 z1&uC1ph3kt;X=*L9v=&jtR|7&cX=>mXbE78kQKOH7E>4w41`%<*bTZ=Agho3Qf2It z0&#iwwD{%HzQzVg?0!ayuPMT%q;Or67ipzy=zBWg!0{LDy*sVRVCq#;*#)9VWmZ?z zxV1n?@>hUBi?i9Zl~6N(5xKq6r5AL2U`7}HCx4W9YvS4&cd zBn#m*rwYO(G$r`WPAhp`90zO&HVQj|aPg|Yq$yH&S*SBvB#v&_mFu-1#+h6y;tqJFf*-1H7Umf)POE_!>8@^-1SyJ22YuihJ} z3XOK>C@gj){qW6*4z`-r6zzMx`a{*DD~6D4JEh|Dz#4~b;vf71-|g61XUn*qm*;J8 zFfiY+Yf3-0MA6L>cm@O*i*_Q8E_U9?22tLu7W670gJqe(N9U;_HUsm|Va|Vd!M(&K z5&drn6B=>&z&)TNXj3b+UVl5|_Pu-h?1*VFA@xGb-Wtn$H&lc^Ud_w1f#F$)6w1hY zjmhp|H=<)YzO`Rg+kF}p9yG_X@@J)h@!H@aV}AwU4$zVS|2G`w8tRi5g@3S9>reqIY+2KV*-YZUNAQOugmhUmx|&qB zDWWv$J^mPbu0HLUEs(!>IPZpsf33YNVZ_vP`Hh2A{I<)=68-+z_sX zAiomYL`ofZzD;3)^84=GTG-lv1AX3-B&u|9QoPl3Sn!g$QEAAz!Kp(Z(7veWYy+QF zWtzB>5dAI){o^B6H-!6@;?630A|3oI+F&et@=LuIj&%tQdK-*jrC~zeLtKlqX07$N zbTf~L)OTL9h=pUeYW((%xclIaH-IMa_r{j@=ER`r3gg-B&2Pj+qA{ezLmP%F!o;?< zZ~mGGU$`pu(_IbR1th$at-i72W?;PCRJC(#V+|MEyKb@BsfBREQLw!;arYcnelgLM zjkmaetT85SXp)Pnml8@^Ns8@OdDr(oPX0k+`6;Lq4o23(ZO$yjV@M9JUGnwF-zam@ zwO-I+ht+aVrBg9ZeyTPBv&8h%na(?L+nRb(qlh%}gli20Y`i@T)vYgSj{)fiZKE}e zv4PnI4M`#DX_Da+2!frF*jYmw8BGCV2IHWyB<)%&a#EKz4eexASS2cLP+?eCOt29c z9D>rfB2B9Wq2ul&)Ii*ogZK>yxTWKX|KQpal@rS4$R?R)l=&Kj2@^d|AS=@@TRvl296nSr_2hVjninad;XW)8Nz{O5>=!;(=GdI> z_q7mPq#b-?oW6e@^mobGMv@$nl9z3#h#bA=(RiWIcLpfD+GyY}rze-VtcF>`+lCQQRaL_uMfkZ6CN3c00%{ERSz4-Wx1ioPzURGg?7=d6Hi z!FvI_l+=mR^=7!gk2G$WQV5Z8Ik!!*gN7>&Re*_EDYxaHB z+pj9k&W2!4hpmen+n<}D)*Yhf>>Fq--9Stolg_5g(Lt0bDjv~*JfQD33c5C~x-GD_ zpM`ZwAo5$d^g;RjL^E`BM!Sv*iEOZ&WZKxaOz=f9^vZ}xr9_~f;h(B#7c6+H`)6#z z&73yAc4?Wz;piCImq*KL`kEnDZA9v$$?)Vu*Qz2gn+!T*7aGv?_D9s zI{@JV<7FXmPx!oPW7ivf2jCbn!O&Ho;S?_XLIVOws@)*XoqUSr&cFubWbC>uPhjtt z4t2-{L&G?vD$G0B;JljVqO0~p2~`r^A2-ob=f!aJNp)2&oy|`xy04~jN)Y#ZX_U!3 zP)T`KGGNwrEmqQqaHctj*+w7SB1>3lepgs3Jo7Rwm;NwbJWw{-&mM zh~R!pPfavcx)&c=-WCy8X3#e6YCz@ArQ9OoG^dH2-}yo_N2bb5ZB?oeo&wLt2|)O# zyeVgkf2AP^F|}`q!?aXI{HSwuU94*PvM$ba;h=XN*RDR2X5A9t$t+?y6T|<>=7jcF z-6f|;rPOm8pv`FWO9MHO?!nVg=Up{mzdx8B!4s8z-HrMW3 z!po!&A>hf`yN31&<18FS&}yk0uIYEhv5n~PGHX59nVyUW@R`c~i!Kp0S0d*U0$26# zfH#;*=(TlNLcb$(`#$79Gx{Af6p*^b+`hd&Fo~s`Q>{T4mbbKZ{=Bsa zrWdxM%cOS_(G}-2WHrVV@y%VCqB6hKl9iE^QVZ~$E-LjdpWUzp4_>B)PETj7iTZiw zyvXNSulT9A^7={1Rulz?3O)x+uXv?N- zfvt*6;o)%&Z<jS@YAY(SW`#~KJnI#v?R}_*h-0h*EqDkjgf{c1hhHdL|A9oOaOL$t-a?n z8{ODs1iIFGS%^!PzK_b$S=plSo!)%BONf~Al1xhr9FLWOBN^w1r>is_$Vji1BwJWz zD@J8?!}ERu%J&(chRfPi@HWC5>1M!ryNM4{4xRl7`!l=ei_?6W^-FvesXb2}W5gG% zOoOMHqf$&@1G4mCb0ARq^n0hqAo?@lWrkn~ne8EHp+pmH1%} zg1L-Rkw*QrtZhJWk{$mu)|%J5>$wqR`?OJX5IA?tpZ+eQ*taA<^&8z)_}tz-KSRmd z^{O+q6eLE|*w?OXUWft?%e#k6GiIzv{;lFuwm~Ke&rrK+|MfhfvT%sPp-H`%zl!L^ zr8-fNwbZF+0&=rYO(C2ytRcpWoB9c+w?SVYAycP?gTauoR0_^zTd?*SGM-^J`2(UkZb-8B@a;VaV zfkJ$4LzQ(OnmGlj1Ngabi9_`>-aa~c7CKNAN7K8HVOkC~UKWM<2|lJ%yFU&${m8iB zYifuy*WoT9TqWK&%mDi0->b$vqfg^{uAcklD*j4;*OpHg7Juny{xSZOt;9eo%fXP} zcBK5qs&nLSHJzz3nGLMjsUV%tvjH@ZL9(sQHiIQUo~keEBNT2om5dY8Q>59^cIIPo zhDu7I7$qmC(W}0Tp`6lcw1W|FE>dFzvVXRzTRt`H=o1&ykC-wPZ1Vm&G|Dh5X zx+O9A$Co8(Z*R8%_aUnP4>lh|caY;`%=GZ^@uDe-mq4sM8*O1yxl4h~l}>xuo6lfX zd{{3wYlTW~NK8(Urb3)>)$ch23Aq>S@}8$1tUhz+8I+==*=T=ipFB zsL7!S)bez6e@K!mZcm_#>IUwJ;o!Xfd{qAVKU6dwC_iI=R8(8^23%BqKI}W}SxyuP zObxo4;~urqPW__mvyXOMxLz1D9g0F+AC}5-9R)Y@HhadsOqST=yKAm96B&I9;P8>A zi^dUPG!AzPIFhuSc!dPVn=T%MQ>?eF^@?LW;gq@o!{y!7Z(yJ2G&AS+sQzwO-sh6w zurL*Ps~?l!YrlLk+NBC}w38Px3S00*w3AZEp%10TsB?7KAg2<8$JQB1$rm_$>QWR4 z`O*opOW^CZkj;*iXV=!@M7}cgC8`9VK7coPg9+l;nwSv>Dtxj-jvkgPLiNlBUC`Ai z1DRhJ`ex^o#2SJi{H8I2QjDz8NSn$x>4ZNE4^QrIIxJ(8(`nNu4BDa`PiV0fYl3Bh!REL6w74+TJRL=o%ek&n3d>|9cwY_cv0_AK*K65kA!EKj zGHtJBcvx|if}n#7jlQ~O=h0K85)RBiNaq5!-w+NWB%2Ms;;v<0^$X#D7HL!=y!Q^Q z6C_OIl06AJH4XWTmF$P;yaKkC95W()z`rnHS9$limx>FXb582gK>9Y<+j<&6aMnSy z4_G`2P?iB<4eTtgjao|6=2p$2iMpp%#2SES zQfrm%s9jfL&uE-#^?DOV`V0B0Ke(yL<{w5Os_f|pIO1e9mdr6l+t!rp&np#z*xiTz zS?~0Pjk^AYY6CSKeePe$1NSIL#Ee16Fa2~0rMd^maPW8H(zp8=Hs82s>HMK4tluCH zFCAomyuKNW3{ugz5{bMQiF;~usL1FGag>H74Ob<>{tgAs3PypBjR<`=gqf!o%^XBU zkdKQeeOG5>^7VeUjd@~|thkTT6NlOQaJ8L(klBz$9DHFRl0CzLTRACmWN?%&NSjpKMXdZXyafPzXQPN&# z9PgJos;4ksMC*sI#YT%#sXGo&_JyxZzNgqL6|)&6PUw=ZdQ|$%$3|v6w=1+~N{V)< zgZUkcS!td^-n+OyU*v$l%X5vFYxnJM&7v=jv~uy21z8NsP$N1kJxNhe@W%*My@NQh zZz1W}RJnVCgv6m?V$JkbOp)jVWGc-Ect-kIaEbtI)G*|(=E?%vVBIEE&>@4J5bA>_`)Gbb z-7c1Zyjg?Nz8cy^Z6IoFIw(NXDuOj*z3U>3%$X z>{0;#YQeHph~$a-@f&=)KoWD_@`E!KSv)5l78+gICV%#VYiInhrshHu`bW7F!jTV^ z7Gi~4LwvE<`f8VpZx0ae79|I(A4*L=j*;=TvH!Cz8K%}Mk3cl6DG8q)bb3$N7}_MJ zoUYJzT-M9iKEklj?BeyP=z@e)smO+^qdx+9id`JFRFIcRf^8()@68VaB2dWUu3&YM5-Y&o2|=U3|)5WhXfyw$w|S zyPM`|N?>EUyd8vRg1w0uHn=SL%ky%G{quGF!r-%1Qo?Qne!=1eOoA>3?UvdI0cRln za|Z0aPtnf8u0!+4S&2IG8VBL3$XE$2ud0J-YE%5E$iuWK#i-O1Ik*ydS`~)ei-bLS zv?D$^lJwqp$!w+0VEeWrC5#u|v{N@zf6sGDj+rhL+5=c@9Cenj1Xy-tZeo4>;v!&# z)=X$o#~I>tjI-niGM83E#*!W9Ro{j#n`7s9E)(2H#? zPsFFj@zD?5pLV65jj5w@yq$5A+`L%~kZZZr*dowX1*meVC9{mD1#(m|GFkyf8FlOY znJl%{vhgTMz!Nz*O-Uf;dkdOio-70~QE5&x8LO_z#qN66crUd7s7-)mJ6e3PQv=GsfXk&UKVy(S#T2;fs4A zMv|EtJ83dZ)uVr1YMj(8QS0t#MV3&VY8^D)B&{>1#7@EQn2d` z-;A#{KF-^sdPq!40@P(w*fCOt#}x;rpjjA=nVdkl`{@N^*AR~A-=~O$vd)UQU@88v z#SNeCO@yX%Q#2aTkNj{Oj^P=K+>6_~=JssR9T}FpO1?G4;k}k+nPq{D$I{)w3|wMK z7L@=>z^vWXD;J5$S#Y3}vWLM)Hq^H}8kL1=U%MW#RwOx~A$*S!T!zf9y|wBeXZs}G z7ul>DS4e7`Wmu-p8KBdvCuI;w>oJujyg6if$kwiim4IR@(BtJrv1VU|Lu#z~P%TIe zC@iu2hEVgk>ahliC7a75ANlhJQI0#V=T$Ae`^LwXuZi#$DKUsN$(b3I2Gh`|Y*5c*0KdP0UG?y<(QQ~*I8-iWf)=cWD?V74=pbbRq~GdU1xL;Sq)x%G%n zYUM}IUn9R{U5&zSvP8BAVyYxW&zsAX#>B3dhhDU(cKFU5OQGjZ;4s z0Pri93iqNMae&oI(5ZtSwL z3dRXU?45hTMajk%&I^NK=NZnFFu$g_sW`|ZU#Mb1Z(pKSD2*s>J|LQY{2gyv1oU?; zOPB!z(4yq){O(NCWsGHUTb_ODyhNM!t-2?PlElyt_H4h2VH8AI2)vfhZP49-GZHYB zHhyK7I_1qFBg?MkR7Yt}zo}u)(B*XgG|6->;W0U7D<#JM`zi6K_ITw@(GgyLS5k|3G5|0riPk9%vl-%&?i<_hL#PeD8_hF=%%D) z{m&zjMM)LM_!jW|%rzFcu1EE!ljEagXh$9Z((JMuG}&%onqsjbSAYO738^FF;t;nu zXLFW1Z`>}+eGXHeGi1s{ACb7nzO<_2Z zY)y>+NcDY?r+xm0~{HxK$!9xVZ2JoMn3- zWq+=A!cVY`<3aAdi%|yr=Nv<&kwnY-h20Kg z)V@8}C^$!>m7}zqY_U>zCu69zaSARQDJtc0qsi_6SquoedSw9u*?FsIM{>=-^DBZ> zwdnNGeNT4H4|o6I@}gpdmpumO?Ex8Rrwvh4+zsbWY>q&mK(Bi-ThLom4RA$c0C^Np zDO{%SkVA8O2%66G>t#-jl}w4l%_0hKtDIoT4V%}oxU|!Q5BL{_G^X#MNuUWcWI;pu zmxFC<5_TrryU;LP(Hs$4Zer}%^-*ePoMIT79%M%9j+eflm>JDkAV4x3^jrqN-l#uZ;sy<~#pDhia;@)rg@#pbU9sB;R zNDjQY^FEMiwgG;86&J}7J&)9_u7OPQn*CML#53hQh|yrAAfV{*Tv0`5aCehybSV*t z;aualbASN0tc|6}%Q_c5ZVc8jnaUXyEEY6=P{^}+2z$XsH+&>juxmcNgUGel57v|E znTK-k)0XihJqI$CcHy?1`a#yeF*{S}(IeqTC;7bP*ZJvKUliqh&N&69Y|?S)GS3vF z^auo4x=p}DnJl$QoZ%>|oW=lKZ#2Z-u(4FlOr`cG=OQv9W^S-pj@2BQPNE&W*U@?t zn1h8aOg52x_8H&-4PDUd8Obd|b)w`fTpx;bZx?S4cX7OSfq{AOVxROkM1*HDef&~8 zr5OJznmHDJbooynt#`;NC6)^esCw*sQBtHb$m^lql~xTU`7ian%^}ObQJRbr2vDB> z5loY8f5GmzOx!HRoU{;X3GgMS;i*}hPE&GMyCL0P*mW#Q|K)P$SLF^JP0Mh}EgSj8 z3q?#1SBfpgQNX%M2#tNLgSf*X+HggxRy41phFc z&R9yc@mqA9&m;H6I36}QGkQI&&SJgfx#R2N3I!)=;QN!4z;8mWLs*yhx;+1tgSYiT z5Uvbg{1i!bciu2iY{9tATR zThpC*f$GRSA1#K>UbE7b@4rVh&boE7fB{6i=O4`^L_8at#soqiS&bado^&BQI$sBn z)iPED)a14aI(M3n^Dhaa6{l$oRFI?uJ^)#+5??OSG~LOxdM?e;Z$O;lPi-TVWeT37aMJevW7zYH$^yH4&0()hB5qbWJ;U z^vLe&6<}yyM00Y^Le#(6qy27za8bu;qvw+dJ{=mteZP~^cuY(#p)Nj%<03umtw)f& z?w>-+^?^^>d~#T1_#lhTGw=RIs$J{b8Bj@Mm!?XZ20tmF4mBQkbC`dK0B2 zFKDfvU->y~O*ZAU1DL3+;YNPje_7(%{kk%1k1wTKWZ%~Rfh1VXPH}PwO`<eU2MRRIErmO`L}l&HWQ*Bg^dX zvLJ3MggI|An74B~7ku}G%!h%LDji5c8?V2>e7AN|nwv9lj*Ovd0n$9SE@rR~l~v`O zE$P52&iQTUicj1&aV-MLd4iL3*lJ6s5kv%(oe#KW*Ze&fvHnO9Rk!WapXKw^y(wyMqRj(PF2%b{4lPkP0OD>A|H;Z;3_ z?&V7*Mv#mA<4A67h-<~3T(QU3(ir-6NVIM}Ht8`{+MI+RIki>RD~9W7;2Px%c3+|_ z`KGsf<~~7_-!lZg9eBk<nI2=EhA($&}1gQVqQ2)y=l!u z@0N^tV$=^i#NzIrOxCk9briM~Hd@1JI*>!a&F}(N>;(;a<0EYR#r?xTp@c6puVtn4 z2@pdv&l&1$ttdv^h+>NKZCkfYH-;!0%uF+vCQ|S0*v)aTP-t^@n+!Z+V7cLt5|Nl& zG*L?9JteF;dHJ&lwl67`+@l6eomwijJq2rFKb(1HfH>LbjKQVE82lm)K}9yNgpNE@*P80}xm2{P zL*T{e!6rTVGH{1i=6WdRJOpLJh?4SNNA^c1&FAEAbP+Z_XaKRYj{qfZ$FnDHs71-T zrRXIe4cqC)?WP(;cJ~dc7BnmJ{WJ;l5DXTSMWZ626n# zTvVFN8C7&RwNW<)TC9YpMOlKzv*`7Ts~ur)>XCWJI)W5#mlWq$YadaWicZE4SC>B~ zY1G%Y*Z=OZu+;i$U4sZ4eF)VFZOmpF39c0-uTf!deTgKdN5%8i!Q!Wdjg#KFsHOv zF!-JhTqmL?5+w2P$ud^^fdD>EvBcW0V$iEGRh)9hSZuWm#K$eDVy#3)r@dnlpKN4V z@PEmTP6ih?E*lCcZmD*0jkahrp zY>uftly@qao=wa*9O%}Zm8~{9pR%tfSs0hNS_Z|l!wG z8=G*Q95KgV8R}i-J76-&6E5qIkTx~X`JTyg7ibNbVQ~@u^F&i)8~{iV@6OX?LIrdg z>ja;V$K;|5+kc4zEY|GnyK33m;9H0}o>~(S#$4_N_EM<s z)HfB++U%CM?fvsDEy&UREEbjoW4^6nH5*~SWwRdne`m{2`C=?EmNb=^e~%a#$er9V zHV;}H^KFSFLn4CDS_x0;d;8)n5HIJdn((FOd)F8k6a--DrIV8-sc;IK{o6#?$&)qH zfjpTnr^Z_T1ujm{d*+B9^U8+Cb`+ALjh#v{Z&w@!WUlJp4eWxL&_2Q*Zfkrz^A}vJ z91l6;XxGH0CijIb3va*730*47aWq|04|}14j9jiaLwwTH^~!j@i8P-JQ}Gzp zGaB_vRD?wvN6O7CjLrp@Q?CIu0fa`+01JxscrU4qKYvf_8QY>~us|x}#Dmti2SW@Z zkJwspti+)Bo`snFSU)TW_LteTH6PAlCpx6Dx03NP-y!OtMoHXoQCBtpiMk^)y*JZM zLiuf+m6kA-H*!cd^P_<<2~Kk5d{H*;@KuRTu_!jq0y!8p76nm?I+7oL=e^3<(%!`D7l&FZfZAi!-zFGbVbAnQCBC@5wf8s3BOD?aV z#dR~VF;L>WaG@q-T!Nhu*Mo@^nffvJ`xT6bhD(Hf3cHx`#lg3r$BMvx!dKz&-OAg5 z=Zp|xIsQbE$ZNAaT;=`||NUM1jEd|2AP0>R;wY_G|Bd&FNvK_O(WC^suiDuE#{0lQU(@}Mh&H*e+-5~MurYDNuKNorWOL!66bYZ| z0@baOTVNb=1H6X8%$MIT>1bywSR`1{F;M9U8}W!Folm6tRfvpWai4!PT^nEsOD#n3 zQ#KhCPJ3@SvWMA$7}>I`X^F!^t#|n^?0{^}(|}|I@JwVHe47D0I}|Y0!|6?tokw2} z-$C*=RcV8ljx2i?ybho|VRF5H4KAnEX=kJ2l}1q{`^em*hj7+~(<3XvVrYIS4mEA`KI09A4R3dI;lmTulGw`tIC81!hL85*@~ z1wXxy#inM{5gd6!fWUZ;jIWKd^V4F zO3}fVF)e!>IZJO92e3;rq!ZfZqp!l>q>Z*{!NL!PVc?`R8dLnebh#8<;jWpSp}UW_ z(tYx&7A)IVq*?w%Kj|>86h8cdo0M{R{aU&py#hWic zFM@24DPkWybz>UW^80@O&LWcvmW5Ul1epUX5hb})h9FUj;AU!QyIS~YlvS8<-#v#7 z&Ag!bK>rjEbJ|B@6<;=RI}2un-80?ay-oxz4J3BnqPGv=r2eB{BqSwcy}V0Jf#Tmt zNt;dGojnZRBLNqA2l1}zNwpijO7&$$KIBMB=`+$QO?>&)))(kS*`uwIYh7NIfK)!tK#o!-kuEM#m{$+CUY##A_s+6tHeq?(( zpO*T9fz|26+^gE2!k}o|Ep1(kBcD|)L?>vQ{WrUlK;joukye76phd3?t^@*~_o`KX zKVunsVg{2<&1T>#UgNkv4FY%LM+MJZGlYkm2R8qvTWOHzrNlJv$N7OJQGSQXHmGiu z-X29CmD0q0>@64-1CeL%%NlqqjZ{p2=k!bv;_vfTb> z&{qYu>V{Uoq1V^&1nU^xo>@-;s<_@WClC<6(1qvhQ*%VMF;t_$aET>C#VjT6b2;Yn z$H8;)Lb0*;jZNH&GFm5HL}Ku$x|X;2zphu zD&ClN{BV#`)N{^m?~um*b~NQyOVU~?Z4PlwQ(i1+0E~}H6yj-lxD`#jlC5#Oi3f;mL=FtLVt9eKCsN$g>n|Y0koOam=EEM62rrc4E>&&UVS5 zHG37Fush^oj9DVbH{Zs2Y`-fx+{j_GPpRI$GE?_#s^Ma_&&|g!s7w?6FXSPsd;$G} zg7h_6*fPc7xrDLc(O+3JTKvKlrYy0p_KY>AJG%_(izCL(1+Nr`=}V;V`Ps;flZCx=!UlmIhc`+?znN9K-A| zZi16IS{0byzJTfwySD&tAN>D#Sgv_|GN`=~{ z@b-%|oe=IN2SDcE5O%&Oa32WGt=}4kf~SkT+Ry4>vV6=D<*E93m)u_z(Ey!)3ZW&h z6nYnq`Nh4DS8Bh6-Ekw7zc_z`9lAO#nL+FVP@CHlJ%bHD+m`sX_03M893q^+4Xz_< z&c$F53M&rwmnvrs#3w(1Kxd9GWNX@Kv>q;hzR~B1w>;6~p0pYQSa__FDmYP% z08YXJ?_X5~W1*SU%6R%de(|h@G-+7-8!=*pJo-t#?K{~ITtr_QnP5>3knq&`OOxGn zbRT?cn(m=ymMymjkqm zm8Qp~kcTk8U+~r|)KPJN`ZI!x^NoEwww6iY)xQO!0mS zdQmDZrJtTya9ftM49oiVY94hWQiXXP-l7sGVt{bw%KH+!Z(myj^Z^%R0jn`D z5?vlD%Zf)mPUoV7hmO2m`@3D0(@0v#{g)4((z@NEq@MJkr|;5)=$3*m}RYZ+(IrPCJQ zL#q@PhaPud9tlHbUbLv`Lk=&0H|IDph43Ij9S7yjaoU)ohPS4`~ZZPl5GFl zNHbH9RJf=PWaSCFh^*I?;TCzbTJ=B;Lj|t%%}86)>yRglMKD)w zo6YEOot#}Qx+*A$@IMH!pPOb#v{Zs=;m&30Ks^@n{77sOo@n0esEo$@H zY{+t4pXB$;CGj4Z;Tx9*0UYvXi;&@(WVJVRnbkVdZbm|%ZMD$`cH5_`6HEQD=T(J( zX|2J|1HrE#iRA?^VhPYc-0w_Gbaji3SZ<#htFg$^-EcTw5iR_Y*bZPe$Lc#pt#lC& z{Z_8W-Y;Vyz_aIe2wh1*@THA30)yQ^iR#4MBged^>N3$y6i1)VVYiYVXIMh6S9g_< zGX#Ag9(@JD0pe;ikH0=Fp-SOxTisF3sOyA^8S{N)??$7D7OoSkc)M~|-i#T^VJ_d? zhqM^Y@?9SDB9$P@+;!6S((e$s8oB|{y33ashN=wG>BC*cC}zi(q;&@RwHD^(!9fKa z_)kh;u4?d8U+&t}$??lm3_`2X76ij98t?(s3I^xuus-$yhmD#?iurl&!3PuhTLWw% z;}_OW&&`6Pnm}9Qwme(mQZaC$2~#Bk1JWT>DKYS(`EdW-FyUF8)rHxH(xh1` zsJ?iVQ%kZ?KmZ6&L?XcX`vVpx$bi3VR;LY-Fg3iC9)2(_2c>|rH>nM`%@VmcVoFuxxM~ogCkeS9RXvf`d zs=6b~>+n*Kj_#&;u#%fkQTEfxICDOlZHUA#E{0_d;E=%b&3ZQ!iUQ@s5)^ozC*=O^ zviSP61(OcYQg?W#l|>Pt@XEP;Z++2@vJ0)l;x$TEQTGmvf3e{zeJs$`azepI3J*k! zFQ#?4apYl4e0jCQIE+fI2UPw^`t!_)Wpe@3a}AM0hNhIQ5?Vs!?J?auw&YmQ;9Cwr z=gWK;6)vtP9nW@45(Y1-nzT%lYSmZv0a}FEu-b7KSg*k#rB?40idU7`0H3q{l29!$qlit z2X7>kl`5P2v4eip!@~WE$0?0*Z!<)<=+56}{z&nTQ@8Y{B!BeDoq^_fsL9YsP@bBj z=J^5O`Vy8=SfrOCqiHTGSV{S4DskSg2+{l=Z^Y6%Qd8`&QaRpQ9eQARtgAg=cV$p0 zJT()zG=Md!M6;P!y~?lgTT6_AL!kt03WFie-x5y@O2oBC1R`RUU2sw#!qplRahG99 z&C&ulAio&3^U=$&U-wqvq*5!2hx`v)=hQ3=52V{|+qP}nwr$(CZQHhOTkp1Q8}rqv znyPa#m-zunI_am^x;Ph9aCjr|#)9{uarSiwmz_OXi!_1jJe_ePNoK-&dQzEJi57I+ zPun*ne6$$bZ+z4Gz)JqoV_$tWRC%WpQrW(Q3~~|bzvy1pe!QNwfQ@FPsN5ylK;14X zwI8+8C?@6$Kxx0D!mF2u;Ot|sGQ>04pmSYuT^koJjlas{e3W}D_sVk(U3)RTEbrmL zSZD}p8py1)wEM=RDl}2(y~MyBYu9fqMW`;JN6@^QVAcj)?rz+_W*3m4WFN0@50iPK z(_R^H9zREtniJYN5`6v*T1lfdbSR0NSun&n1Na-RzvmbLV&iR6ZlGG^#>;VZCqX#G zY_XbElz9dxEH{>EDNjTyN{U=tk@Nw~-j-63-ufj&d6~cGcHE#~G^Qr$rW2V8x~gkmaiVIS(7wy@FbUA{=RF97WV? zs*xZ8b&;);^ocDo2dYir*{`tSCCD6?0~08gyT~*t_@oQK#bHTG&Q|B{MD;dZ&NuV+ z9sZ?`nxiEJmTNcrgj#yn+|Vd;D4dI_)|`Pajba4V-d5{)1mo?3s$Mw3J!*ZW9AM(% zx&A8u*2e%s*Kuu8HE%86fFbTx|FDt4CjSrOZb6*BE!?MyMTV61nfnmkr%XgA^3OXd z9>w9(q2yc{UH)RZrs~3PN$y`^qC&YO8HNxUA9HXXMWgJ(=KWG=6mccQ3`2EA6e`qb z=t>TdT+D7PuDI)nCI^2BuXi1yL}vdVZP$|U;dphnd3$l*UzBc9F~A;_(Sy1Q{)PCMevN zw&to-QY#PK7c1v)Wt&C(u2dsxOj{|zD7RHgvzgBwbk&K?%jtKk1Zm~ zi``;s_8N@;${Z&Wss6_;Rp4$g+1}G*Fpx?}x+$G8_8t_-lQuNDZH>D=-e8_jeZhzH~696@oQmR?T0G0=r{(vqSH$pzTy!NC4hRH6$6@W z4lq!G_y1mUkkY`bS1%7bC+@i~JU0Ul)q` z5cu2|d;OG9A4FC)q??_QJ6lk-+gG_*G7PVH(d@b;3z2sxe?-hdcN5ZSKF2uvkc(Z) zzohTN|5H(eEOJ3jVRL30>O?>ym6vh$ymlh24X3Ojm%lsZfnG;V$%xY6<&>=2I0cFA zY648sU;nk_ps#(?3>)e1Nt1$8>9ub=wc~lHK&;4>=wyzmi2Gx zZMlG`OtspW3Y4MV3UV%df9cN7b#RB-hks?-j6ve5q|empb@21~?w0)Nisn*?P!V4D z^A+&$$_(`YpJ#bg0(rozWxSeyjZMb4I+C^-P?2hLw#M3PgwlF;pv0ApyN)tr=*LxN zB0-@V1js}#SUj)D6q8zT{plQA+?1leyq^)cEX#6s_=g9GLFT=kO>G2iGKh&nD*D`n zt*UrT$th_D7#&TU^oxe)m%B6&MIGC<2WJilb~G{$9&p!ueq$`+1Okt zPp7|4z(Y;IW^$BR`LcODyXtOrLs-NTfaC{x_H-G!z%=}nDc@%JPv4mGM#qBG*R@2| zfl5xTxR0zM$V8b#35jh5`uGTd)InfGSr?Pd9hMd7D_y^z?FJ0 z(%P|OQdI}1ji2lxjR+-TF(IM$5t2@-Z%%_ANDp=aohjG7BN3v(NX5Rp~0ouk`0ZDCQj(Gw*dD}yEp${ zc*1{8JZC~hAj{&G(lFWVEVXl_lf=)4kP8lgc&1F;Ij7g5SGQlLUvJoEig1N~qypSB ziGlT12C}Sq&YQtU;kL3Zaxc}7Q2kkoo8YPZhK z8O`oGE*Ne%yO2@LVykOiU9+3_0`Q5^%zMRm4E&;rY^ zqH3_1IkZqE%4EYeD(azuO&R_S@3P+Mwer>%k@C;$2S&Gh|H>m4V5?l#a~#Q*_d-mtJKQl4)cv0bkV2X7d#rmgSdZ8_E7| z2Sq1px7)FIwlwHkW*``sQQ%N~KwXS9%A(8mC|2K1xh&zdR#8Wvv`I>6yXM2dgA7LP zmJQ6?jJJ$DT`gAKI@`$Oups5Lg!wCmH;e4fwmB_rbQX>)t0IspE88zx1W9G#-8t5zvafBF#vlu|{E=i68Eq(;Zv+E!Quoqg#ub zYjkd-2%%^%nE*r7L%&C~@9VhplKCZ>yX(+e`&-;$q}Pq7PKIowdI={oD%#cHA5X2N zA5cASE*$myQnY@M80-kr(!~7`=YxSgm1nF`cBzbfB z=ij3i&lq!P_MBre@uX>)sW7k6payb3u>FicE79{DU@mHXxA57m6TSqkeK6^<>jhJb z@Im+y_AQcjjX1q1t^b8xEh0SP109Vm_Hh##mI2|X33wGYmf}P(BlseZ-vylj@0Vu3 z>ubQ=tXpd@@ok2IO92vp>y>3nDNfeoenQP6`PG_mv^NS>GObJt4Q$|$!*^6(CVIhf zh1QL;qF%!$mz$!9l_PCRlkWSj9E3VKX0HLv zT~%pCnN2K;qUG?L{#7_*!ueqdcB-Rxg#9 z-GqnpusaoBD>hc3C4bLd=Y8$YBiD!@vQvvHt=THcHaAr)cgv+@lO=#m!fRmouK4VZ zi2i4~;Vfu=Vr>0~3Cot5#7eh1P_`?47TPl6jN>>Sa_YzZ>qv`1Z{=6%%;hTC(=N5| z%H;cO_L3NAqUsa^e~I(u@tRLBgJe`GAID2I4aLEAPmH4ia$pfb=M{??9Lo($$A4B( zEPaXTw;9VXrz^lWs z1i#1{(`{GgT^k6-FV5S6P9RmtLA^|CP+H9Y= z{)xb1k$OYSUL$V)r-_BaG=32WzA7?UIhsykkta^M=jgz+Oq zhjoEt1)Od2fY)BugLc1|0(N+J#lBDs4$g;XWrYAaK9a(f~3a)rLi#vbrP&e^WL1&lURXuH1 zKZe)?3d>_Pe~`VZQonJs_bbakba0Ka(;lf%SkBfAA$a^_fj)3%HQ);K2jXycpC3H7 zdc3JC*5FB0Nw&B$Yc5`aKdST7V7(2eb_V^uvKEpx{bV0CwN+3q!hqfbEgJdP(Fj@Z zXowD^$zH+cow31)MrJ%nHVrjw5NEFa`@aZ#+Ycygl4z1 zZvKMF(q5)urT^iNO5u8NKafUXuetHfn;CMR+Pp2fSC>{@s#bXdggfaqXU8b*8W&h& zyWv~hGV<1>@t$Nsd)?j_yp6m>rJRgs;GmjHx>;XMkQwx|lnjSKAPME~j;uhn|I zrfySMZslikdXG!tRf4B0!3ifw2?j5CjHnLKF>ubn#Fy7;-DiX{ip;SP$Y-^7Fl6y< z>C#QQV)S!bjG{iS)q6SglP)ibe;+*t zq*ZEX&5^Ww_h9{EqUpzDsl#=*%RYZYNkZ2SXa)3Vzl_MU%WsINS8&;Mh93-w(B^k@ zC}*9t$?zR81)-h%s&dy6G-TOF9YdcdJ47-Og?E`-Xk^uX5Y{V%_UsDwejv-x-nZGG zU^)e*!TH*?-4;yc;e$Zs`tmyOa@)wdqDn=9FW}$c2+~&uvY9}T0&h}H(HkK+8)F>_$bYEDz>6 z=1*Q=Ue9!w26RuhYB$d~^aVsBK*m+@T+zebT+rB8Q#bY3WU{Qq8E#^seKw z+NQ3GwBlQCp|O;i@L^D7@o>t0Nyy}@cf_pS79&(=&?4dFLcyY zz}tC=#r^FRJj*weL(ZsAp>lNB5{-aF+L$CMS-OxsKHxjIN z+m6lg!&u5+k1SH>>+;())4?zmbk#W3C8` zpdNJulzzFy#P0Z(C?|%D3{2u6yCy~7J94u*Gq}@AhiSX(fi~!ZVwoxP$nkA^8?eWw zl7UmT>8dVGy}P7pRC;(%&NQb1NWYYS$aaQxy;eA^iq`q1S)7#U0exh;T?c7;q5Ht|EFgDzwna&h8{McTo5m+9DTCO@yz8_SeP?y6^u4gr>Uqd)n zuf8I_Zq-68kjVWTqP`N2fy;gk$~i2=CI#ROrr`7dWyi#?2;8NuTI=z&6OfTLi!=8N zZlQgjehzlJY|b7UvPEDl0tcYl$o;pSdV=G85bfKrl-Ay?kLP&Ll&|-OgWT>OuuHwG z6`w^;82WDg?IT}0x6fZku_1c5d6U1Y=0MYoNuzIMP~`V}#_EbkTaGI0F`I5EbD`NxH4 z35=i(zAQv7waA;lGMy$MdModdn|$$b9|%I?^)^gE8*S3D>Gv zHT8nyQjKcpN(ONCbf$Adv)3SeSauT+kj$0RK%2oI!B0XgJr&T~-f&XP^}J~}`)Y^k z&n5fFQ*==MY}(xmtcMWtDZ_^!hroA%J0cAgV9Y5oUNiZ6Jk+JYtQlgPBXCBjQs}s9 zy-?Vz(c})1cAx*Wz+Rp0TTu@g019NLuNG(R=N=StRJEOZphyfL|6(zGX-Dnb}V?*4|h zPU@m`G6m#klLbb>J#M!2pa^3;jZ`w~cS@812HW;b4F!efJZpinUUuJ_HOqF?`M!ve zk~Au7&;L=GJh6R%JVXOX#5$uD)=a8Dt!mhIc<_S(L&NE~#(R+`B5Y*3#9R@Qr+1%u zY;V!Ro0IiCpl+fvrhca>pMwy+w6@yY((MeEkK4WZ*g4hqNk>iS_yziQb-{T&S*lzl z5ru*I{=01=DW2xn9Aw18@xbwMZ5E(GG>!JfR3VRF-4nS=+rV}d8S!L9)LnJaOq)nn`VJ6hbwTvzL z+ae!}`8Q!Mxe=b;`1yfqUagZMf7$YaX#i$%9KAnYny@y3E+hlM=ELrl7d?nOiyAnl zsSVC!vWSL<(X@BCv9@7q#^(8lcQA`$QqKcO(ZJ&8CrXg%ypKW+6J~7wlXMKufX4KE zQ0+Q`Gt|NOInk`;aK#c~&pxymguGjr?nu%)GMY_4*GP$TT%<(KzE z6Ke9G5H_g(?rrHmBWrcix2RblDLQw9fzib#3&*WcgG|pD&QKj;A9bd){(3drQ5R5B z=3E5_L3Tjk?$s4t{+;~d%xnYuBX@O&7jl4L`8;^?(wt7jZTyxVBVP()-K=rKJCGy$ z+IQjm!`(eGnTr{ET4l5xaiIf5h=4>Ju{k4v89WCc5LkM0Xl+z;ON{&vuTTKnz6(0M z5p(W(EIlUu7qxcRVsz>~BO6U?O7YtAyI_iT+}7;H{8A6go`Jqz@S2|htvnC(IJdodGj5Vlo8*@N4m*rqe39D9_bCsd0-UTk; zUA5?Il8=`bW9*e(+g$f-TIn(N;c!1a7m}E?k7PnPhCBrJ@plp=YVG3GBg@RKV@-=Z z#@kB=Ms?RiKU{OZ?9Ns?v|FFb+aIUSV99qPPomkabpRm9WArWWJ$9kHwcgkwLtGqP z{CgKLYC!O@Q&4a0u|)qoC##_&rBjO#ZQ_iC@O65fYbu|>&5!TkKk40aPljirAnHb4 z?C;15h5A`?ZSuMH`hO5jvMjy9Bw!Ui9HVX>ip7MS!wNW@m0d~Ewyv&&I&mXJI_X^L z8c15{xcFI-=iv>N#o4gfGTq z_1EtgE+Ve{gd<2~eBz1~5rIFtdOlq)&{k3^f}W#|@#(;8K6ovv0Ua>XnCxN+Ov9!T zD-$gR$CKx)f`n7(ogQvq;F?}vVcrRtJ>;iW9d)}n)A_MiOSfHnY7enx%Y(Pj{7tV z%~?rcI4C>0Y@L666{(f<|FI^LA2XEb?7!$Y29!IH_5BNNbn?2QqTdj47c~ zfRuQ!+&Yzo1sr9tL)0aYLcM0=t(&CvF-r+A|6PYQcvSGM#;tXE8ocKaw%FcvFiAS< zv4qISy;)6@p^nepXd?AtpKP_*b8I@U&r#J+}_ZZ$XoZ;nWGK)w{rj*Pp&uDGY z4T@lQN(J*T89CXjA()QSwZ2PAFTabld#Z5cAty9-jjwv?6(~l(Xu;xmHMl+}z_3S} zmio|*O74u1?5Psm$gejzG=dRSdNk=U+FbtY|liR9wOJX zwcaXwDF4mOSyUV{l{%$@Y@C6{skl3}^~Pjq^f%dR0vmMM_Spgta{e&imDd z5)r?>&xN-&(-bC3jxRQp(Woavz*W90K~A1by4f%RjFj^VXjV;_Qj-(oRF9P)j8{4( zQ5&n)UiQr^Qv5TlQpGv`8^{>_IDi&!tghYkqSOMse{kGIUi_2G-;-q7tlT~XOmNR8 z>lwTrW}#DQrPwqR+{a;Oz;N=M&cXH>WfAb-+QdVw*Nt-7%JhNjTX~kbV(#);A_{dT z0j6MqtQpOnG%k+Gbm0ZgPT7I0k@0klLmTJIacg-YPXc9=t|qRbY%Nz3Z;ba$-y%xY z50Jy;U85gAZrfdVz(K%D=`Y_rsyqN;=*}sa_x_f2gz4dk?5^&iV(SlBrCpASge_BW z6VT$#sD^gzcduMn;F=w#+w_5DgTagrb;~v+1d1gQSHXY%qz7?Bmmy^GhuJAb zOW2{Dn0}JT_<5qvMK)EUOdN3eOo;D($oQ{Qr@xSHY@K3jNZ0ZgHpd&-5Ja#Z)D}4Y zkwR5GEN0b)AhH46_FoSCdQ;i9WYH(o6(HEsbOal9urSDcY+~*}TE>{tcD^{7csr{r zw=x?^SFO0MIdf`4%<}ml@cN@{#rBtj_AeZO;@#%Mu4_hzNQpea^Td zp)L{SlW_~>pE6ar*V8(;Cr7!CktgrsC_uV;pZ!L%ewIPbR8csEgNTx?z-MEbXpW#1 zuQ_Q|=lT{unZPcaJbT;vO|x3tv$DuMHbF+sb29cXi2&7pKtcTX_?uc4JOtF~R)E-6 zj=JBrMmekg@irrjXQ;k%>CM5p$HE2Ov5+cH&5=*%72g<~SEfNbL)co#}K$ zc5pU*rfw*i!5P0D`FlZJap^$qEHt6C&;(Xv^7A>L@&vMquoksK8$93mbY2T>Va>e- z)(9fLo(vvoM(Aa-E&NrDJQb-K}xukE!{WWuxuYNLz2$ z_;1u7zfcg<^pKBjxu0atyoAydl&6>;J>k~tZG_J$DWZjB?E@c$n_*?VbS&yT2*3DC zi5PiJzQ;;uo)PO*g@P8>=W*|A#++80;uu*i>KkfQI5?_6ep6-@d<6Aky4c8R;CSU9 znOZ8;d&Jle3fba9J#oTS$;QO);O>3-D z+A_Sl{Vs0#N4G>RD3ZA!nJ{BaV}?yqS%|4)E3myPr!5}fv}cw=#MxBzN0$%7kJG+q zKfh?oM}sZ5W2?4Kv%5wD+N+~xOb0?jv)DbXUdmfxNtcxEoD37+mpchWqBhNp)DW;1 zQ2r0s3j6=zT47{n=J;Q^3KIbv8w>0IuKs`33Og$s^Zzrg5J1t3S=zXmIuX!|*%-Q* zikKSPo0vlJ@j*GeIGGySLV0Y)x`3*r*#4s<+$ks-C~$$prIK)miwHR!fMI}{8Tc>K zPq-r?DIw)9T}&V$?Oq-tAr=b$bhXK z1GB$Z4`4@)7tqksLh^_29?%1f0@@5j46tbsL%VWtDn{MF3lP#0B*=LC2Qm1w@E;8_ z1rf=~)fE+ly8twi0$UT$FMt}x7@8rVsE`7k0_48iBmiz5@YCHgXaom9SR3P$9vH$h z))BP0AOL#+&kR(wz+NW@5rG2A!I>MNqz-h!HH7Gw$ofZgAN+dr z=3yMu#x&H$0fczl5CS`Zbp{V=L3I&nq{HwX&;aC^9nmIUZ1+7_cYwjW;RNf!$V3OaLVj4o(3CO!k|bpA-Yi%5~Y@y|6zV zTAV-^G4GE{a~J{PYR^<42RAeZfq~q-qHAd{2Rq`OKLZv87y&#AN?IBcxIhk|B0IHq z;SL&R2e;5a#(zH>tA`giF%IA=o2g)z!TNj_zmXj48VJ-tVNSt*{*UeZNrZ+5CSah3 z0YJI}7Dn(p|D2s+<+J{2_FIvGJpl7w%x?%_|8B2;FK>qeCP-wx`!D?0d_+i0$_q*! z_5GLbV^0hev;=!V8H#{_%#m1KQPD8qKDG{U_YbBRJnScT%)hhhzx))yWPeky<#Ioh z+lMyLw*MVC{C<9;O9J~)C{V{=v9ogl$HL_);rCzdD?i;Id*WZ*sPT*Kmq|! zWTzt_2bXaV;F<)=!(F0;Bw$F5O`+QT-aWpE5(1&94i-E&PY^_js?`9L37) z>o?|&tkNIXpQb9s3aphV4{uVl6N;ruaG1K+zvit)}nY<$2OOV#K{P!2(0 zInS&=qAOMY!c(G?ooeRS@dM0N>Y1D{te}H&j^A?WKlx~l;DZ;k!%F%VUEw>o<7$3J zgy&=6@aQXE)TnB%(!ua5jvaSqFUaspfAmiLP(IYIdMskfDqbw zrz*Y%|177io07O-6oxEGJXU_lU8EeT&7}s@!Ns>gIk<@T5s{~fjLQ<|Tp%NE7%iQV zUHFocYbJ2qb06;Hthjt1*)}w)#Fw`KBc2^KcG@odg)xx<@eF&1C4?C3t>5EZI#Twr znFIwgvCBVu`y^6%tU2u}LuDL7PmI*26ip*uxy&dn{3TWDJ;cMt-v1%itKD<9!Tbz> zH21mlUI50-mrvH{M^dZhCr7^{?_R|hUwL_NjbfmBj>daX6Ib^9c<~f_gAzzC#Lk2mXm7% zAg&l35R1Z>V5jdj3*%8!&rY8$`p))mWp9td7A^Im8E_QyNo>4#j4^B!{X@5Y6jswr zca8&(HoWJg-0c9kvIKn;Nz!&Y;|opQka3p~y7e?=K5Ly_%--9n6xLQ~)K*e&e>9qOY#C9z97Xz4Pwc(0d9P9N$)-Wr3<`-8%lc}}=wfjnS#8k} z=!?=>s%k|!pgTqTeGH#s-1K%6$0KLjBP#-Uji)j;Sop;t-mxKTQ9Mhf?`t{TV z=j9IBNiEQuMIw4L8TOp#KPDx1Z_oD;8;9dMOdfB^yq0768-rf}Yv#vXf2Oe3Q&$)A zMH)3?mp@dIOrhu2i1)Sq`SgvrWcs4ENkn?9NZ0?vmaL$MJF|yQ7 z=By<&+Zk{JM1<4=#JYNG->yD7FM|~9d!NkIXbFa;!!N-o>wTN0nmgK}D3_y{$&Q8O z-_=!w8f21veP#&7@msVX$ZD&n5(4%9xPSiYMU>902-~rQvHUHd%0sE)`ea~yuCy&G z(+mwuRU%E~%yYPmM`S}C#P={8TAy|?q7jb_sUr1UqNG;xBf+)4u*ei{Z^;}}1%Q}M zzd#zAFdnsIS#hF&Mc%Q~b{>zve~Duve75b-!1s!ktNqTLB1DYAV>wTsd$cJ+LaLnS)lGJ^sT_X@nLPC64%%vq?CAck z=!l*(v(A?uG*3&5WWueN!N;)%&m_@JME!{=8E*Xcn^15`N30sEvSpAFIfK`k!k2~R zs_7}_L&50Gpt{{`S@rjD6Qw1G|1CnV@m<071XzC*cd2r@;iUX*h(^TO$s?VTub=Ad z_UCaN2c;(Xyfc^i0o2pW=1v5yi;Tti(?k{aL>BXfb7s{RK=ZjUb2o7v{hb8fZCthJ z=knjTOQc&c06yK%e13$Y1P~x|1(+tHR!?(j>-U(@jW2R|J&E+Cv|Z`jSTZ%&4}Yo; zZAe5Be)Eo6fj3Cg=zQOsXDhBS;(vIoy@4C9h6rsAx8sCB``@|KPg}&zvq> zK2yXN_R!SUL!rBq)!JxPJDhZ&<&rll-NJNR%xB*mpRq8wJsy>mu_m3OHXl3)l&Enl zIs%ejvk1uP9`EMV;wNH{9!+6y7xCJ&7RNikHv(znfgMd^<@w*iuZ-Pg7bT4EnMOXp zyESamu-hUAk2xyMh>D?z0 zFuJcGOJ`msxu=l3L<(3J8gv0wlTdVt7??|3e#dnC@>ow2YvOeUHy_ey$oVr=iWnva zt6E$X>pg{LSVNxru<~X@L{->xuwAE4Z|zwB|E6Ivt3M{0Th)qIXbLHVg1DkL1i&WAb2`FN<^exPE@ z1?GK$!BYyaPq30umP`yw==G{gGP19n3dLJz_?NyE8$7Gz#`j0ewx+~!g$HYCqM-X? z7PEFWD1K}1@(tuh#9`b<>j4sX)H+OvB*w&$zjo%(BiW-!|GdjE_th$jCIOUv&oZjxWx?DnliI+6L*t7M{8N%LBnIJ z#BbK)FMHBm`%bWHhJv>~31*O)DVG{DKBNKTCiKsqcBmN24c(&6Ve85j6M0_PpX-%%(w7$-zv-^=%0!;4Y2XA z4z8VXhlqI%uFjp%y0^MEg*m&IN1C4BUI!Q3g7yoN&ndl4Gk#62jZYJ}F+RLB7U`@c zyU5J63It30@!QUeaJSXOb=*&nv@X92Z5C!XsC1f1I}|5&yFNp+e8?x?H&QEhmm{n3 zk*WUGd@-&-5mVipN7G^5UW@7+1br;XMf~*pMp3sJl}q6#r4FQC!F4 z{9+H3*rBo1tzA31^wc#tw*1)Kc7*ScqwK;5W<{ON%C?v6&n+$9D>0@un>#W= z%?gZoAMl;s-`l2nF{>QGb(9s70@RRj5Jl?QqjoK61OHv7`@ir9fIp&q*V~Q=wTD>) z-VGB>)L9)ghR!iv_M|2R8jq!wGIb_cK2v>v_*{Fr!Ssa5pGQ9azwK4QEHBm4!U)zV zpR}*51EIo)ZlsU=5-3lG^&bYSZ*;B)jYm zVuY@iAE6lEFWnmv!Y6|j;#UjIdeGP`Dg=!40&V3-QU4>+rL$Xkk@_p88YM;?+K2I@ zH#W+9nbl>s(eO0ElpVSmUj;OuC!LR0=cP+g9_P4s{U^A4WE$RS68)|AmialKbb|Tn z;Tev$k$&?-5gTYH%ihVW`ulq!rbA!e-VOk;s-O>M&i|;s{HKAhnQ=Quj=>Pa=W;o2 zL+c>2oYBh%pLTK1fsgvJ3miNsVD|~&EsDw520y@2Na5}n->3%jN#OyIA%8cS z23MAu3(X7pQ&^@Y!?opdfgn|p%;i(0dD!j~5fbzjWGR`^s`ybB%=OLn`dld*2|ld9 z28CuTKbi@K6s>)`6w};H*$GWNS}bGpa2Vx#O?PGES>kgHs=)?HBU`5F=ogvu=Nj5k zt;A}5%N9$XR18>uH<+_4f|}Ngyy5FMEZ*;Z9bk@ zthKI=_-*vsS3^e*;~CcxYqL_rv@>Ev9;D^oypnA_1&HJ$vB8U#f%$Bkw-zE}U<6V~ z`OXd`6MPW0(Y`;8DWgR)AkzNaOU5ii8>9^Ygyr{@(dppb3L@0?2>$O=Ey69RZt-MO zkpnY8GmLs9K04VSuQ$yXH8&t^1a&GX`f^@foyXk1T|sv}Ga*YNjJWI7)Q5PmtwAJX zvd*8Y!Hm`0a8Q)B~-g7vOg>K6ojT&n8a8 zXVP8>CR(vOG#-tujc6K?p0`n@!w_*6#d2Ih5vPDKS_suStgYy8xT-;!^C3hGm1 z^`GF^vZ5ZD_~q{;kUIxdk5wFcJ^oT<%wYqWI}r&%+KUG<|8{=7ggtZafQDlI6Zi0hSj&)cG zV$mWxMBI0VHS$ziR>~iZ-c$-X*Sz9eFmP5tI_s4%{CCyT47@skjbZJd%{WbD)pj;XpATvLXKpVrzI+pOFAJIbiSw8^Td>1?{E-#?|n!_EV~xtj+Re>L46t#)PNMLR8{V)|aLb8zD=fXtR}4 z#%~NYJ#H;k&f{Lnu93&PIULRv-JIRht}XqZxNO|DyKKs4L4Z9+AqG$2)EOI?d8Tv zed;y+kX#7;HaQ%Hr@IQHV&2bD>g=I%qF#JK8aeMy_*A!3Z1RE6H!R9(Ls^;6yK8UbsSpjd z<6eS)D?To7mQ4A}QNVJC9zXG*I&=c5rLI6fSdKV2W9L^TQTgJ$A`|a;u?0EV(3wpW z#@OAbdliV}ZXAgtKR)+N)IzFiS1-jJwJx^H4;lI7KAb~)xHhE+};!*jOOQsa5@;jEaD(&NQ8`rjGK+jIH7 zs@46hGXj5gnQ*S7jL`$kG-a*hL}!0nYgP|}wQkJ$)yO>LJhJm^l=f&HSSEBo2OtIz@)uY_N7NwL7G`zI;ANvGM?S-Kcu+uD8B$yAwn41b3ZR__lFK`G z{Y!Cvl!_PimXhV5PBG(pnKD51w1_MiU?2j#oOVKuOr}at{;v<~bnRhgTWAD%3FCI& zJf}LOmM$u46d)_8Tr4`~zng}ax9i%K_arcjvz?}%{!06tT;fUS-8Gggxq=nfC5QrP zvV8yWt{8d!l=Nvnw>iV!^$rUe5dm5J31qKQLnQUQ$n9o-2Q>&e6AXorSyaUR`AzRe z%p!avY}zbX7^)3l$~krS>%O-}$+IP$3sL0szt5x}s6~&82I2Rhe}EKW-E#3$lleqwzoIlcKA_tDp7FVM+A96r+Vq}exxnTX9uOl-6kCQCagaiTV_9d| z{4cJXe@$7RWO0qQQa?ys>u^(5O-MWF477*7*y2fQr8zfml9t2CT9NmD`)~z!l(#!m zV#fK!>-Qa7Ar2>Qg8h9uU+l(1pLxKg+>LBxiZYF-k2no2qWI2~SN}S`RA5;^sSj&X zvv=6_dzn2n-|`BW#@;G2^q;%E>|9=aIo=S^Z+uuSsP;_AP8$dXo6y6iC*|1)3Kdnu z7eJy)L7Ux#c-BCtLr64r)>$_b%shwmxL|fcLPeEzpO9sNZ*2-Sg?LxrdFy8N_C>xI zapbh5@(g=`?FO)jC3@df^&&`qH%=jtjeCVo(uO?9;*C2L^on`vk|b`ZT+78fwF7^Lq=~cTFCRLKa;mpM$EkANDWFlt(CNSL)FZ~yY*kvh zu+Mm`+Swr}zI`_ASA7b13l-%009y5ZESq=_vZ&A)EN-fuKfg6$=q&n){d*EiA441& z8MNO#-eFEIf`?fs)W`iDjvP(+F65o!T`ThvS=a%@sTDUi zNC4R{mR%^4?xJFnP~A0cH1Xc!Bvi7460MVau|&knnAAwuti1{dJc>O15B@8Kxi@R= z3myB@TwLX-;?o|o)LfirC?T$3{Td^6v%w%;d$T5WtjGZZ?D-U}9uTu|lxxaEDdDlm z_x^{la|qIeYr<^Vwv8^^wr$(C(bZ+!wvD%J+qP|MzL>?V{)k!KZ7y%-$vjWuAF~*? z{m1>$uMHxV(#xoe2?cff4D9#^JnQ~Jb&A=uD%kGw&i zP&EHUq&tj#){BAa_xV=O&N}q);C(g-%~l>9EDqTr$h`Hqz4OC~uNYFN`}DSW*n2ml z;u&0gXPQkt(dhYhj=jClvJ2aE#TciNZF3s?W*KrRMtSU0x5ktT5|4TT_F7Yz$u+N{ znhXKxKZE~ewb5qlI89ih2QxY#!`dOt)F7m*!%k`Nuh^~fCe92i7^HlceW~4{GEqpp zvQf?em!laviHNQ`?|xXwF2L?G7$~C=BV95cKBV&PYxt26arAS^$<7cCHw(N&G!f^{ zo&$Vb@=wq@w3nOJx;#9RM};t@&5|liR6MUvK*>ZkCeCWX7|bj7=e$@Ne^R$mq>of= z9;=$1a)vXv6)EIUK82Se5w4~X%xBf}RyVWDitM1%}?#dtDc*!+}E?cuHTK< zYMQo9!cz2qf9$m=hY{R|ds_7s-WV_#bnszM^fxDv6Cm0&P>hv>vn@>WR>n`C%i7z1 zP{tbD97d1V=6Pf`t$P)jO$-Zf6KAC=U5chW(z`W(AqTIbF#ZPt=K3E5n1lKMAzx-9 zCU#~n#{WftnbkWXWsjoFY|X8e5vk>EgfZFKslFk2MPv;%GqXcea7L#3 zfyAUJP9XRkT3b6&_?*BkAozS}L2ye*Lv!GIMkc1gL{L%N>n(iRsi_~iWJ!3&7B?Wx-!dS^))uCQH%tnN|1wqUoq)MH zzd@&#`d>H%|*b%pfNi6{ojsX-WT5+SZQF;SComAEn{j?(_=G6=-Zd6A0fH==We}x&MH0`WJxn(^~KT`|I{2 zK%gui`WsJdYVv~UC;O|U#Tlss0G30e%l_Hl9Ph_|BW!`V6EGo#WNmQ#`OdrO3*daW zP?fW_tKR-qi28Ls-o0j`hJhWov03FIGH8-)fF*Y)RP;~kTQxJhn`c?bdthThg z&x86Pr2R!z0y_ECNo8?$u6G3eGB$qG2xt7h{3Mim{q=^1-R$sMU~y>#KGRyy_=RR+ zcL$=RX?hLlXLEOV1lr*I&Uy;?cn~-afJ8+)gJ2ES(`jWpfo_zwgQj{#rpc}eEY8Vc zS1i&qhtSKoX#WW@W-_%e3~-F_DT~ZHg;8KUbQo%LX`pjkl{LnK4Hn2uk0wD_)En`m zqNrhftXTI|nFy#sTE+Rzn zqOo_|?-vi!PD5c6`Gh3^Na)1y+7ReEnvvZ#c0yoeVgf(|Up%!L`4H&|lji$#yrBNR2rU#Vf`L1kc&PcX*bm~=vrdeSL2R3)K zir-hm>egOqoZpK@Fv^%no`G!!ZEuDDP6VsY&-bTcEvwYZegL-3T99>Sk9Pg{zgR+V zp5_27q`6vkxznEsSVB6%;Inwy3w^yF6b?8@@y?ckDM8EMfs3oAB=^Q>-C-h0G~vfD z)x;l?wEHA2MkcBtVy*J$4#$0w!_7E9Xb{-~h+o_&H7nfGemkLDFJmRlTJUU}j(WoA zXUzwxn+X#q?hd}=`c+nKl+$4EIs0-bk&-E z(>JS8Usw9B(;{}ZFa&t=ICCMV=xkVn0$j7|^PHh==zvP*2~O+QAU8q5TA@o z=^k1~)&DeFVJ72Jz74lg4}0IP0P*Dl4d4YRhAlG8{`J23%tC*?n8OEz61lI_&$VR9 z#5YJDi7Qki!`BX<6^w(ef51A!*q zI_5}((LzU4+XO+i&>qVMI61#S;59boDgc#*6ZO)f5F;z;yxD?d;@O2Engp~PNBSFY zj)KT$TipViz!)HH)$Fj{Lq!W314Y5KP_aNq@&Z*d=JMqnDG@S-IX*tAY?``qxzA}h z<`e&s52VzIu^Uh=HN65z?%XcNln<(P%*bbPd!i_3;mzI&o7#{9O6xpjX6HwzZY5)+SB1ry zQmb$|+UeydKjYb0KIIM)7NfSP_1S!}WnwVsgbSaV5rl$tSH{B}C%1y38KP;D0Nd(n zj$4A-8;Q+45W{k(FWinr(t*Ve>ZSMCbDSH!)0k+d3&lUimFBZgodq|Low!{0N+|ouDuYNs@Xwx z54C7%Ud7W7tCnRliB~M=VZLR{4rZ?KC&6L^&Y3PjOiyE8(`?ykmv8iu+=mDI84xE-ANfd|_rJ~9iH}QrtNR{gVwI`d zY7rixx_=$2D|0HYb1F zlZ~r_HGMMB#bA9(O+Y&dkl1_sYt^i*CEasVDY97vwnp+HqUcn*IR4vx6g<)mMkCb?BNke+ z;Cv;s^z2QID|?oFhUIcP`)c`mw5wQF3gqhEHOMj$hAc8$7Dlx?R9@)@!ok|jg+mDQ z*mS7<9V33k9<-F(4KmwMH=T>}OD~D^^Sll&VSRKWxv^DGt0&sM&QgydWs1D+EW%`bRaqB#zY;IB8l~(nQI3HupY~K;ut2>-(2m)HK#vlQOB?&>U4F^2X0_segNxW~1HmRM3 z+4G}q692Y_PgdjnyfW}xnjXLEcV*d;_w^<`)JTlv(omJ_6}g8RQ!ZDDYH>?W+VduV z9Sb?0#C!C4g!wEot%=G40XCn&IgGjC z4}~5!BD2v7>XwA%JzEc6#iI{#8-)@Xf(ng^m*;W4ak!Mp<*~-kx#KD%le^Hm9M14Z zlsbMiLciE807F4sv&f=GLuq!bD?Nnn_TL%?Gp1OZ?prf5O`XVSW zTZ}C$9F=!AC7eT$>P-}B#Ynw->t%e#xQxh`Ygg79^+8SQ0HK~r?FdQT4+g9P{(MEf zTX+LZ*ZJcJt{PWxG@;0xci37~^JZ(L2h?zx{uQ?xbB*m+?a$6N5o{=pvAtQh?7@ZE zkhC&qAQQ~@N{A1P&$)@y!=~KUh_}Ge%vMVRwiD3F9H;eo(iztFwI5Uw!3Q*tkUgR? zNH<`e=?oqr&1DZP8ZW44o=l0L`mI%?JmAtiU+E0C$%r?(|A>aW@xLK>#EC& zHePN-**T}4dJJ_2zLw7>qZfE^VE@kSX;}f4ds%`nkcL>Wy-j0b^86%1On@zIK$iyu z#n+xvwJB*cI~ll$fN5R5@HzxaX_nIPQ!;DinDhFy4&8v}_!{hTm|w)_aWkZkXil)P z5vP^h55+-S&$G8R$9Rt4eKTy|@yiylt5ammjA`@urK)j6c_`z89|d0Y5XN-D(uUxDq909$bQ$se0Yc0(#;1$(YoeZjNB8 z;hFUOYIi-m=VU)ia&|SXJZYEeMw&UB!9(mtjgJKqk%4NAJ4u`|aH{%?lZsq1y9i02 z18FZd#C?7)qlmGhYwMDC(3%~HNKvUN%Eg@9eN=mk?Y-@l%8M7wWIl0?(a3BpTGWz# zC3B%P<4Ow3*mZ&RCzTJA{>XyJ0CVkDS+AES%5mM?QjHn0>@y>ztWbq016HIV`Z0CM zR&(cshpLC7&@)n%mtl>SM>=UU$SFM?ALtGPn*D$-%Qhoc+hKMA1eQQ3E z>=J<3ZKw{iLfwWVAIKUhwNgZ|`Tl8|oxZ~w;+hH@YRQkluNGF7JuGBxk63$tj3lGA z68(OWpr*aPEoO2-KS3t(u@7irC5oDuF@sD5%#sE-G|$ap>V9O^FUqQ&*J{m$id1+( zwx_|K%g3F8$(0w+v`2-LbrVUYbw+<=lw65;EEpDxP}%dR93}~1Pby6A*GcV=Qi+FH zQGSC*y4pj#)_3+%#ef-oP$8+}F#QM*>{~Vb=&>^M!y9a9(it zI53KdG<1+iD?})MSMjOs!rzjT^(@DG=O!TL)3wV)>d>IUMcy_f(-5w zbZhZ0TX@i?YTbLJ-E6eNk*G(NlhO6EsHXtPw2UmrJ*EdfsRrTY z72;$E>F1#Nww(7gGFs;RHj3;n>>$J;=DriWkjaB23Zz!vr_1p$!6UQZRN=Zs$Z|D+ zlw0}iY{^6-U&9Y!ulK?X@tJf{!%FAqEGo&OjJhu8lp5FYo%fE29xA{O!3*q6VE(NT z@Mx~jOt2m+{UQ8B?Bou|OUnbZUy?h9a_!3>zUScmY0mMwv_GJqI>wGRn<$@QLDnK& z(>Y2|crPZN87ioWz{N%;Q%NDv3yxSV9*?T*6m3p?W%NCf5b=m1zfwF9m4>D*1)q=D zBUKev7wgsDe?mKlGA!ZZo=uj!*$$0f2a&s)ChSPadn;Vv^uzfOrg6`QwPHCnIApdQ zA_8P2DR_xT+X&S(rMWB%5T7ZuvC>TxHl4b=%8=ex0=WB7NK0j*9|lSbo(^3rvj0%RgFdM+rK2@2I_yVA z%7s_EOorw8r?ih28gQ>}228reHwRAbi|c?m<)j2{_aZ=O7obqd7d%BR$J3?-R4IS_A$>IMOabct3G5(5*0R4ibxP>b z7tRgVt=C+qENCS?fTWI{@a*5*6Vyye3>7euZU}<`!j?XE(|9Xmkhbj_4qSwCKq_Lt?8R}9S~B87oyPB(s}SI=hF?R zE2HDPzgk7g<0`7`=d6jOSYB-4%do!+&N5=ehSm-<^GI;6lqwFrrIZ@TvD+eBhfJ_+ zFa<@Yk?lww-fU^ymlw;{!Q#$~!wrVOY}ZI(1?y+N*pp+Lnz&|CKa>lj`IZ^*CcXO0 zmn`nBtZT!9nW^l`lOQI|jQCA`wd%HpXfF}Arv-5D$@w&v zmwFSKtF?sT?ZAkpC{`6A!(@tX6q9s`A2kAZSiJMPaabL%JVv_rG7bMl#b~2F*C-V< zac*;124NW=r4qT&!DP{zg15F>`$H$VBP=f+Fgw5--8vwCZVgv`%5rZR|AD+yo$bk# z&>;hdQs|{L>H2-s&~T{<@5wG}mTqLK4Z$r{q}Cv*MN;{)bf1o7xt zDrrh{tl=C&q+a}c)m)uo_FU(yw*hwUH9G9DyeC}0wJA5eaND@(GExy#PQ!S+dWa>y z0#h2HuRbst{FBho_Z(4B9F0nEzPUu*XjdhE)j(un|c$zyg zr_g@6sG0^yEu2m|5n3&$%)a~ACMy3#`ftJ*1(Uzg%l>O2vf9%jcXAiYcSro75yBW1mjeX_NCX14J&PKBo#PEVIe(^_DP+o1q^2ls6W(T|2LAX%+%|Fe z5qTaTKW_sVR%C1nUC9owmIZF>tkR8bT)i)G%?QcM&7g^szU{cDeNcxq{+$c-+#zi8 z107@>A(^2~A<4q--VDI`Wi#FXhI1aiKZe3u!826NNbbQoguj4&N;N zh{N&+GNGzYcZH9SO`iz{5k<^9>K_&8T$x#-6Y4TOT=+BeL$9vgjmAL$ccbvNll(xteqW`}y&rsmY?S)=%q>*%<8ll8 z!NW>euX5>a1_{<^lYP~r7EU2;a~Kk6){2~7kU4XA{^Sx zsjGnS%vz9Fjd|eP=66V3S4HBP^KH};_Fz|;V_D%84yFTtr*m1bxH~B3BFTN8&M0NZ zQvJ*L_pa!(pLpDV7mr~K>5apz?n{jPc^keVGXgYkc=nEH(ML6yk$rYvlzEBsEWSb% zXWuy0*87EwR|jQN?eC235MkmYVJpy^WG|V#V5|TOd;~!-c#MeqI2dM?HAt+mNuf@; zM1+u}4n0$+5%FhdzAxY3`~pgWpFRCgjU|T1{pw`i4L=e67?bN)kE6Pd^&myP-m>Mb z5h^W~`>|Ac6S)@Njt(+QFlPYqWY&?j11LDff8nHD%lKavZ|Oq+97`!aK}U2ZqH))Ky4^yF`p_23MG>M`H=a9X>3$jXhi0TE$P_n2aE{R)MAkr zy-mI$z6?ZW&*+cp9HsBx-T#`M3}9Pz1U z;n)qbHQ%?N3aMw|Us3Cbz<9lWUf!0s!w-u&#t;0X^g6ew!Le#mBtL~2Z7veD`&E@H zS!I})qY~WZNXCE3(|TT?91j>Z!>15{a>{Y;j255f4~izzBIIv+v{1D1Nl~W8 z!oGl2Zd(6MbIs0>BojOtcVjm3$1J6e@sE~Y3==&zm1Ih8&uq8Q>nC|7c2qaL%|2^IfpevUOHmB5dVP-1GGlhOVH>JjBX0j)8j} z)I1VQs~j+xRcs0x=Q6x>zEvEU=!)o;dOYPOUMBLZ6Y8~fns_FVUXBXEzSP3tPxB*b z`ct<@4@VY4`&$?JS*4E5a8K0gPybpQSF|{Q) z9)K8Qbrz`?=gXhOu62T@$;?GpI*D#klD7AK5luM|1G?{5Rfa3H^8KPweKmS(gPRK5 zF2p`e`p?()ZKH`JLd_*S`ok|uqH4?lWyg+ewCF9VYnB3n)t9Z84C6-P+rj}59RmQy zyDT@@z#q;Mq3iOH&GECnX-x0`ejq#@`>Yf_AONmBEnND4y9>^XfiVwxKB2+eUhSPF zeFSQLR{dq!lh_3AggcijR0yP-<4k?4T@%4?%P>AVcKPgOJw!oCUzMHdwIdhcnqgK{H48;QP*h}EemV8wB(#Bs zwTB3X zF(^55K&{ckM&1sDFTmxH2vI)RVfVEM^a{o1uXBCLrn zw6_c*$_82J8PzMB%n5@;C{NSQodUVP-gVL{M<_fq#gJ;xzSdG92X1vcq7fAoSD~-p zb^wNxw~b}U9!{EUx2ZJZaapUc#lz5A&lvljvt&*-*vjL7|0pR*LEje&wkwhr;-e!l zx8KHla^lADMfP56uK%kZFZ_sT$oHYQU4nl!p;Cx~apFG;1MT!IsDy>LBW9%h`%hlc zGmTsJs*E`J<20wpoI4DGtQSJ8 z`O&=|8Z=GTY1aQBuFlLM57XxRZj}A;|wQz7OtuT znHrsGVWC&tF6Nj-Nrg#Jqz6L~&zVZRh&jhbR#O>VpW{Zu11uVp<_FS|4{X-+ z{9bL8WDkS=H{ujM@1Qtlme~4qbt0* zxIyKfsxo8%w63O0PU|6J=gWOfO+ZUgcrT_4Q*}00>&gMlu^g(6YtjAF3R||n;iiO} z95YLFIKweTD^aY`EU+deaqWeuKf7lbFVl{6xqx|vaU9G@M!+U`{ox{ngjORRGAEb* zLw^=i=ddpUQ3YneaUrm$-fyBhzxsC`V7TbSd};^V)@!xRx&_OFkFdIVZvFaIVdMP{ z^2^)k-RCRl2B`I(Fb(26{+AJ*4VLBp_9l^b(4@IsTdGXF$HPD@qcn1oYjy1bY`5LB zrmK{h?$XJ=WNU#&UE@fN&Xo0KY*!Bmju}yGyETFv`HCO~mcXJ0{zJ@%U`?2Pn?%Z+ z&bo>4*GFDuasrmp#48XH4n%O}??9GEk6YKnk2QmrnS5a_wV+az$8*L)L3yb~c#cEP zN8PVFdq5X%RD;;Eq-R?3I?TD3SL~Y2ljzF_!)G@CG=L?E4&B$(&j9}Ecx61Zr3-bd z;}j;km;D770~zh1Ji<2`c980%c?n$4z>SRaxpVEc`M?_pPJ+zknT-GOsIgos8uF6N zd_u6sM<^mB$6yQ$cP6r(qRgr?+(2^@A1yS!srnTN1c!=C8AhEZl;2C$cy)<6J~&hv zFx;i}#O^d{iE=>pOUh&=g}c_r$CdIK(d3>mEpa-mi1D6;^}MMN zB^n7W>Vk@W~}cq^oBX518Eek@gN2EpXA;P&a?j{CzN3d*`>l?GDOw* zBhdyAqI}*s9tNWz}0NmRz(UMNjy`mn-$Xd6-zC1DZ#kD`{?@SRiJq zOKY6LEa@lC)>z&|XWmQoD?WDto#N3q8_2d4dZ79M=C`C$ZRU%dYUCJc(PSe{1*D zUKe3aP|j6Whq@gFEtoR?<|j)->sGW1iBz{g=K=bYmUOY#uM{^S%0Ho@beFM6NgFR~ znf&FBgz?r?5%!c~2Jg+Wx3Tk@ zsHk>(IsrqCv4Dg0`OG(=8$?~e;9_#xI$D5>*SuBdN8=AKaivBvZo2C6P0>wC{$k#8 z2C>m)ljRFgrF-X!8{oI;w$3S*Ku>BIMGvxb`n(cjA?;m@>qlvrbNmB6@+#8tt|+zK z6!+wK5YWSdg<7#=bl)-}D!C?M8OuLT_+9y7*Xc0DO($TccVc6MxmAbjdPqSYT*2}h z;DB;!F!H(PnktRq!AX4L0!o@Zxyt*SvIJBy+=O|M0-;AWN{Ucq@*~2_QeYfqGXvDW z8jgp2@t(8u2Va^qYqL%!|MYOLj=sHPSoUAseOR{&kO2{`fzA%{S`NHSzVh}5s|d@L z2mv`YNLtO;-7&Oh{pD1sV9C@H?h%h@YW8RzaTzc(BB%31C_^HlI&0RlIkM)X!1y7GGsqf!RNo@zVj*!4}ks(#+AzO#CgrFJV z;Qq^ZN92g5IeXRNWdRwzPE|^{nkb$5*nrx`l zcQwgPp&u|Nlrj{;BPt*D1sP929!;@N83n55pAbh&cuvuCqi0(^4yk2;({HT zI`7%$5qm>xYa4bHGHMo;TFi|Lr$gBV9*xV?E^f=ZUp7y0wN! z*^bc*m$b@X$^udywv6plePf~wJ0IyK1x5&_plxcgytPoIl)*P%u0!6=5FLO&1hdlW zs&h(ZG_uPOM2fooPqdYF%Cm;5nui8TvNxYIOWew3zSf*05pAj*E*C@Cs!DR4{);=D zf&m6QJI^iS44q7^yZYxrOh4@RVn^I2z^qCs;~Bzezhlt;H*Lg9)Og{VM^N(GC_3>Q z9yU0vt${uU#L}`hr{C;rhnR$=Z|R@^`mfL06_w=5x@$OAg_^SL_A26ea%4&r|9%_7D{qC-HPr`e31hR~J+U9I%)zJ>R!@Qady&`@6R`Q-euKVL4^ z>-mSH_B#C{NdcfJm2+|vzZssyjUbKrx9>0Q;7QK4xLCcF9v}{ZfNRa!kR#Yr`kjHn z6O7`YcB*#+LtXF0hGma>T}Gdwiz#0k;Ky1i+a^uyZx5YZO)Z;Dh_v5*mgPJk+S z1xM!@6@|CVjX&d|HuguXT2#~YG{al`*GZLTT<{J~*GeZIjn_ohiB!tfNc4Htsz>I- zIJPf<#>Z}h)X4}c^Ol-UFs>UVi3Lw!KpSWJsC1C?Q5T^ODw}cUgtbdn2II>Fw0#w9 z4f8@)R|g5vE!SN?x>bUK;kKw8=q)-e4DaQBbLcRuW;z0VW~ZpJErKIWh>15#OwcAg zU0X+rM_fwr*JOizxf_j2y18LSoG?_4&onAozs~;D7sA7)fwQ(s zIdREJSld7{Dep#9=7=yB5m?v(iX8Y2vtjK$1jV2g59@G{Re*xO+nNctrQC_*h&@qT zq6UGz=wF|-j~*kl15B4`D6)?m?5`CJ6Edd||w9!$N z<)m1aK@!qpX$XvvRP&4+UsFL`znKV6R0MNt#`GQ{<~myl8?Rz1jSX&sG*H+SBs2J{40{!5k#%x+Gr*)`Xwy7Ya)N?f&9+s|;@ zOux;6O0cqn)#9xs&hdu59Gv-PgI?@5vezxpoZsWXv^k~trp``X2{k>%pn6mL52aB% zJ}plPS3WINoQoiL93-!L_3XzH&-oVl4w~B4KT~`Jx6AoZjva@cS@;_Ax0LD-3%?xD z^h)u^bMXCXXHAcJpj)8hFb`cQ7PpR=A;|KI@dal1nR_g>ehA6SF2pJ~{&>0@`4cRb z>I60nSM#%z#t2&9raUS6HI>CMn&RVtyLv-?^yIt=x~p~nU3uv9ukc?NfByB5ZNj6C zSnm=k0&^sY_{s}=ri=Iv;P$jXs#hN$udaJFrF6rQSC}ReN0_fJRFD*snqpl>KPy^J zc#tI||_T-QgMg zWD#1ex!UzDH)MRaRf`>~HWscIQ(~6~BM1p@0?QJTZ%PwR=J+i`c}&ygstOhQZ$`;y zF=m$*!m<|Fc*LeCtt_miDM7QAlyWDydQ_C@!xLR%ABLpI(-mKdv?~+1>G*BrpR;ObdU;Yh&VGuXyZ8Q>6jevukAOJDBdu z^E^)%z=VmD@ITj5PFjPCKZ6tX)YvzRubSoCxXhd0$2vqorEQXBq?RCKIRz5g>O|}mQmG<~9${&xu zIFt9PH>=rR`iCU%vABG+!G&feE>Ot=e#o9)B#-N{)hMujYw)O8o5PW>>=Xra<4E-+ zP&1xM4s-T+CAPt&B>_7lC1E`(Z}KBRX{oaY=KV-_kz0J0hFTL}bC`Js74Sza-ZJ*_ z`+Z$l@_0~JC)TNSi5g5t#hP+t2jk^Q5NH!xiB@V1Z1N3Wa!m*8DO%};6cCYufdrUj z>qnG4juE_VUy=4aIVgxhai14?fnDXDC5v&?*hdX^YO0zh)@oHn5U6#pYqdozHwtGe z<$2e3zZk2?)3fAIPWe~X@V(YqC@*`leL`h(Ra1hh9}raOXd8fXEL5|n-r|PP{_Q&@ zI4qt#+IHId&4&bHbaGSaM>V(74!|vU;IcwbIM0v~CC9fCDK2iVSO4L^k0;?l&;UAF z?DKGDcwA4QIul``Bv``b%V#c=FF5iJwe2Mt10ELT6T*h+Q!I8zXoz%OThvBP1r<}wZBvPaGlA_!8L zhcZY0SDVK`tF#(U0-H%&b3i1XO~9cby^!)gA1$SwMy7;e%IQ8r-DKrR4Yg~hUPxlz z#lG`zoDgTEYxlq5mW5{|n)dY@*5-lrwRmAs=kuu7HNea`cA7SqN5L08w4X^mGaUP) zxD8Jo#My|t0(;N}MP_|y??|p^gdhxv;`N5Y28i7irZ%s{eb;eQB+;@O!w$Oaf$&rd zGg@Xy|BB!0GW_-o!i3Z_O+tgwRBUJFWuB3~wkVQl3=SUAg6S(?y!HU6uf~4k5C+t7 zLT_1g!GOab9W_6)vEDy`qXX3m^KTYa+8>cJ zv;WL0S~zQazAijV5=l-0&_9R9J%;e|G-1Z<7~wzl>J%zCmaIy z9ja5#`J@6o6ZgpC!IL5l)qAI7AN_f(wMk5bnF~@RCV$?ev0kxWaBQ6T~ z)ioRs19l^od|0RYor@#Pe=#SaZzQW*Yv_{7A(m*%3FD1vg>#F$dV?!t?=svRTLMqx zv*p%}PWw-$rBTqxH4W+CA#&~6=XWj|yXibHrKs2nxbk|A9gMRMn|0;JpjbRmHX46~ zJDzaW`tN!CPtGDqH0w4?V0V&*`4Fw8{)YC=!s;ci#TdetM;Fr*er8tMW{p~DIi1g8 z-6rm){gJ^EGbqa9<(# zL69A1r5!Rz|Aj|CvHPGFu6xl5SuIRFon_QL661&Iu;2jv^qp9GkLd@v1HNDyGv>bN zX)MgQS1vA5$@K8deO-ljMf%BEpoue4we6?IUK()T9Dxp3rcAC?nG3KOhd>e#Nr_ zX6?t|<;@^LcVcmhgwl5NuqGN_Om9Zh@QQ062WA-Wmn&q+!6?KNs#YQRWZ_8qQ#j>( zLz|bZ)_OcE56}Kq8VnSrfHoltZ08GpI-MuXylP~Lq*`8ex!&)CU%_ni@&zDhoO2g3g=Q0I7#Ox$_pZgJENi-N*b|#eBtC7pLYNfKA^? z*?br8d3Z$+GR(Mh)8a*WbThJm-lf8Dlc512HtqcJUCo{}bb2b?knvXNKA$eT zI+$+^0@X{fd|kH>;~4HKGNs<33Rv!sRyz1FPw5em&N;x(APXh`AhPA%&Ca~HfE7z)GIMH1ucGW29`WEbh^b2jio~+mY9fUTrke;=Jf*4m zkm9SURI^Ll>4b+5hA(5=C+BY1*3)2i`al3?;3o01rRv!DJh8bq zm(7c>`QLv062SZkU+wxF4-kiVi%Pjy>;)p8zxYHNJh3Fp3=)KW5!iz2xG!VH;E|F% zj%ek%dqXhF`i&i${T4I}TqaLU0=lsTJ#|184+U?F}HmX=sZHM*Df^ zD{)uEbK(Pf%HxqB+jEK|ORB&$SvT&oBf53#e6<-TkCJGj$2`Dk_DQ!fVaK0ea?b}> zu(mtm49oYc_@jXyv#23Lj#{y_#Bl5?Li!JF7zU&D9kk2buJ5%MEZM0X z5o3{o9c0p=-|y-sPD#N;YBT4TlhlsYaidaNVO^%oUkLes=sAOGvl|Et&;q@;{z+ue zyLOK$0FRZfYw~~u_&f~u^@t|p!5`B(Dz)JDYWGTcrdxwNaP|mt75J>Jc$wAFy9Cim zUZQ4PRiUfTVsN>RAP>hutBHFGGa8?6GDurnd>txJh(CxH*uwH@FPMGV zVl88&|31WTEEFBPKR2LFFGlXa>SN^BzgmYf6`46|0I_HH0rM2Z_kaQHT(c$cO1>Pf z8B-@eA}iTy-;XenjSqUtS@5$xw=5_N7>|0bo4u+S4{Tl&3-e`#IP8$1jN>SSYU)60 z$p?V{Y7Lr(tH&CU%xJO6+qN_Xc#E)G^4_^>!^QHK@33p$lW}aGG|PK4JaH_jK1Z(@ zny3{053kwy8d9It!%aq?4N^Qy3E5lQ`QX#N6OkqdbUk(S#?xh&*H&a3kx(&qz%0r` zhyU^or}V0-6_GUD;Yz!_Fz=5M$o}f>Rlm1{?!g$W@PpHA0NjnA>w3?^ zY#rX$p>t#*l@zD|MrPNu(@)AED}Ua$ySEur{zQN;f=*M`kS3D$rMd&31q!^r+J>kj zEY4SjHi2&N!M>87gQ-%eSvAD|4T@ggh18 z7Mey7hNR%?g}N%M<-O2@KBG}vcCg;r>u8ea zlGAvZJ9T}}8x6eOA7SaB%5kX&2F^gH;W2Ux6lPA*=}EL(f1OMh{|u%Q7YFpTMm5h> z5Y`R4dzVjt9H`=g_F@qzwXbbL`?t#%SiojF>PI%4fK2Oi29STNqOFu!!Fh|sutpsF zd=FfG2UG#f{n{7K6b%Itm|xi(TrcZc!qBct+6r8Wp7iA5c8Kj1<5Hk-v@DD%mO-v; zn*a@__9gEZ%u~+1v8Hq8#GxrYhJtQj5oF)&TdIc(3#fBctP#b$uy@(7ydNo3(KNVd zG;5C)V`8l^-04*ipi`72mI^0*3HtvucHY5oe)}K)h9EY2?>0m)yUVU#qSvU46?L&XtCwI! z7c8QMs6lkmJCQ_5LUfVnL39#bwDptwy?5r$+~3T-GtVFAbDq!hocGN6>wRX<>s=|8 zOQ-RUq<)C}7!z$u!?8~P&D~V)RKzG?&+*w0ndB5gpgl{D&L~|ILJzGbkIvscDKG@{ zUQ4qRLa3c4(BbDq)9Lb8^cOg?`$i&xsZRkL5rDVqhg4zf#ZUa_j=KHn=)y~uR>2i- zOf64&$!`UOE~Lh=hfh>rESm+~n%PYucMmCdJPj>1tv$P;6oOdjJX|N(Y4KxkD64|h z)78AFMS*z*_GgxqZac0K!&#C?!3F$+2~fW|L?${Z$|B?8evzxno2o;t@%QFY1kQHR zMxCFN#i)=(UgAnLYh9zkKb{9z-1Vd^G~ISmeK)}ND;wWWreyS(r=YJ&QN*L-J?MIkx(ZOQBYK^8`4xow@J>(dLu_HE2Y zSM@5SGXF(TZ|s>vQ?M6RXEW2T^;ym4RDKo)aOSnPA-r71rscS)&<#k6d|WzLU_u|p zvikA)XIDO6T`iM^=4viJ&($sEzssCe_}l7qd49 z?^phJ8Lpn6Vl)Q{?x z1<$;e3Ux~DF@24h2Iud)-|Em0viKj{SCYc4%Djfr<=Og{pVer&NgF7 zMD-l=r=O+?X3sAiOn>q$>g^j#X$(8d5Q|NdaGAhJk9ofN_{)FIzLkN$my!T%Q;it6 zsCVA6Q{$vaz@HVFNqAPSWFfx=j6gMm5@GxI2+r%G@5I-VnVb97yl0cdKd%p5Bvc+) zWkuT?=NDKljT(>{D$eAEF&Nk5y__|%gB0`>CtfA5M%&w2rOF3JLW4=P$&BMTAB9{^S*)-0jdd&H%{2BQfBC9w;D4*T&h<#|;3| z0m4N7z%IPJeSlE#%}^WV?%?AH6cvHN{?{;s-A87I%xZL)uQeNL9p+`fo=Nugk{WgBCus(Z zf>Y%1Q=#OVk#rt}W&GMpoWB$M)k+vX^5Qc^XTDP)bVXP*x_aT|=aoMpqRtc}%LnF% zp#(P%rndJliaTM2&YM*dfNjDqvULXVd90 zW6}JuW;@f-*M~jite6w9FoXOsI-YEim9)|Z@euK(Tu17%I5VLHRg?K3&~v%A_2;^D z5qhTqXzQHsD@tVHNZ)SIHb_Rs?}-P`73&nb0hMJv$+PTEr12l*s8Qt#8XLS=js5f* zY5~5Sjqj*nx{f%M2r*k*>A;}JS<_p(yMmua42~T)nz*)Bb7w`)#RRZoq`E4WIUmN6 zEq>x5s0L#&Z?4?eKbQpd{W^=;L|V@0($b^Z2W+;BTXV?{>U$PRf{M0rdl~x?F0s=! zY|RV4zbzq~v_0Fwh%rg(lPq&aJSA3#qC+Hm5a74IAyFkGe<^_wH1K?LW2HtK?UJVx zUmrTLbaCZnu)DCzWmDJ_I=oF(ZpXu>ahY}n*<^S}vs-I@X&c%qn{(tq&8lr~;@{gC zoy{Qe46&ayWp5i3>w*(nA>3uwU6}5RGZ7d*$*|A$7T}wZ&kmznp6Qn;TMCqB27Jhh@#o?#(@f%2_Es6jgUPT$G#M#Tdk*7UCwc%9vu`?wUxqz@6xdCR60 z=)vJvD6iK^6q?ES_o^9-QI{5$a&-1}qPa(M-JmH63FFhl3*28UB?NRPZzpiT?p)<= zp5olh_-`3%R5Rmg)m;3yL_7(P+2Cgdi?eJCWPrVqmQQ4~2ez4vuWZaT&3Jj)y9Lf$ z{Eg;_+g?_ZtM@t`2`6%BT`9aw-oZ6j8wU=B4ofw=vGOEeVFTWv@=mH~{WxR@l~psn zgR+|2MA9~e81-12RE~{QBb!bRvYcnMB1^g)*HcdpM1x-L`8PI=P+`)`^L(@pt&Rp< zg%6xJq-XV|hlXyRtq>4#SZkz2qqH;;9NjD8@ZbNYom5oNx*H z;<{5dUG7N(lQ~A{O&BKO!=#*cpz*IZBlYi?vsheN9#O4E1Rd!UZ+~aInr0!+RsPjj zW_=IS*+Onav&jEm;cnyL)`oR(aM0n%yxSP9TwZg>(DLu&?Z(N$b*--8W}23gD@qsh z1e`2xpf~ICGnNPY_py5&~d113`$iPPe+?^H1I^abfwC~0$OX}r=QZ)n*gIq zA=rA7+rul$=F&>y!=(dYkp7O`5gI=1A5Q?fA#{19p!8FB^*%m`88+@E^(}I2B-ryQ z_`$UyP+G2n%1cWhBxNfVX4XywgrSxjoUEZbnMrR(mb|S)Y$6(X!?-WF)JHUo^WQ1i zd)bIrl_@=cEtw}s5>F*|7N?*JBvxvSjzD?lQx2!T;7|ld=k!IDsWTDfK2t!Fu@~Nz zq~5PDQ!=j@2P@FiHFHQkbc1r$M+aw+aY^^-J}q;aer|7OJaVTb%PzeYotQm^<&x8$ zL((BUg`9Y?F1)vcN#z(;Hm>(V=~C$R)~+#mp{FVbE)0qVtyu6vTNM|CQ=1_v&OG9iGN~*{RH84<{Q>=yeTZyj>{F6B!}?72WG>zU z;&vahE++-2LKDt#uV6h6dp@=IcD<^}&-AHR43$qvrt#{SO!eRbiX19|?<6bK;3L6W zleEd-lBOmLG79|R`BZStz5ym)`Ck4eGAR`f#?5S0oi0P%^YJGnVF@f#^$ZoM#8T-Z z4H{XgP=15zf=&{wGxb-_49>Q6Y`Ze{J;~7z1JW35gJj|${Bs-JX8ap5{XCyNTB*bQ z^Yl}%JI}m^RD8g?%_eze_q(Q6)Ka+2vL484x^?v<2)4YOt9#Ef)V?toc}7dINzTr` z)z#}M-Mr!nY@VuXE-?}+61=a^^p!&DD(Rz91ZBl^q zb{YMl=_H(Wj^hL!R}m);w0!IT5up&Y@l3Q~+exppxQr@RT*f9zKfiBTn=|;}6;K5v$}diRoyqOgVX? znY3Q^T;5Yy=R<6w0>-Ubgoy2JIXQ|t?&FVrQ{{Lx@{q~FEIN^+I4ij-&2PAldh!{O zbMmIpJ;~56%|Mye#r6V*q}ul2DWQv@rLof8B9o37X?la|P8GB%_M-Mx4XH<8pU}D& z#=$SkX(7eJ11r#2-1n-(uU+B8;E_rfF)~5)=Rt~aB>IH>cx zI6h|Z;?f^S#J(3K_k&YzjVpgIPWKG|dV6)5gUt6eM!NA4j$Dms9lfA{>xu9^aM3vU zfbi7)SkujOodFdkm6+9glJSsW=fI!h9AIO0X!cmwDlJ$~n_Gx-mz#qvxBDXC@hwW- zVk^#~>J&oSK1v2FLQbs`n~4t@Yr+$tFhK zCh#H%dxHB#S1_3WEJQ93zPDD$Lf^BENwVDD_nhvIktfo>~lfzTnGQogbdcNX<@fX)63cYRpGBr3>pQcmNvt5{Tq>6gieGpT!kUOXRi-!j-A1oDme7NK!m!rNI;LyR3 zCpy>+?JAngaGz|ZjI?ZAQ0f2RaQh}aHNqpqZgDRJm%+Z5k9>Z5d57<`xOs#KS2wJf zG|B!Ypa<=KE`z&fn($6ooh{*``}FvmT0)Xdu{<(8%HoZ-nvrJ+Onv*4rj_T<;%lrf z;n@$e<`<=;h+To})X*Y>x7Yjw#%!LDg8-zlNjGPlej_nE@dsBkL`FY z+U?Q6e>l)Tg)-1g1}vheqykkIgQzG$po%aEOiWc(K@tK{ zx=Er_Q2;~afd9SaW|e{U`Y4vbV|Be-R=-O1boaK}=FFL53*3!DMk7ATcQ>7&>-joq-NwaeN=gxWiAiaD7yl(ai0)v5JFaQ^q Jimoc){{SxF?-~FA literal 0 HcmV?d00001 From 8821b7767f60a0b773a3afa6b25ec24863d77ea9 Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Fri, 26 Jan 2018 14:20:50 +0100 Subject: [PATCH 003/182] New shiny travis script + test dep on dolmen.dev --- .travis.yml | 62 ++++++++++++++++++++++++++++++++++------------------- opam | 1 + 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6256da54..70ef58d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,47 @@ language: c env: - - RUN_TEST=false OCAML_VERSION=4.00.1 - - RUN_TEST=false OCAML_VERSION=4.01.0 - - RUN_TEST=true OCAML_VERSION=4.02.3 - - RUN_TEST=true OCAML_VERSION=4.03.0 - - RUN_TEST=true OCAML_VERSION=4.03.0+flambda - - RUN_TEST=true OCAML_VERSION=4.04.0 - - RUN_TEST=true OCAML_VERSION=4.04.0+flambda -addons: - apt: - sources: - - avsm - packages: - - opam - - time + # Check that funarith installs correctly with and without its test dependencies using opam + - TO_TEST=install OPAMBUILDTEST=false OCAML_VERSION=4.03.0 + - TO_TEST=install OPAMBUILDTEST=true OCAML_VERSION=4.03.0 + # Check build and unit tests + # NOTE: testing needs OPAMBUILDTEST=true so that test deps are installed + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.03.0 + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.03.0+flambda + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.04.2 + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.04.2+flambda + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.05.0 + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.05.0+flambda + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0 + - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0+flambda +#matrix: +# allow_failures: +# # opam-installer fails on these versions (will probably be fixed before the official opam2 release +# - env: TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0 +# - env: TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0+flambda +# Caching may take a lot of space with so many ocaml versions +#cache: +# directories: +# - $HOME/.opam before_install: + # Download and use opam2 + - wget -O ${HOME}/opam https://github.com/ocaml/opam/releases/download/2.0.0-beta6/opam-2.0.0-beta6-x86_64-linux + - chmod +x ${HOME}/opam + # Some opam boilerplate - export OPAMYES=1 + - export OPAMJOBS=2 + # Init opam, and the default switch with the right ocaml version + - ${HOME}/opam init --compiler=${OCAML_VERSION} + - eval `${HOME}/opam config env` - export OPAMVERBOSE=1 - - opam init - - opam switch ${OCAML_VERSION} - - eval `opam config env` - - opam install ocamlfind ocamlbuild - - if ${RUN_TEST}; then opam pin add dolmen https://github.com/Gbury/dolmen.git; fi + # Testing requires the dev version of dolmen + - if [ "$TO_TEST" = "tests" ]; then ${HOME}/opam pin add --dev-repo dolmen; fi install: - - make lib - - if ${RUN_TEST}; then make bin; fi + # Install dependencies + - ${HOME}/opam pin add --no-action msat . + - ${HOME}/opam install --deps-only msat script: - - if ${RUN_TEST}; then make test; fi + # Build and launch the tests + - if [ "$TO_TEST" = "tests" ]; then make lib && make bin && make test; fi + # Try and install the package with opam + - if [ "$TO_TEST" = "install" ]; then ${HOME}/opam install msat; fi + diff --git a/opam b/opam index b7f0bff2..fcd22ffb 100644 --- a/opam +++ b/opam @@ -20,6 +20,7 @@ remove: [ depends: [ "ocamlfind" {build} "ocamlbuild" {build} + "dolmen" {test && = "dev" } ] available: [ ocaml-version >= "4.00.1" From 25b8c97fa099e57abf21f976ce6176ed8707fb58 Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Mon, 29 Jan 2018 19:39:13 +0100 Subject: [PATCH 004/182] Typo in travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 70ef58d9..3c806ab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: c env: - # Check that funarith installs correctly with and without its test dependencies using opam + # Check that the package installs correctly with and without its test dependencies using opam - TO_TEST=install OPAMBUILDTEST=false OCAML_VERSION=4.03.0 - TO_TEST=install OPAMBUILDTEST=true OCAML_VERSION=4.03.0 # Check build and unit tests From 16e6be0c0d37bcb983a306cf2e8fb31ead0a508e Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 30 Jan 2018 17:04:58 +0100 Subject: [PATCH 005/182] [travis] Force installation of dev version --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 70ef58d9..c6a849bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,10 +38,10 @@ before_install: install: # Install dependencies - ${HOME}/opam pin add --no-action msat . - - ${HOME}/opam install --deps-only msat + - ${HOME}/opam install --deps-only msat.dev script: # Build and launch the tests - if [ "$TO_TEST" = "tests" ]; then make lib && make bin && make test; fi # Try and install the package with opam - - if [ "$TO_TEST" = "install" ]; then ${HOME}/opam install msat; fi + - if [ "$TO_TEST" = "install" ]; then ${HOME}/opam install msat.dev; fi From fd75f536fac39a6bfc82547c6f711a970057158a Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 30 Jan 2018 17:15:41 +0100 Subject: [PATCH 006/182] Fix typo in opam file --- opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opam b/opam index fcd22ffb..a1ac6cd4 100644 --- a/opam +++ b/opam @@ -20,7 +20,7 @@ remove: [ depends: [ "ocamlfind" {build} "ocamlbuild" {build} - "dolmen" {test && = "dev" } + "dolmen" {test & = "dev" } ] available: [ ocaml-version >= "4.00.1" From 696002bcf711859e84b4ab8cc4bbcded8b18580f Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 30 Jan 2018 17:23:57 +0100 Subject: [PATCH 007/182] [travis] Fix faulty conditional for dolmen pinning --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c3e1e478..a1e014fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ before_install: - eval `${HOME}/opam config env` - export OPAMVERBOSE=1 # Testing requires the dev version of dolmen - - if [ "$TO_TEST" = "tests" ]; then ${HOME}/opam pin add --dev-repo dolmen; fi + - if [ "$OPAMBUILDTEST" = "true" ]; then ${HOME}/opam pin add --dev-repo dolmen; fi install: # Install dependencies - ${HOME}/opam pin add --no-action msat . From 1722730e261cbf3cbe4e8b0eebb1367f00513bd9 Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Wed, 18 Apr 2018 11:53:55 +0200 Subject: [PATCH 008/182] Fix typo in doc --- src/core/res_intf.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/res_intf.ml b/src/core/res_intf.ml index 20b689f1..5b443bd0 100644 --- a/src/core/res_intf.ml +++ b/src/core/res_intf.ml @@ -76,19 +76,19 @@ module type S = sig (** {3 Proof Nodes} *) + val parents : step -> proof list + (** Returns the parents of a proof node. *) + val is_leaf : step -> bool (** Returns wether the the proof node is a leaf, i.e. an hypothesis, an assumption, or a lemma. - [true] if and only if {parents} returns the empty list. *) + [true] if and only if {!parents} returns the empty list. *) val expl : step -> string (** Returns a short string description for the proof step; for instance ["hypothesis"] for a [Hypothesis] (it currently returns the variant name in lowercase). *) - val parents : step -> proof list - (** Returns the parents of a proof node. *) - (** {3 Proof Manipulation} *) From 0425ce6206c20373ce8ff556fd61aa0cb7ae8f6a Mon Sep 17 00:00:00 2001 From: Cedric Cellier Date: Sat, 2 Jun 2018 14:36:02 +0200 Subject: [PATCH 009/182] Fix typo in the pigeon hole test description --- tests/pigeon/hole10.cnf | 4 ++-- tests/pigeon/hole6.cnf | 4 ++-- tests/pigeon/hole7.cnf | 4 ++-- tests/pigeon/hole8.cnf | 4 ++-- tests/pigeon/hole9.cnf | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/pigeon/hole10.cnf b/tests/pigeon/hole10.cnf index 12a5b066..8bff3e1b 100644 --- a/tests/pigeon/hole10.cnf +++ b/tests/pigeon/hole10.cnf @@ -2,8 +2,8 @@ c File: hole10.cnf c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c -c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons -c in n+1 holes without placing 2 pigeons in the same hole +c DESCRIPTION: Pigeon hole problem of placing n+1 (for file holen) pigeons +c in n holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. diff --git a/tests/pigeon/hole6.cnf b/tests/pigeon/hole6.cnf index e450eec6..5ec07c59 100644 --- a/tests/pigeon/hole6.cnf +++ b/tests/pigeon/hole6.cnf @@ -2,8 +2,8 @@ c File: hole6.cnf c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c -c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons -c in n+1 holes without placing 2 pigeons in the same hole +c DESCRIPTION: Pigeon hole problem of placing n+1 (for file holen) pigeons +c in n holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. diff --git a/tests/pigeon/hole7.cnf b/tests/pigeon/hole7.cnf index d8f83b00..d8aa0a4a 100644 --- a/tests/pigeon/hole7.cnf +++ b/tests/pigeon/hole7.cnf @@ -2,8 +2,8 @@ c File: hole7.cnf c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c -c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons -c in n+1 holes without placing 2 pigeons in the same hole +c DESCRIPTION: Pigeon hole problem of placing n+1 (for file holen) pigeons +c in n holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. diff --git a/tests/pigeon/hole8.cnf b/tests/pigeon/hole8.cnf index 660cdfb5..69ed5409 100644 --- a/tests/pigeon/hole8.cnf +++ b/tests/pigeon/hole8.cnf @@ -2,8 +2,8 @@ c File: hole8.cnf c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c -c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons -c in n+1 holes without placing 2 pigeons in the same hole +c DESCRIPTION: Pigeon hole problem of placing n+1 (for file holen) pigeons +c in n holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. diff --git a/tests/pigeon/hole9.cnf b/tests/pigeon/hole9.cnf index 36437c11..51f1b4f9 100644 --- a/tests/pigeon/hole9.cnf +++ b/tests/pigeon/hole9.cnf @@ -2,8 +2,8 @@ c File: hole9.cnf c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c -c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons -c in n+1 holes without placing 2 pigeons in the same hole +c DESCRIPTION: Pigeon hole problem of placing n+1 (for file holen) pigeons +c in n holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. From 354f2013b158fc188964beca5de360dcdae6ddaf Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 24 Jul 2018 21:51:04 +0200 Subject: [PATCH 010/182] Add assertion to check theory conflict clauses --- src/core/internal.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/internal.ml b/src/core/internal.ml index d559fd88..2a5fe8b4 100644 --- a/src/core/internal.ml +++ b/src/core/internal.ml @@ -1007,8 +1007,13 @@ module Make | Plugin_intf.Unsat (l, p) -> (* conflict *) let l = List.rev_map create_atom l in + (* Assert that the conflcit is indeeed a conflict *) + if not @@ List.for_all (fun a -> a.neg.is_true) l then + raise (Invalid_argument "msat:core/internal: invalid conflict"); + (* Insert elements for decision (and ensure the heap is big enough) *) Iheap.grow_to_at_least env.order (St.nb_elt ()); List.iter (fun a -> insert_var_order (elt_of_var a.var)) l; + (* Create the clause and return it. *) let c = St.make_clause (St.fresh_tname ()) l (Lemma p) in Some c end From a5eeaa0edc83cf1dfddb3b7391bdbd75d4186c2d Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 24 Jul 2018 21:57:12 +0200 Subject: [PATCH 011/182] Propagate consequences at the lowest level possible --- src/core/internal.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/internal.ml b/src/core/internal.ml index 2a5fe8b4..0409fb19 100644 --- a/src/core/internal.ml +++ b/src/core/internal.ml @@ -968,7 +968,8 @@ module Make else begin Iheap.grow_to_at_least env.order (St.nb_elt ()); insert_subterms_order p.var; - enqueue_bool p (decision_level ()) (Bcp c) + let lvl = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in + enqueue_bool p lvl (Bcp c) end else raise (Invalid_argument "Msat.Internal.slice_propagate") From 2bba885266c63a1ecfaaeef371a07d95e3877e96 Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 24 Jul 2018 23:42:20 +0200 Subject: [PATCH 012/182] Prevent semantic propagations at level 0 --- src/core/internal.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/internal.ml b/src/core/internal.ml index 0409fb19..d501ed67 100644 --- a/src/core/internal.ml +++ b/src/core/internal.ml @@ -568,6 +568,10 @@ module Make else match Plugin.eval a.lit with | Plugin_intf.Unknown -> None | Plugin_intf.Valued (b, l) -> + if l = [] then + raise (Invalid_argument ( + Format.asprintf "msat:core/internal.ml: %s" + "semantic propagation at level 0 are currently forbidden")); let atom = if b then a else a.neg in enqueue_semantic atom l; Some b From 5e57bfc8271a2ba1615631f77b4637b8dfb28adf Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 11 Sep 2018 14:19:22 +0200 Subject: [PATCH 013/182] Bugfix for user lvl push when already unsat --- src/core/internal.ml | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/core/internal.ml b/src/core/internal.ml index d501ed67..2583c301 100644 --- a/src/core/internal.ml +++ b/src/core/internal.ml @@ -1199,25 +1199,28 @@ module Make (* create a factice decision level for local assumptions *) let push (): unit = Log.debugf debug "Pushing a new user level" (fun k -> k); - cancel_until (base_level ()); - Log.debugf debug "@[Status:@,@[trail: %d - %d@,%a@]" - (fun k -> k env.elt_head env.th_head (Vec.print ~sep:"" St.pp) env.elt_queue); - begin match propagate () with - | Some confl -> - report_unsat confl - | None -> - Log.debugf debug "@[Current trail:@,@[%a@]@]" - (fun k -> k (Vec.print ~sep:"" St.pp) env.elt_queue); - Log.debugf info "Creating new user level" (fun k -> k); - new_decision_level (); - Vec.push env.user_levels (Vec.size env.clauses_temp); - assert (decision_level () = base_level ()) - end + match env.unsat_conflict with + | Some confl -> raise Unsat + | None -> + cancel_until (base_level ()); + Log.debugf debug "@[Status:@,@[trail: %d - %d@]@,%a@]" + (fun k -> k env.elt_head env.th_head (Vec.print ~sep:"" St.pp) env.elt_queue); + begin match propagate () with + | Some confl -> + report_unsat confl + | None -> + Log.debugf debug "@[Current trail:@,@[%a@]@]" + (fun k -> k (Vec.print ~sep:"" St.pp) env.elt_queue); + Log.debugf info "Creating new user level" (fun k -> k); + new_decision_level (); + Vec.push env.user_levels (Vec.size env.clauses_temp); + assert (decision_level () = base_level ()) + end (* pop the last factice decision level *) let pop (): unit = if base_level () = 0 then - Log.debugf warn "Cannot pop (already at level 0)" (fun k -> k) + Log.debugf warn "Cannot pop (already at user level 0)" (fun k -> k) else begin Log.debugf info "Popping user level" (fun k -> k); assert (base_level () > 0); From 4bb1f5b79333a6156747e0a52410017d20bd8883 Mon Sep 17 00:00:00 2001 From: Guillaume Bury Date: Tue, 11 Sep 2018 14:19:37 +0200 Subject: [PATCH 014/182] Update for latest version of dolmen --- src/main.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main.ml b/src/main.ml index 305f52bc..952a69e1 100644 --- a/src/main.ml +++ b/src/main.ml @@ -88,13 +88,14 @@ module Make | Dolmen.Statement.Pack [ { Dolmen.Statement.descr = Dolmen.Statement.Push 1; }; { Dolmen.Statement.descr = Dolmen.Statement.Antecedent f; }; - { Dolmen.Statement.descr = Dolmen.Statement.Prove; }; + { Dolmen.Statement.descr = Dolmen.Statement.Prove []; }; { Dolmen.Statement.descr = Dolmen.Statement.Pop 1; }; ] -> let assumptions = T.assumptions f in prove ~assumptions - | Dolmen.Statement.Prove -> - prove ~assumptions:[] + | Dolmen.Statement.Prove l -> + let assumptions = List.map T.assumptions l in + prove ~assumptions | Dolmen.Statement.Set_info _ | Dolmen.Statement.Set_logic _ -> () | Dolmen.Statement.Exit -> exit 0 @@ -205,7 +206,7 @@ let main () = List.iter S.do_task input; (* Small hack for dimacs, which do not output a "Prove" statement *) begin match lang with - | P.Dimacs -> S.do_task @@ Dolmen.Statement.check_sat () + | P.Dimacs -> S.do_task @@ Dolmen.Statement.check_sat [] | _ -> () end; Gc.delete_alarm al; From 48ec2d732c19cd9c2d8fd2640027eafc6d50bf2c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 14:03:23 +0100 Subject: [PATCH 015/182] capitalization of files; add new Log --- .../{backend_intf.ml => Backend_intf.ml} | 0 src/backend/{coq.ml => Coq.ml} | 0 src/backend/{coq.mli => Coq.mli} | 0 src/backend/{dedukti.ml => Dedukti.ml} | 0 src/backend/{dedukti.mli => Dedukti.mli} | 0 src/backend/{dimacs.ml => Dimacs.ml} | 0 src/backend/{dimacs.mli => Dimacs.mli} | 0 src/backend/{dot.ml => Dot.ml} | 0 src/backend/{dot.mli => Dot.mli} | 0 src/core/{expr_intf.ml => Expr_intf.ml} | 0 src/core/{external.ml => External.ml} | 0 src/core/{external.mli => External.mli} | 0 src/core/{formula_intf.ml => Formula_intf.ml} | 0 src/core/{internal.ml => Internal.ml} | 0 src/core/{internal.mli => Internal.mli} | 0 src/core/Log.ml | 33 +++++++++++++++++++ src/{util/log.mli => core/Log.mli} | 9 ++--- src/core/{plugin_intf.ml => Plugin_intf.ml} | 0 src/core/{res.ml => Res.ml} | 0 src/core/{res.mli => Res.mli} | 0 src/core/{res_intf.ml => Res_intf.ml} | 0 src/core/{solver_intf.ml => Solver_intf.ml} | 0 src/core/{solver_types.ml => Solver_types.ml} | 0 .../{solver_types.mli => Solver_types.mli} | 0 ...ver_types_intf.ml => Solver_types_intf.ml} | 0 src/core/{theory_intf.ml => Theory_intf.ml} | 0 src/core/jbuild | 15 +++++++++ src/util/log.ml | 1 - src/util/log_dummy.ml | 18 ---------- src/util/log_real.ml | 24 -------------- 30 files changed, 53 insertions(+), 47 deletions(-) rename src/backend/{backend_intf.ml => Backend_intf.ml} (100%) rename src/backend/{coq.ml => Coq.ml} (100%) rename src/backend/{coq.mli => Coq.mli} (100%) rename src/backend/{dedukti.ml => Dedukti.ml} (100%) rename src/backend/{dedukti.mli => Dedukti.mli} (100%) rename src/backend/{dimacs.ml => Dimacs.ml} (100%) rename src/backend/{dimacs.mli => Dimacs.mli} (100%) rename src/backend/{dot.ml => Dot.ml} (100%) rename src/backend/{dot.mli => Dot.mli} (100%) rename src/core/{expr_intf.ml => Expr_intf.ml} (100%) rename src/core/{external.ml => External.ml} (100%) rename src/core/{external.mli => External.mli} (100%) rename src/core/{formula_intf.ml => Formula_intf.ml} (100%) rename src/core/{internal.ml => Internal.ml} (100%) rename src/core/{internal.mli => Internal.mli} (100%) create mode 100644 src/core/Log.ml rename src/{util/log.mli => core/Log.mli} (72%) rename src/core/{plugin_intf.ml => Plugin_intf.ml} (100%) rename src/core/{res.ml => Res.ml} (100%) rename src/core/{res.mli => Res.mli} (100%) rename src/core/{res_intf.ml => Res_intf.ml} (100%) rename src/core/{solver_intf.ml => Solver_intf.ml} (100%) rename src/core/{solver_types.ml => Solver_types.ml} (100%) rename src/core/{solver_types.mli => Solver_types.mli} (100%) rename src/core/{solver_types_intf.ml => Solver_types_intf.ml} (100%) rename src/core/{theory_intf.ml => Theory_intf.ml} (100%) create mode 100644 src/core/jbuild delete mode 120000 src/util/log.ml delete mode 100644 src/util/log_dummy.ml delete mode 100644 src/util/log_real.ml diff --git a/src/backend/backend_intf.ml b/src/backend/Backend_intf.ml similarity index 100% rename from src/backend/backend_intf.ml rename to src/backend/Backend_intf.ml diff --git a/src/backend/coq.ml b/src/backend/Coq.ml similarity index 100% rename from src/backend/coq.ml rename to src/backend/Coq.ml diff --git a/src/backend/coq.mli b/src/backend/Coq.mli similarity index 100% rename from src/backend/coq.mli rename to src/backend/Coq.mli diff --git a/src/backend/dedukti.ml b/src/backend/Dedukti.ml similarity index 100% rename from src/backend/dedukti.ml rename to src/backend/Dedukti.ml diff --git a/src/backend/dedukti.mli b/src/backend/Dedukti.mli similarity index 100% rename from src/backend/dedukti.mli rename to src/backend/Dedukti.mli diff --git a/src/backend/dimacs.ml b/src/backend/Dimacs.ml similarity index 100% rename from src/backend/dimacs.ml rename to src/backend/Dimacs.ml diff --git a/src/backend/dimacs.mli b/src/backend/Dimacs.mli similarity index 100% rename from src/backend/dimacs.mli rename to src/backend/Dimacs.mli diff --git a/src/backend/dot.ml b/src/backend/Dot.ml similarity index 100% rename from src/backend/dot.ml rename to src/backend/Dot.ml diff --git a/src/backend/dot.mli b/src/backend/Dot.mli similarity index 100% rename from src/backend/dot.mli rename to src/backend/Dot.mli diff --git a/src/core/expr_intf.ml b/src/core/Expr_intf.ml similarity index 100% rename from src/core/expr_intf.ml rename to src/core/Expr_intf.ml diff --git a/src/core/external.ml b/src/core/External.ml similarity index 100% rename from src/core/external.ml rename to src/core/External.ml diff --git a/src/core/external.mli b/src/core/External.mli similarity index 100% rename from src/core/external.mli rename to src/core/External.mli diff --git a/src/core/formula_intf.ml b/src/core/Formula_intf.ml similarity index 100% rename from src/core/formula_intf.ml rename to src/core/Formula_intf.ml diff --git a/src/core/internal.ml b/src/core/Internal.ml similarity index 100% rename from src/core/internal.ml rename to src/core/Internal.ml diff --git a/src/core/internal.mli b/src/core/Internal.mli similarity index 100% rename from src/core/internal.mli rename to src/core/Internal.mli diff --git a/src/core/Log.ml b/src/core/Log.ml new file mode 100644 index 00000000..f60603ea --- /dev/null +++ b/src/core/Log.ml @@ -0,0 +1,33 @@ +(* +MSAT is free software, using the Apache license, see file LICENSE +Copyright 2014 Guillaume Bury +Copyright 2014 Simon Cruanes +*) + +(** {1 Logging functions, real version} *) + +let enabled = true (* NOTE: change here for 0-overhead *) + +let debug_level_ = ref 0 +let set_debug l = debug_level_ := l +let get_debug () = !debug_level_ + +let debug_fmt_ = ref Format.err_formatter + +let set_debug_out f = debug_fmt_ := f + +(* does the printing, inconditionally *) +let debug_real_ l k = + k (fun fmt -> + Format.fprintf !debug_fmt_ "@[<2>@{[%d|%.3f]@}@ " + l (Sys.time()); + Format.kfprintf + (fun fmt -> Format.fprintf fmt "@]@.") + !debug_fmt_ fmt) + +let[@inline] debugf l k = + if enabled && l <= !debug_level_ then ( + debug_real_ l k; + ) + +let[@inline] debug l msg = debugf l (fun k->k "%s" msg) diff --git a/src/util/log.mli b/src/core/Log.mli similarity index 72% rename from src/util/log.mli rename to src/core/Log.mli index c60916bc..8923f8e3 100644 --- a/src/util/log.mli +++ b/src/core/Log.mli @@ -6,13 +6,14 @@ Copyright 2014 Simon Cruanes (** {1 Logging function, for debugging} *) -val set_debug : int -> unit (** Set debug level *) -val get_debug : unit -> int (** Current debug level *) +val enabled : bool + +val set_debug : int -> unit (** Set debug level *) +val get_debug : unit -> int (** Current debug level *) val debugf : int -> - ('a, Format.formatter, unit, unit) format4 -> - ('a -> unit) -> + ((('a, Format.formatter, unit, unit) format4 -> 'a) -> unit) -> unit (** Emit a debug message at the given level. If the level is lower than [get_debug ()], the message will indeed be emitted *) diff --git a/src/core/plugin_intf.ml b/src/core/Plugin_intf.ml similarity index 100% rename from src/core/plugin_intf.ml rename to src/core/Plugin_intf.ml diff --git a/src/core/res.ml b/src/core/Res.ml similarity index 100% rename from src/core/res.ml rename to src/core/Res.ml diff --git a/src/core/res.mli b/src/core/Res.mli similarity index 100% rename from src/core/res.mli rename to src/core/Res.mli diff --git a/src/core/res_intf.ml b/src/core/Res_intf.ml similarity index 100% rename from src/core/res_intf.ml rename to src/core/Res_intf.ml diff --git a/src/core/solver_intf.ml b/src/core/Solver_intf.ml similarity index 100% rename from src/core/solver_intf.ml rename to src/core/Solver_intf.ml diff --git a/src/core/solver_types.ml b/src/core/Solver_types.ml similarity index 100% rename from src/core/solver_types.ml rename to src/core/Solver_types.ml diff --git a/src/core/solver_types.mli b/src/core/Solver_types.mli similarity index 100% rename from src/core/solver_types.mli rename to src/core/Solver_types.mli diff --git a/src/core/solver_types_intf.ml b/src/core/Solver_types_intf.ml similarity index 100% rename from src/core/solver_types_intf.ml rename to src/core/Solver_types_intf.ml diff --git a/src/core/theory_intf.ml b/src/core/Theory_intf.ml similarity index 100% rename from src/core/theory_intf.ml rename to src/core/Theory_intf.ml diff --git a/src/core/jbuild b/src/core/jbuild new file mode 100644 index 00000000..777d98f1 --- /dev/null +++ b/src/core/jbuild @@ -0,0 +1,15 @@ +; vim:ft=lisp: + +(jbuild_version 1) + +; main binary +(library + ((name msat) + (public_name msat) + (synopsis "core data structures and algorithms for msat") + (libraries ()) + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + diff --git a/src/util/log.ml b/src/util/log.ml deleted file mode 120000 index e4261c8e..00000000 --- a/src/util/log.ml +++ /dev/null @@ -1 +0,0 @@ -log_real.ml \ No newline at end of file diff --git a/src/util/log_dummy.ml b/src/util/log_dummy.ml deleted file mode 100644 index 554e3c2f..00000000 --- a/src/util/log_dummy.ml +++ /dev/null @@ -1,18 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** {1 Logging functions, dummy version} - - This does nothing. *) - -let debug_level_ = ref 0 -let set_debug l = debug_level_ := l -let get_debug () = !debug_level_ - -let debugf _ _ _ = () -let debug _ _ = () - -let set_debug_out _ = () diff --git a/src/util/log_real.ml b/src/util/log_real.ml deleted file mode 100644 index 8ab71503..00000000 --- a/src/util/log_real.ml +++ /dev/null @@ -1,24 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** {1 Logging functions, real version} *) - -let debug_level_ = ref 0 -let set_debug l = debug_level_ := l -let get_debug () = !debug_level_ - -let debug_fmt_ = ref Format.err_formatter - -let set_debug_out f = debug_fmt_ := f - -let debugf l format k = - if l <= !debug_level_ - then - k (Format.kfprintf - (fun fmt -> Format.fprintf fmt "@]@.") - !debug_fmt_ format) - -let debug l msg = debugf l "%s" (fun k->k msg) From 768f59f88bf83a8aaa315137fefdc55a30ea459d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 15:51:04 +0100 Subject: [PATCH 016/182] big refactoring - move to jbuilder - use a functorial heap (with indices embedded in lit/var) - update Vec with optims from mc2 - change semantics of Vec.shrink - use new Log module --- .gitignore | 5 +- .merlin | 20 --- META | 31 ----- Makefile | 70 +++++----- _tags | 35 ----- opam => msat.opam | 13 +- msat_solver.opam | 31 +++++ myocamlbuild.ml | 16 --- src/backend/Coq.ml | 5 +- src/backend/Coq.mli | 1 + src/backend/Dedukti.ml | 2 + src/backend/Dedukti.mli | 2 + src/backend/Dimacs.ml | 2 + src/backend/Dimacs.mli | 2 + src/backend/Dot.ml | 2 + src/backend/Dot.mli | 1 + src/backend/jbuild | 13 ++ src/core/External.ml | 26 ++-- src/core/Heap.ml | 155 +++++++++++++++++++++ src/core/Heap.mli | 18 +++ src/core/Heap_intf.ml | 51 +++++++ src/core/Internal.ml | 208 +++++++++++++++-------------- src/core/Res.ml | 27 ++-- src/core/Solver_intf.ml | 20 +-- src/core/Solver_types.ml | 19 ++- src/core/Solver_types_intf.ml | 4 + src/{util/vec.ml => core/Vec.ml} | 70 ++++++---- src/{util/vec.mli => core/Vec.mli} | 16 ++- src/core/jbuild | 4 - src/{doc.txt => core/msat.mld} | 0 src/main/jbuild | 13 ++ src/{ => main}/main.ml | 28 ++-- src/{util => mcsat}/backtrack.ml | 0 src/{util => mcsat}/backtrack.mli | 0 src/mcsat/eclosure.ml | 7 +- src/mcsat/jbuild | 13 ++ src/mcsat/mcsat.ml | 6 +- src/mcsat/mcsat.mli | 2 +- src/mcsat/plugin_mcsat.ml | 34 ++--- src/msat.mlpack | 34 ----- src/msat.odocl | 43 ------ src/msat_sat.mlpack | 1 - src/msat_smt.mlpack | 3 - src/sat/jbuild | 13 ++ src/sat/sat.ml | 2 +- src/sat/sat.mli | 2 +- src/sat/type_sat.ml | 6 +- src/sat/type_sat.mli | 2 +- src/smt/expr_smt.ml | 4 +- src/smt/expr_smt.mli | 2 +- src/smt/jbuild | 13 ++ src/smt/smt.ml | 4 +- src/smt/smt.mli | 2 +- src/smt/type_smt.ml | 54 ++++---- src/smt/type_smt.mli | 2 +- src/solver/jbuild | 12 ++ src/solver/tseitin.ml | 4 +- src/{util => solver}/type.ml | 0 src/util/iheap.ml | 143 -------------------- src/util/iheap.mli | 55 -------- tests/jbuild | 16 +++ tests/run | 2 +- tests/test_api.ml | 5 +- 63 files changed, 723 insertions(+), 673 deletions(-) delete mode 100644 .merlin delete mode 100644 META delete mode 100644 _tags rename opam => msat.opam (66%) create mode 100644 msat_solver.opam delete mode 100644 myocamlbuild.ml create mode 100644 src/backend/jbuild create mode 100644 src/core/Heap.ml create mode 100644 src/core/Heap.mli create mode 100644 src/core/Heap_intf.ml rename src/{util/vec.ml => core/Vec.ml} (77%) rename src/{util/vec.mli => core/Vec.mli} (88%) rename src/{doc.txt => core/msat.mld} (100%) create mode 100644 src/main/jbuild rename src/{ => main}/main.ml (89%) rename src/{util => mcsat}/backtrack.ml (100%) rename src/{util => mcsat}/backtrack.mli (100%) create mode 100644 src/mcsat/jbuild delete mode 100644 src/msat.mlpack delete mode 100644 src/msat.odocl delete mode 100644 src/msat_sat.mlpack delete mode 100644 src/msat_smt.mlpack create mode 100644 src/sat/jbuild create mode 100644 src/smt/jbuild create mode 100644 src/solver/jbuild rename src/{util => solver}/type.ml (100%) delete mode 100644 src/util/iheap.ml delete mode 100644 src/util/iheap.mli create mode 100644 tests/jbuild diff --git a/.gitignore b/.gitignore index ee79f402..f07c6d70 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ _build/ *.docdir src/util/log.ml doc/index.html - -msat +*.exe +.merlin +*.install diff --git a/.merlin b/.merlin deleted file mode 100644 index cfd63256..00000000 --- a/.merlin +++ /dev/null @@ -1,20 +0,0 @@ -S src/core -S src/solver -S src/sat -S src/smt -S src/mcsat -S src/backend -S src/util -S tests - -B _build/src/ -B _build/src/core -B _build/src/solver -B _build/src/sat -B _build/src/smt -B _build/src/mcsat -B _build/src/util -B _build/src/backend -B _build/tests - -PKG dolmen diff --git a/META b/META deleted file mode 100644 index 993b7f01..00000000 --- a/META +++ /dev/null @@ -1,31 +0,0 @@ -# meta -name="msat" -version="dev" -description="MSAT is a modular SAT solver, plus extensions" -requires="" -archive(byte) = "msat.cma" -archive(byte, plugin) = "msat.cma" -archive(native) = "msat.cmxa" -archive(native, plugin) = "msat.cmxs" - -package "sat" ( - version = "dev" - description = "SAT solver instance" - requires = "msat" - archive(byte) = "msat_sat.cma" - archive(byte, plugin) = "msat_sat.cma" - archive(native) = "msat_sat.cmxa" - archive(native, plugin) = "msat_sat.cmxs" - exists_if = "msat_sat.cma" -) - -package "smt" ( - version = "dev" - description = "SMT solver instance" - requires = "msat" - archive(byte) = "msat_smt.cma" - archive(byte, plugin) = "msat_smt.cma" - archive(native) = "msat_smt.cmxa" - archive(native, plugin) = "msat_smt.cmxs" - exists_if = "msat_smt.cma" -) diff --git a/Makefile b/Makefile index 942abc33..f58123ad 100644 --- a/Makefile +++ b/Makefile @@ -1,62 +1,68 @@ # copyright (c) 2014, guillaume bury +# copyright (c) 2017, simon cruanes -LOG=build.log -COMP=ocamlbuild -log $(LOG) -use-ocamlfind -FLAGS= -DOC=src/msat.docdir/index.html BIN=main.native TEST_BIN=tests/test_api.native NAME=msat +J?=3 +TIMEOUT?=30 +TARGETS=src/bin/main.exe +OPTS= -j $(J) LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) all: lib test -lib: - $(COMP) $(FLAGS) $(LIB) +build: + jbuilder build $(OPTS) @install -doc: - $(COMP) $(FLAGS) $(DOC) +build-dev: + jbuilder build $(OPTS) @install --dev -bin: - $(COMP) $(FLAGS) $(BIN) - cp $(BIN) $(NAME) && rm $(BIN) - -test_bin: - $(COMP) $(FLAGS) $(TEST_BIN) - -test: bin test_bin +test: build @echo "run API tests…" - @./test_api.native + jbuilder runtest @echo "run benchmarks…" # @/usr/bin/time -f "%e" ./tests/run smt @/usr/bin/time -f "%e" ./tests/run mcsat enable_log: - cd src/util; ln -sf log_real.ml log.ml + cd src/core; ln -sf log_real.ml log.ml disable_log: - cd src/util; ln -sf log_dummy.ml log.ml + cd src/core; ln -sf log_dummy.ml log.ml clean: - $(COMP) -clean - rm -rf $(NAME) + jbuilder clean -TO_INSTALL_LIB=$(addsuffix .a, $(NAME)) $(addsuffix .cmi, $(NAME)) -TO_INSTALL=META $(addprefix _build/src/,$(LIB) $(TO_INSTALL_LIB)) - -install: lib - ocamlfind install $(NAME) $(TO_INSTALL) - if [ -d "$(NAME).docdir" ]; then \ - mkdir -p $(DOCDIR) ; \ - cp -v $(NAME).docdir/*.html $(NAME).docdir/*.css $(DOCDIR) ; \ - fi +install: build-install + jbuilder install uninstall: - ocamlfind remove $(NAME) - rm -rf $(DOCDIR) + jbuilder uninstall + +doc: + jbuilder build $(OPTS) @doc + reinstall: | uninstall install +ocp-indent: + @which ocp-indent > /dev/null || { \ + echo 'ocp-indent not found; please run `opam install ocp-indent`'; \ + exit 1 ; \ + } + +reindent: ocp-indent + @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 echo "reindenting: " + @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 ocp-indent -i + +WATCH=build-dev +watch: + while find src/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ + echo "============ at `date` ==========" ; \ + make $(WATCH); \ + done + .PHONY: clean doc all bench install uninstall remove reinstall enable_log disable_log bin test diff --git a/_tags b/_tags deleted file mode 100644 index 31793d6e..00000000 --- a/_tags +++ /dev/null @@ -1,35 +0,0 @@ -# colors -true: bin_annot, color(always) - -# optimization options -true: inline(100), optimize(3), unbox_closures, unbox_closures_factor(20) - -# Include paths -: include -: include -: include -: include -: include -: include -: include -: include - -# Pack options -: for-pack(Msat) -: for-pack(Msat) -: for-pack(Msat) -: for-pack(Msat) -: for-pack(Msat) -: for-pack(Msat) - -# Testing dependencies -: package(dolmen) -: package(dolmen) -: package(dolmen) -: package(dolmen) - -# more warnings -: warn_K, warn_Y, warn_X -: short_paths, safe_string, strict_sequence -: debug - diff --git a/opam b/msat.opam similarity index 66% rename from opam rename to msat.opam index a1ac6cd4..4131742c 100644 --- a/opam +++ b/msat.opam @@ -3,27 +3,26 @@ name: "msat" license: "Apache" version: "dev" author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] -maintainer: ["guillaume.bury@gmail.com" "simon.cruanes@inria.fr"] +maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ - [make "disable_log"] - [make "lib"] + [make "build"] ] build-doc: [ [make "doc"] ] install: [ - [make "DOCDIR=%{doc}%" "install"] + [make "install"] ] remove: [ - [make "DOCDIR=%{doc}%" "uninstall"] + [make "uninstall"] ] depends: [ "ocamlfind" {build} - "ocamlbuild" {build} + "jbuilder" {build} "dolmen" {test & = "dev" } ] available: [ - ocaml-version >= "4.00.1" + ocaml-version >= "4.03.0" ] tags: [ "sat" "smt" ] homepage: "https://github.com/Gbury/mSAT" diff --git a/msat_solver.opam b/msat_solver.opam new file mode 100644 index 00000000..8b090d0b --- /dev/null +++ b/msat_solver.opam @@ -0,0 +1,31 @@ +opam-version: "1.2" +name: "msat" +license: "Apache" +version: "dev" +author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] +maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] +build: [ + [make "build"] +] +build-doc: [ + [make "doc"] +] +install: [ + [make "install"] +] +remove: [ + [make "uninstall"] +] +depends: [ + "ocamlfind" {build} + "jbuilder" {build} + "dolmen" +] +available: [ + ocaml-version >= "4.03.0" +] +tags: [ "sat" "smt" ] +homepage: "https://github.com/Gbury/mSAT" +dev-repo: "https://github.com/Gbury/mSAT.git" +bug-reports: "https://github.com/Gbury/mSAT/issues/" + diff --git a/myocamlbuild.ml b/myocamlbuild.ml deleted file mode 100644 index 818b0017..00000000 --- a/myocamlbuild.ml +++ /dev/null @@ -1,16 +0,0 @@ - -(* This file is free software, part of mSAT. See file "LICENSE" for more information. *) - -open Ocamlbuild_plugin;; - -let doc_intro = "src/doc.txt";; - -dispatch begin function - | After_rules -> - (* Documentation index *) - dep ["ocaml"; "doc"; "extension:html"] & [doc_intro] ; - flag ["ocaml"; "doc"; "extension:html"] - & S [ A "-t"; A "mSAT doc"; A "-intro"; P doc_intro ]; - | _ -> () -end - diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index 51bd105c..efdbe8b3 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -2,6 +2,7 @@ MSAT is free software, using the Apache license, see file LICENSE Copyright 2015 Guillaume Bury *) +open Msat module type S = Backend_intf.S @@ -71,8 +72,8 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause let resolution fmt goal hyp1 hyp2 atom = let a = S.St.(atom.var.pa) in let h1, h2 = - if Array_util.exists ((==) a) hyp1.S.St.atoms then hyp1, hyp2 - else (assert (Array_util.exists ((==) a) hyp2.S.St.atoms); hyp2, hyp1) + if Array.exists ((==) a) hyp1.S.St.atoms then hyp1, hyp2 + else (assert (Array.exists ((==) a) hyp2.S.St.atoms); hyp2, hyp1) in (** Print some debug info *) Format.fprintf fmt diff --git a/src/backend/Coq.mli b/src/backend/Coq.mli index 61c42785..205319b6 100644 --- a/src/backend/Coq.mli +++ b/src/backend/Coq.mli @@ -8,6 +8,7 @@ Copyright 2015 Guillaume Bury This module provides an easy way to produce coq scripts corresponding to the resolution proofs output by the sat solver. *) +open Msat module type S = Backend_intf.S (** Interface for exporting proofs. *) diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml index fb1089e9..a4a842d1 100644 --- a/src/backend/Dedukti.ml +++ b/src/backend/Dedukti.ml @@ -3,6 +3,8 @@ MSAT is free software, using the Apache license, see file LICENSE Copyright 2015 Guillaume Bury *) +open Msat + module type S = Backend_intf.S module type Arg = sig diff --git a/src/backend/Dedukti.mli b/src/backend/Dedukti.mli index 449b82fb..e24deb9a 100644 --- a/src/backend/Dedukti.mli +++ b/src/backend/Dedukti.mli @@ -9,6 +9,8 @@ Copyright 2014 Simon Cruanes Work in progress... *) +open Msat + module type S = Backend_intf.S module type Arg = sig diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index 7afda874..239e8604 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -4,6 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) +open Msat + module type S = sig type clause diff --git a/src/backend/Dimacs.mli b/src/backend/Dimacs.mli index 0bb18a94..dcd11b47 100644 --- a/src/backend/Dimacs.mli +++ b/src/backend/Dimacs.mli @@ -10,6 +10,8 @@ Copyright 2014 Simon Cruanes iCNF formats. *) +open Msat + module type S = sig type clause diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 93981958..559b3fa0 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -4,6 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) +open Msat + (** Output interface for the backend *) module type S = Backend_intf.S diff --git a/src/backend/Dot.mli b/src/backend/Dot.mli index 59412b47..84cf048b 100644 --- a/src/backend/Dot.mli +++ b/src/backend/Dot.mli @@ -9,6 +9,7 @@ Copyright 2014 Simon Cruanes This module provides functions to export proofs into the dot graph format. Graphs in dot format can be used to generates images using the graphviz tool. *) +open Msat module type S = Backend_intf.S (** Interface for exporting proofs. *) diff --git a/src/backend/jbuild b/src/backend/jbuild new file mode 100644 index 00000000..aa2609ff --- /dev/null +++ b/src/backend/jbuild @@ -0,0 +1,13 @@ +; vim:ft=lisp: + +; main binary +(library + ((name msat_backend) + (public_name msat.backend) + (synopsis "proof backends for msat") + (libraries (msat)) + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + diff --git a/src/core/External.ml b/src/core/External.ml index fb5e03d1..e14e03a7 100644 --- a/src/core/External.ml +++ b/src/core/External.ml @@ -31,6 +31,11 @@ type ('clause, 'proof) unsat_state = ('clause, 'proof) Solver_intf.unsat_state = (** returns a persistent proof of the empty clause from the Unsat result. *) } +type 'clause export = 'clause Solver_intf.export = { + hyps: 'clause Vec.t; + history: 'clause Vec.t; + local: 'clause Vec.t; +} module Make (St : Solver_types.S) @@ -56,8 +61,9 @@ module Make let pp_all lvl status = Log.debugf lvl - "@[%s - Full resume:@,@[Trail:@\n%a@]@,@[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." - (fun k -> k status + (fun k -> k + "@[%s - Full resume:@,@[Trail:@\n%a@]@,@[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." + status (Vec.print ~sep:"" St.pp) (S.trail ()) (Vec.print ~sep:"" St.pp_clause) (S.temp ()) (Vec.print ~sep:"" St.pp_clause) (S.hyps ()) @@ -98,7 +104,7 @@ module Make let solve ?(assumptions=[]) () = try - S.pop (); + S.pop (); (* FIXME: what?! *) S.push (); S.local assumptions; S.solve (); @@ -119,19 +125,9 @@ module Make let new_lit = S.new_lit let new_atom = S.new_atom - (* Dimacs & iCNF export *) - module D = Dimacs.Make(St)(struct end) - - let export_dimacs fmt () = + let export () : St.clause export = let hyps = S.hyps () in let history = S.history () in let local = S.temp () in - D.export fmt ~hyps ~history ~local - - let export_icnf fmt () = - let hyps = S.hyps () in - let history = S.history () in - let local = S.temp () in - D.export_icnf fmt ~hyps ~history ~local - + {hyps; history; local} end diff --git a/src/core/Heap.ml b/src/core/Heap.ml new file mode 100644 index 00000000..a8f78d08 --- /dev/null +++ b/src/core/Heap.ml @@ -0,0 +1,155 @@ +(**************************************************************************) +(* *) +(* Cubicle *) +(* Combining model checking algorithms and SMT solvers *) +(* *) +(* Mohamed Iguernelala *) +(* Universite Paris-Sud 11 *) +(* *) +(* Copyright 2011. This file is distributed under the terms of the *) +(* Apache Software License version 2.0 *) +(* *) +(**************************************************************************) + +module type RANKED = Heap_intf.RANKED + +module type S = Heap_intf.S + +module Make(Elt : RANKED) = struct + type elt = Elt.t + + type t = { + heap : elt Vec.t; + } + + let _absent_index = -1 + + let create () = + { heap = Vec.make_empty Elt.dummy; } + + let[@inline] left i = (i lsl 1) + 1 (* i*2 + 1 *) + let[@inline] right i = (i + 1) lsl 1 (* (i+1)*2 *) + let[@inline] parent i = (i - 1) asr 1 (* (i-1) / 2 *) + + (* + let rec heap_property cmp ({heap=heap} as s) i = + i >= (Vec.size heap) || + ((i = 0 || not(cmp (Vec. get heap i) (Vec.get heap (parent i)))) + && heap_property cmp s (left i) && heap_property cmp s (right i)) + + let heap_property cmp s = heap_property cmp s 1 + *) + + (* [elt] is above or at its expected position. Move it up the heap + (towards high indices) to restore the heap property *) + let percolate_up {heap} (elt:Elt.t) : unit = + let pi = ref (parent (Elt.idx elt)) in + let i = ref (Elt.idx elt) in + while !i <> 0 && Elt.cmp elt (Vec.get heap !pi) do + Vec.set heap !i (Vec.get heap !pi); + Elt.set_idx (Vec.get heap !i) !i; + i := !pi; + pi := parent !i + done; + Vec.set heap !i elt; + Elt.set_idx elt !i + + let percolate_down {heap} (elt:Elt.t): unit = + let sz = Vec.size heap in + let li = ref (left (Elt.idx elt)) in + let ri = ref (right (Elt.idx elt)) in + let i = ref (Elt.idx elt) in + begin + try + while !li < sz do + let child = + if !ri < sz && Elt.cmp (Vec.get heap !ri) (Vec.get heap !li) + then !ri + else !li + in + if not (Elt.cmp (Vec.get heap child) elt) then raise Exit; + Vec.set heap !i (Vec.get heap child); + Elt.set_idx (Vec.get heap !i) !i; + i := child; + li := left !i; + ri := right !i + done; + with Exit -> () + end; + Vec.set heap !i elt; + Elt.set_idx elt !i + + let[@inline] in_heap x = Elt.idx x >= 0 + + let[@inline] decrease s x = assert (in_heap x); percolate_up s x + + (* + let increase cmp s n = + assert (in_heap s n); percolate_down cmp s (V.get s.indices n) + *) + + let filter s filt = + let j = ref 0 in + let lim = Vec.size s.heap in + for i = 0 to lim - 1 do + if filt (Vec.get s.heap i) then ( + Vec.set s.heap !j (Vec.get s.heap i); + Elt.set_idx (Vec.get s.heap i) !j; + incr j; + ) else ( + Elt.set_idx (Vec.get s.heap i) _absent_index; + ); + done; + Vec.shrink s.heap (lim - !j); + for i = (lim / 2) - 1 downto 0 do + percolate_down s (Vec.get s.heap i) + done + + let size s = Vec.size s.heap + + let is_empty s = Vec.is_empty s.heap + + let clear {heap} = + Vec.iter (fun e -> Elt.set_idx e _absent_index) heap; + Vec.clear heap; + () + + let insert s elt = + if not (in_heap elt) then ( + Elt.set_idx elt (Vec.size s.heap); + Vec.push s.heap elt; + percolate_up s elt; + ) + + let[@inline] grow_to_at_least s sz = + Vec.grow_to_at_least s.heap sz + + (* + let update cmp s n = + assert (heap_property cmp s); + begin + if in_heap s n then + begin + percolate_up cmp s (Vec.get s.indices n); + percolate_down cmp s (Vec.get s.indices n) + end + else insert cmp s n + end; + assert (heap_property cmp s) + *) + + let remove_min ({heap} as s) = + if Vec.size heap=0 then raise Not_found; + let x = Vec.get heap 0 in + Elt.set_idx x _absent_index; + let new_hd = Vec.last heap in (* heap.last() *) + Vec.set heap 0 new_hd; + Elt.set_idx new_hd 0; + Vec.pop heap; (* remove last *) + (* enforce heap property again *) + if Vec.size heap > 1 then ( + percolate_down s new_hd; + ); + x + +end diff --git a/src/core/Heap.mli b/src/core/Heap.mli new file mode 100644 index 00000000..24c47bd6 --- /dev/null +++ b/src/core/Heap.mli @@ -0,0 +1,18 @@ +(**************************************************************************) +(* *) +(* Cubicle *) +(* Combining model checking algorithms and SMT solvers *) +(* *) +(* Mohamed Iguernelala *) +(* Universite Paris-Sud 11 *) +(* *) +(* Copyright 2011. This file is distributed under the terms of the *) +(* Apache Software License version 2.0 *) +(* *) +(**************************************************************************) + +module type RANKED = Heap_intf.RANKED + +module type S = Heap_intf.S + +module Make(X : RANKED) : S with type elt = X.t diff --git a/src/core/Heap_intf.ml b/src/core/Heap_intf.ml new file mode 100644 index 00000000..a5d11100 --- /dev/null +++ b/src/core/Heap_intf.ml @@ -0,0 +1,51 @@ + +module type RANKED = sig + type t + val idx: t -> int (** Index in heap. return -1 if never set *) + val set_idx : t -> int -> unit (** Update index in heap *) + val dummy : t + val cmp : t -> t -> bool +end + +module type S = sig + type elt + (** Type of elements *) + + type t + (** Heap of {!elt}, whose priority is increased or decreased + incrementally (see {!decrease} for instance) *) + + val create : unit -> t + (** Create a heap *) + + val decrease : t -> elt -> unit + (** [decrease h x] decreases the value associated to [x] within [h] *) + + val in_heap : elt -> bool + + (*val increase : (int -> int -> bool) -> t -> int -> unit*) + + val size : t -> int + (** Number of integers within the heap *) + + val is_empty : t -> bool + + val clear : t -> unit + (** Clear the content of the heap *) + + val insert : t -> elt -> unit + (** Insert a new element into the heap *) + + val grow_to_at_least: t -> int -> unit + (** Hint: augment the internal capacity of the heap until it reaches at + least the given integer *) + + (*val update : (int -> int -> bool) -> t -> int -> unit*) + + val remove_min : t -> elt + (** Remove and return the integer that has the lowest value from the heap + @raise Not_found if the heap is empty *) + + val filter : t -> (elt -> bool) -> unit + (** Filter out values that don't satisfy the predicate *) +end diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 2583c301..f2227514 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -11,11 +11,18 @@ module Make and type proof = St.proof) (Dummy: sig end) = struct - module Proof = Res.Make(St) open St + module H = Heap.Make(struct + type t = St.elt + let[@inline] cmp i j = get_elt_weight j < get_elt_weight i (* comparison by weight *) + let dummy = elt_of_var St.dummy_var + let idx = get_elt_idx + let set_idx = set_elt_idx + end) + exception Sat exception Unsat exception UndecidedLit @@ -89,7 +96,7 @@ module Make (* number of toplevel assignments since last call to [simplify ()] *) - order : Iheap.t; + order : H.t; (* Heap ordered by variable activity *) var_decay : float; @@ -147,7 +154,7 @@ module Make th_levels = Vec.make 100 Plugin.dummy; user_levels = Vec.make 10 (-1); - order = Iheap.init 0; + order = H.create(); var_incr = 1.; clause_incr = 1.; @@ -183,9 +190,6 @@ module Make let decision_level () = Vec.size env.elt_levels let base_level () = Vec.size env.user_levels - let f_weight i j = - get_elt_weight (St.get_elt j) < get_elt_weight (St.get_elt i) - (* Are the assumptions currently unsat ? *) let is_unsat () = match env.unsat_conflict with @@ -226,14 +230,14 @@ module Make When we add a variable (which wraps a formula), we also need to add all its subterms. *) - let rec insert_var_order = function - | E_lit l -> - Iheap.insert f_weight env.order l.lid - | E_var v -> - Iheap.insert f_weight env.order v.vid; - insert_subterms_order v + let rec insert_var_order (elt:elt) : unit = + H.insert env.order elt; + begin match elt with + | E_lit _ -> () + | E_var v -> insert_subterms_order v + end - and insert_subterms_order v = + and insert_subterms_order (v:St.var) : unit = iter_sub (fun t -> insert_var_order (elt_of_lit t)) v (* Add new litterals/atoms on which to decide on, even if there is no @@ -269,8 +273,10 @@ module Make done; env.var_incr <- env.var_incr *. 1e-100; end; - if Iheap.in_heap env.order v.vid then - Iheap.decrease f_weight env.order v.vid + let elt = elt_of_var v in + if H.in_heap elt then ( + H.decrease env.order elt + ) (* increase activity of literal [l] *) let lit_bump_activity_aux (l:lit): unit = @@ -281,8 +287,10 @@ module Make done; env.var_incr <- env.var_incr *. 1e-100; end; - if Iheap.in_heap env.order l.lid then - Iheap.decrease f_weight env.order l.lid + let elt = elt_of_lit l in + if H.in_heap elt then ( + H.decrease env.order elt + ) (* increase activity of var [v] *) let var_bump_activity (v:var): unit = @@ -408,7 +416,7 @@ module Make *) let attach_clause c = assert (not c.attached); - Log.debugf debug "Attaching %a" (fun k -> k St.pp_clause c); + Log.debugf debug (fun k -> k "Attaching %a" St.pp_clause c); Array.iter (fun a -> a.var.used <- a.var.used + 1) c.atoms; Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; @@ -423,9 +431,9 @@ module Make assert (lvl >= base_level ()); (* Nothing to do if we try to backtrack to a non-existent level. *) if decision_level () <= lvl then - Log.debugf debug "Already at level <= %d" (fun k -> k lvl) + Log.debugf debug (fun k -> k "Already at level <= %d" lvl) else begin - Log.debugf info "Backtracking to lvl %d" (fun k -> k lvl); + Log.debugf info (fun k -> k "Backtracking to lvl %d" lvl); (* We set the head of the solver and theory queue to what it was. *) let head = ref (Vec.get env.elt_levels lvl) in env.elt_head <- !head; @@ -468,17 +476,17 @@ module Make (* Recover the right theory state. *) Plugin.backtrack (Vec.get env.th_levels lvl); (* Resize the vectors according to their new size. *) - Vec.shrink env.elt_queue ((Vec.size env.elt_queue) - !head); - Vec.shrink env.elt_levels ((Vec.size env.elt_levels) - lvl); - Vec.shrink env.th_levels ((Vec.size env.th_levels) - lvl); + Vec.shrink env.elt_queue !head; + Vec.shrink env.elt_levels lvl; + Vec.shrink env.th_levels lvl; end; assert (Vec.size env.elt_levels = Vec.size env.th_levels); () (* Unsatisfiability is signaled through an exception, since it can happen in multiple places (adding new clauses, or solving for instance). *) - let report_unsat ({atoms=atoms} as confl) : _ = - Log.debugf info "@[Unsat conflict: %a@]" (fun k -> k St.pp_clause confl); + let report_unsat confl : _ = + Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" St.pp_clause confl); env.unsat_conflict <- Some confl; raise Unsat @@ -492,25 +500,27 @@ module Make | (Bcp cl) as r -> let l, history = partition cl.atoms in begin match l with - | [ a ] -> - if history = [] then r - (* no simplification has been done, so [cl] is actually a clause with only - [a], so it is a valid reason for propagating [a]. *) - else begin + | [_] -> + if history = [] then ( + (* no simplification has been done, so [cl] is actually a clause with only + [a], so it is a valid reason for propagating [a]. *) + r + ) else ( (* Clauses in [history] have been used to simplify [cl] into a clause [tmp_cl] with only one formula (which is [a]). So we explicitly create that clause and set it as the cause for the propagation of [a], that way we can rebuild the whole resolution tree when we want to prove [a]. *) let c' = make_clause (fresh_lname ()) l (History (cl :: history)) in - Log.debugf debug "Simplified reason: @[%a@,%a@]" - (fun k -> k St.pp_clause cl St.pp_clause c'); + Log.debugf debug + (fun k -> k "Simplified reason: @[%a@,%a@]" St.pp_clause cl St.pp_clause c'); Bcp c' - end + ) | _ -> - Log.debugf error "@[Failed at reason simplification:@,%a@,%a@]" + Log.debugf error (fun k -> - k (Vec.print ~sep:"" St.pp_atom) - (Vec.from_list l (List.length l) St.dummy_atom) + k "@[Failed at reason simplification:@,%a@,%a@]" + (Vec.print ~sep:"" St.pp_atom) + (Vec.from_list l St.dummy_atom) St.pp_clause cl); assert false end @@ -520,7 +530,7 @@ module Make Wrapper function for adding a new propagated formula. *) let enqueue_bool a ~level:lvl reason : unit = if a.neg.is_true then begin - Log.debugf error "Trying to enqueue a false literal: %a" (fun k->k St.pp_atom a); + Log.debugf error (fun k->k "Trying to enqueue a false literal: %a" St.pp_atom a); assert false end; assert (not a.is_true && a.var.v_level < 0 && @@ -533,8 +543,8 @@ module Make a.var.v_level <- lvl; a.var.reason <- Some reason; Vec.push env.elt_queue (of_atom a); - Log.debugf debug "Enqueue (%d): %a" - (fun k->k (Vec.size env.elt_queue) pp_atom a) + Log.debugf debug + (fun k->k "Enqueue (%d): %a" (Vec.size env.elt_queue) pp_atom a) let enqueue_semantic a terms = if a.is_true then () @@ -542,24 +552,24 @@ module Make let l = List.map St.add_term terms in let lvl = List.fold_left (fun acc {l_level; _} -> assert (l_level > 0); max acc l_level) 0 l in - Iheap.grow_to_at_least env.order (St.nb_elt ()); - enqueue_bool a lvl Semantic + H.grow_to_at_least env.order (St.nb_elt ()); + enqueue_bool a ~level:lvl Semantic end (* MCsat semantic assignment *) let enqueue_assign l value lvl = match l.assigned with | Some _ -> - Log.debugf error "Trying to assign an already assigned literal: %a" - (fun k -> k St.pp_lit l); + Log.debugf error + (fun k -> k "Trying to assign an already assigned literal: %a" St.pp_lit l); assert false | None -> assert (l.l_level < 0); l.assigned <- Some value; l.l_level <- lvl; Vec.push env.elt_queue (of_lit l); - Log.debugf debug "Enqueue (%d): %a" - (fun k -> k (Vec.size env.elt_queue) pp_lit l) + Log.debugf debug + (fun k -> k "Enqueue (%d): %a" (Vec.size env.elt_queue) pp_lit l) (* evaluate an atom for MCsat, if it's not assigned by boolean propagation/decision *) @@ -583,16 +593,16 @@ module Make let backtrack_lvl : atom list -> int * bool = function | [] | [_] -> 0, true - | a :: b :: r -> + | a :: b :: _ -> assert(a.var.v_level > base_level ()); - if a.var.v_level > b.var.v_level then begin + if a.var.v_level > b.var.v_level then ( (* backtrack below [a], so we can propagate [not a] *) b.var.v_level, true - end else begin + ) else ( assert (a.var.v_level = b.var.v_level); assert (a.var.v_level >= base_level ()); max (a.var.v_level - 1) (base_level ()), false - end + ) (* result of conflict analysis, containing the learnt clause and some additional info. @@ -628,14 +638,14 @@ module Make let conflict_level = Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in - Log.debugf debug "Analyzing conflict (%d): %a" - (fun k -> k conflict_level St.pp_clause c_clause); + Log.debugf debug + (fun k -> k "Analyzing conflict (%d): %a" conflict_level St.pp_clause c_clause); while !cond do begin match !c with | None -> - Log.debugf debug " skipping resolution for semantic propagation" (fun k->k) + Log.debug debug " skipping resolution for semantic propagation" | Some clause -> - Log.debugf debug " Resolving clause: %a" (fun k->k St.pp_clause clause); + Log.debugf debug (fun k->k" Resolving clause: %a" St.pp_clause clause); begin match clause.cpremise with | History _ -> clause_bump_activity clause | Hyp | Local | Lemma _ -> () @@ -670,7 +680,7 @@ module Make (* look for the next node to expand *) while let a = Vec.get env.elt_queue !tr_ind in - Log.debugf debug " looking at: %a" (fun k -> k St.pp a); + Log.debugf debug (fun k -> k " looking at: %a" St.pp a); match a with | Atom q -> (not (q.var.seen = Both)) || @@ -694,7 +704,7 @@ module Make assert (n > 0); assert (p.var.v_level >= conflict_level); c := Some cl - | n, _ -> assert false + | _ -> assert false done; List.iter (fun q -> clear q.var) !seen; let l = List.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) !learnt in @@ -749,12 +759,12 @@ module Make - report unsat if conflict at level 0 *) let add_boolean_conflict (confl:clause): unit = - Log.debugf info "Boolean conflict: %a" (fun k -> k St.pp_clause confl); + Log.debugf info (fun k -> k "Boolean conflict: %a" St.pp_clause confl); env.next_decision <- None; env.conflicts <- env.conflicts + 1; assert (decision_level() >= base_level ()); if decision_level() = base_level () - || Array_util.for_all (fun a -> a.var.v_level <= base_level ()) confl.atoms then + || Array.for_all (fun a -> a.var.v_level <= base_level ()) confl.atoms then report_unsat confl; (* Top-level conflict *) let cr = analyze confl in cancel_until (max cr.cr_backtrack_lvl (base_level ())); @@ -770,14 +780,14 @@ module Make (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) let add_clause (init:clause) : unit = - Log.debugf debug "Adding clause: @[%a@]" (fun k -> k St.pp_clause init); + Log.debugf debug (fun k -> k "Adding clause: @[%a@]" St.pp_clause init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) Array.iter (fun x -> insert_var_order (elt_of_var x.var)) init.atoms; let vec = clause_vector init in try let c = eliminate_doublons init in - Log.debugf debug "Doublons eliminated: %a" (fun k -> k St.pp_clause c); + Log.debugf debug (fun k -> k "Doublons eliminated: %a" St.pp_clause c); let atoms, history = partition c.atoms in let clause = if history = [] @@ -788,7 +798,7 @@ module Make ) else make_clause (fresh_name ()) atoms (History (c :: history)) in - Log.debugf info "New clause: @[%a@]" (fun k->k St.pp_clause clause); + Log.debugf info (fun k->k "New clause: @[%a@]" St.pp_clause clause); match atoms with | [] -> (* Report_unsat will raise, and the current clause will be lost if we do not @@ -802,7 +812,7 @@ module Make (* Since we cannot propagate the atom [a], in order to not lose the information that [a] must be true, we add clause to the list of clauses to add, so that it will be e-examined later. *) - Log.debugf debug "Unit clause, adding to clauses to add" (fun k -> k); + Log.debug debug "Unit clause, adding to clauses to add"; Stack.push clause env.clauses_to_add; report_unsat clause end else if a.is_true then begin @@ -810,12 +820,12 @@ module Make However it means we can't propagate it at level 0. In order to not lose that information, we store the clause in a stack of clauses that we will add to the solver at the next pop. *) - Log.debugf debug "Unit clause, adding to root clauses" (fun k -> k); + Log.debug debug "Unit clause, adding to root clauses"; assert (0 < a.var.v_level && a.var.v_level <= base_level ()); Stack.push clause env.clauses_root; () end else begin - Log.debugf debug "Unit clause, propagating: %a" (fun k->k St.pp_atom a); + Log.debugf debug (fun k->k "Unit clause, propagating: %a" St.pp_atom a); Vec.push vec clause; enqueue_bool a ~level:0 (Bcp clause) end @@ -834,18 +844,18 @@ module Make if b.neg.is_true && not a.is_true && not a.neg.is_true then begin let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in cancel_until (max lvl (base_level ())); - enqueue_bool a lvl (Bcp clause) + enqueue_bool a ~level:lvl (Bcp clause) end end with Trivial -> Vec.push vec init; - Log.debugf info "Trivial clause ignored : @[%a@]" (fun k->k St.pp_clause init) + Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" St.pp_clause init) let flush_clauses () = if not (Stack.is_empty env.clauses_to_add) then begin let nbv = St.nb_elt () in let nbc = env.nb_init_clauses + Stack.length env.clauses_to_add in - Iheap.grow_to_at_least env.order nbv; + H.grow_to_at_least env.order nbv; Vec.grow_to_at_least env.clauses_hyps nbc; Vec.grow_to_at_least env.clauses_learnt nbc; env.nb_init_clauses <- nbc; @@ -898,7 +908,7 @@ module Make end else begin match th_eval first with | None -> (* clause is unit, keep the same watches, but propagate *) - enqueue_bool first (decision_level ()) (Bcp c) + enqueue_bool first ~level:(decision_level ()) (Bcp c) | Some true -> () | Some false -> env.elt_head <- Vec.size env.elt_queue; @@ -946,14 +956,14 @@ module Make match Vec.get env.elt_queue i with | Atom a -> Plugin_intf.Lit a.lit - | Lit {term; assigned = Some v} -> + | Lit {term; assigned = Some v; _} -> Plugin_intf.Assign (term, v) | Lit _ -> assert false let slice_push (l:formula list) (lemma:proof): unit = let atoms = List.rev_map create_atom l in let c = make_clause (fresh_tname ()) atoms (Lemma lemma) in - Log.debugf info "Pushing clause %a" (fun k->k St.pp_clause c); + Log.debugf info (fun k->k "Pushing clause %a" St.pp_clause c); Stack.push c env.clauses_to_add let slice_propagate f = function @@ -970,10 +980,10 @@ module Make else if p.neg.is_true then Stack.push c env.clauses_to_add else begin - Iheap.grow_to_at_least env.order (St.nb_elt ()); + H.grow_to_at_least env.order (St.nb_elt ()); insert_subterms_order p.var; let lvl = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in - enqueue_bool p lvl (Bcp c) + enqueue_bool p ~level:lvl (Bcp c) end else raise (Invalid_argument "Msat.Internal.slice_propagate") @@ -1013,10 +1023,11 @@ module Make (* conflict *) let l = List.rev_map create_atom l in (* Assert that the conflcit is indeeed a conflict *) - if not @@ List.for_all (fun a -> a.neg.is_true) l then + if not @@ List.for_all (fun a -> a.neg.is_true) l then ( raise (Invalid_argument "msat:core/internal: invalid conflict"); + ); (* Insert elements for decision (and ensure the heap is big enough) *) - Iheap.grow_to_at_least env.order (St.nb_elt ()); + H.grow_to_at_least env.order (St.nb_elt ()); List.iter (fun a -> insert_var_order (elt_of_var a.var)) l; (* Create the clause and return it. *) let c = St.make_clause (St.fresh_tname ()) l (Lemma p) in @@ -1067,7 +1078,7 @@ module Make env.decisions <- env.decisions + 1; new_decision_level(); let current_level = decision_level () in - enqueue_bool atom current_level Decision + enqueue_bool atom ~level:current_level Decision | Plugin_intf.Valued (b, l) -> let a = if b then atom else atom.neg in enqueue_semantic a l @@ -1079,7 +1090,7 @@ module Make pick_branch_aux atom | None -> begin try - begin match St.get_elt (Iheap.remove_min f_weight env.order) with + begin match H.remove_min env.order with | E_lit l -> if l.l_level >= 0 then pick_branch_lit () @@ -1120,7 +1131,7 @@ module Make if Vec.size env.elt_queue = St.nb_elt () then raise Sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then begin - Log.debugf info "Restarting..." (fun k -> k); + Log.debug info "Restarting..."; cancel_until (base_level ()); raise Restart end; @@ -1179,7 +1190,7 @@ module Make | Plugin_intf.Unsat (l, p) -> let atoms = List.rev_map create_atom l in let c = make_clause (fresh_tname ()) atoms (Lemma p) in - Log.debugf info "Theory conflict clause: %a" (fun k -> k St.pp_clause c); + Log.debugf info (fun k -> k "Theory conflict clause: %a" St.pp_clause c); Stack.push c env.clauses_to_add end; if Stack.is_empty env.clauses_to_add then raise Sat @@ -1192,26 +1203,27 @@ module Make (fun l -> let atoms = List.rev_map atom l in let c = make_clause ?tag (fresh_hname ()) atoms Hyp in - Log.debugf debug "Assuming clause: @[%a@]" (fun k -> k pp_clause c); + Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" pp_clause c); Stack.push c env.clauses_to_add) cnf (* create a factice decision level for local assumptions *) let push (): unit = - Log.debugf debug "Pushing a new user level" (fun k -> k); + Log.debug debug "Pushing a new user level"; match env.unsat_conflict with - | Some confl -> raise Unsat + | Some _ -> raise Unsat | None -> cancel_until (base_level ()); - Log.debugf debug "@[Status:@,@[trail: %d - %d@]@,%a@]" - (fun k -> k env.elt_head env.th_head (Vec.print ~sep:"" St.pp) env.elt_queue); + Log.debugf debug + (fun k -> k "@[Status:@,@[trail: %d - %d@,%a@]" + env.elt_head env.th_head (Vec.print ~sep:"" St.pp) env.elt_queue); begin match propagate () with | Some confl -> report_unsat confl | None -> - Log.debugf debug "@[Current trail:@,@[%a@]@]" - (fun k -> k (Vec.print ~sep:"" St.pp) env.elt_queue); - Log.debugf info "Creating new user level" (fun k -> k); + Log.debugf debug + (fun k -> k "@[Current trail:@,@[%a@]@]" (Vec.print ~sep:"" St.pp) env.elt_queue); + Log.debug info "Creating new user level"; new_decision_level (); Vec.push env.user_levels (Vec.size env.clauses_temp); assert (decision_level () = base_level ()) @@ -1220,9 +1232,9 @@ module Make (* pop the last factice decision level *) let pop (): unit = if base_level () = 0 then - Log.debugf warn "Cannot pop (already at user level 0)" (fun k -> k) - else begin - Log.debugf info "Popping user level" (fun k -> k); + Log.debug warn "Cannot pop (already at level 0)" + else ( + Log.debug info "Popping user level"; assert (base_level () > 0); env.unsat_conflict <- None; let n = Vec.last env.user_levels in @@ -1231,22 +1243,22 @@ module Make Stack.iter (fun c -> Stack.push c env.clauses_to_add) env.clauses_root; Stack.clear env.clauses_root; (* remove from env.clauses_temp the now invalid caluses. *) - Vec.shrink env.clauses_temp (Vec.size env.clauses_temp - n); + Vec.shrink env.clauses_temp n; assert (Vec.for_all (fun c -> Array.length c.atoms = 1) env.clauses_temp); assert (Vec.for_all (fun c -> c.atoms.(0).var.v_level <= base_level ()) env.clauses_temp); cancel_until (base_level ()) - end + ) (* Add local hyps to the current decision level *) let local l = let aux lit = let a = atom lit in - Log.debugf info "Local assumption: @[%a@]" (fun k-> k pp_atom a); + Log.debugf info (fun k-> k "Local assumption: @[%a@]" pp_atom a); assert (decision_level () = base_level ()); if a.is_true then () else let c = make_clause (fresh_hname ()) [a] Local in - Log.debugf debug "Temp clause: @[%a@]" (fun k -> k pp_clause c); + Log.debugf debug (fun k -> k "Temp clause: @[%a@]" pp_clause c); Vec.push env.clauses_temp c; if a.neg.is_true then begin (* conflict between assumptions: UNSAT *) @@ -1254,7 +1266,7 @@ module Make end else begin (* Grow the heap, because when the lit is backtracked, it will be added to the heap. *) - Iheap.grow_to_at_least env.order (St.nb_elt ()); + H.grow_to_at_least env.order (St.nb_elt ()); (* make a decision, propagate *) let level = decision_level() in enqueue_bool a ~level (Bcp c); @@ -1263,11 +1275,11 @@ module Make assert (base_level () > 0); match env.unsat_conflict with | None -> - Log.debugf info "Adding local assumption" (fun k -> k); + Log.debug info "Adding local assumption"; cancel_until (base_level ()); List.iter aux l | Some _ -> - Log.debugf warn "Cannot add local assumption (already unsat)" (fun k -> k) + Log.debug warn "Cannot add local assumption (already unsat)" (* Check satisfiability *) let check_clause c = @@ -1275,10 +1287,10 @@ module Make if a.is_true then true else if a.neg.is_true then false else raise UndecidedLit) c.atoms in - let res = Array_util.exists (fun x -> x) tmp in + let res = Array.exists (fun x -> x) tmp in if not res then begin - Log.debugf debug "Clause not satisfied: @[%a@]" - (fun k -> k St.pp_clause c); + Log.debugf debug + (fun k -> k "Clause not satisfied: @[%a@]" St.pp_clause c); false end else true diff --git a/src/core/Res.ml b/src/core/Res.ml index d0fa44e4..f29dc568 100644 --- a/src/core/Res.ml +++ b/src/core/Res.ml @@ -117,31 +117,30 @@ module Make(St : Solver_types.S) = struct assert St.(a.var.v_level >= 0); match St.(a.var.reason) with | Some St.Bcp c -> - Log.debugf debug "Analysing: @[%a@ %a@]" - (fun k -> k St.pp_atom a St.pp_clause c); + Log.debugf debug (fun k->k "Analysing: @[%a@ %a@]" St.pp_atom a St.pp_clause c); if Array.length c.St.atoms = 1 then begin - Log.debugf debug "Old reason: @[%a@]" (fun k -> k St.pp_atom a); + Log.debugf debug (fun k -> k "Old reason: @[%a@]" St.pp_atom a); c end else begin assert (a.St.neg.St.is_true); let r = St.History (c :: (Array.fold_left aux [] c.St.atoms)) in let c' = St.make_clause (fresh_pcl_name ()) [a.St.neg] r in a.St.var.St.reason <- Some St.(Bcp c'); - Log.debugf debug "New reason: @[%a@ %a@]" - (fun k -> k St.pp_atom a St.pp_clause c'); + Log.debugf debug + (fun k -> k "New reason: @[%a@ %a@]" St.pp_atom a St.pp_clause c'); c' end | _ -> - Log.debugf error "Error while proving atom %a" (fun k -> k St.pp_atom a); + Log.debugf error (fun k -> k "Error while proving atom %a" St.pp_atom a); raise (Resolution_error "Cannot prove atom") let prove_unsat conflict = if Array.length conflict.St.atoms = 0 then conflict else begin - Log.debugf info "Proving unsat from: @[%a@]" (fun k -> k St.pp_clause conflict); + Log.debugf info (fun k -> k "Proving unsat from: @[%a@]" St.pp_clause conflict); let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.St.atoms in let res = St.make_clause (fresh_pcl_name ()) [] (St.History (conflict :: l)) in - Log.debugf info "Proof found: @[%a@]" (fun k -> k St.pp_clause res); + Log.debugf info (fun k -> k "Proof found: @[%a@]" St.pp_clause res); res end @@ -166,8 +165,8 @@ module Make(St : Solver_types.S) = struct let rec chain_res (c, cl) = function | d :: r -> - Log.debugf debug " Resolving clauses : @[%a@\n%a@]" - (fun k -> k St.pp_clause c St.pp_clause d); + Log.debugf debug + (fun k -> k " Resolving clauses : @[%a@\n%a@]" St.pp_clause c St.pp_clause d); let dl = to_list d in begin match resolve (merge cl dl) with | [ a ], l -> @@ -179,15 +178,15 @@ module Make(St : Solver_types.S) = struct chain_res (new_clause, l) r end | _ -> - Log.debugf error "While resolving clauses:@[%a@\n%a@]" - (fun k -> k St.pp_clause c St.pp_clause d); + Log.debugf error + (fun k -> k "While resolving clauses:@[%a@\n%a@]" St.pp_clause c St.pp_clause d); raise (Resolution_error "Clause mismatch") end | _ -> raise (Resolution_error "Bad history") let expand conclusion = - Log.debugf debug "Expanding : @[%a@]" (fun k -> k St.pp_clause conclusion); + Log.debugf debug (fun k -> k "Expanding : @[%a@]" St.pp_clause conclusion); match conclusion.St.cpremise with | St.Lemma l -> {conclusion; step = Lemma l; } @@ -196,7 +195,7 @@ module Make(St : Solver_types.S) = struct | St.Local -> { conclusion; step = Assumption; } | St.History [] -> - Log.debugf error "Empty history for clause: %a" (fun k -> k St.pp_clause conclusion); + Log.debugf error (fun k -> k "Empty history for clause: %a" St.pp_clause conclusion); raise (Resolution_error "Empty history") | St.History [ c ] -> let duplicates, res = analyze (list c) in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 2852aadc..39e47ef9 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -38,14 +38,22 @@ type ('clause, 'proof) unsat_state = { } (** The type of values returned when the solver reaches an UNSAT state. *) +type 'clause export = { + hyps: 'clause Vec.t; + history: 'clause Vec.t; + local: 'clause Vec.t; +} +(** Export internal state *) + (** The external interface implemented by safe solvers, such as the one created by the {!Solver.Make} and {!Mcsolver.Make} functors. *) module type S = sig - (** {2 Internal modules} These are the internal modules used, you should probably not use them if you're not familiar with the internals of mSAT. *) + (* TODO: replace {!St} with explicit modules (Expr, Var, Lit, Elt,...) + with carefully picked interfaces *) module St : Solver_types.S (** WARNING: Very dangerous module that expose the internal representation used by the solver. *) @@ -96,14 +104,6 @@ module type S = sig val get_tag : St.clause -> int option (** Recover tag from a clause, if any *) - val export_dimacs : Format.formatter -> unit -> unit - (** Prints the entire set of clauses in the input problem - (including hypothesis, lemmas and local assumptions), - in the dimacs format. *) - - val export_icnf : Format.formatter -> unit -> unit - (** Export the current problem contents to iCNF format. - This function is meant to be used icnrementally, i.e. - called for each return value of the solve function. *) + val export : unit -> St.clause export end diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 1edbfbd3..1fd4b7b7 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -40,6 +40,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct lid : int; term : term; mutable l_level : int; + mutable l_idx: int; mutable l_weight : float; mutable assigned : term option; } @@ -51,7 +52,8 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct mutable used : int; mutable seen : seen; mutable v_level : int; - mutable v_weight : float; + mutable v_idx: int; (** position in heap *) + mutable v_weight : float; (** Weight (for the heap), tracking activity *) mutable v_assignable: lit list option; mutable reason : reason option; } @@ -101,6 +103,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct seen = Nope; v_level = -1; v_weight = -1.; + v_idx= -1; v_assignable = None; reason = None; } @@ -146,6 +149,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct lid = !cpt_mk_var; term = t; l_weight = 1.; + l_idx= -1; l_level = -1; assigned = None; } in @@ -168,6 +172,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct used = 0; seen = Nope; v_level = -1; + v_idx= -1; v_weight = 0.; v_assignable = None; reason = None; @@ -244,18 +249,22 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct let of_atom a = Atom a (* Elements *) - let elt_of_lit l = E_lit l - let elt_of_var v = E_var v + let[@inline] elt_of_lit l = E_lit l + let[@inline] elt_of_var v = E_var v let get_elt_id = function | E_lit l -> l.lid | E_var v -> v.vid let get_elt_level = function | E_lit l -> l.l_level | E_var v -> v.v_level + let get_elt_idx = function + | E_lit l -> l.l_idx | E_var v -> v.v_idx let get_elt_weight = function | E_lit l -> l.l_weight | E_var v -> v.v_weight let set_elt_level e lvl = match e with | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl + let set_elt_idx e i = match e with + | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i let set_elt_weight e w = match e with | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w @@ -346,11 +355,11 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct let pp_atoms_vec out vec = Array.iter (fun a -> Format.fprintf out "%a@ " pp_atom a) vec - let pp_clause out {name=name; atoms=arr; cpremise=cp; } = + let pp_clause out {name=name; atoms=arr; cpremise=cp;_} = Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" name pp_atoms_vec arr pp_premise cp - let pp_dimacs fmt { atoms; } = + let pp_dimacs fmt {atoms;_} = let aux fmt a = Array.iter (fun p -> Format.fprintf fmt "%s%d " diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index e9832e14..24022f7d 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -45,6 +45,7 @@ module type S = sig lid : int; (** Unique identifier *) term : term; (** Wrapped term *) mutable l_level : int; (** Decision level of the assignment *) + mutable l_idx: int; (** index in heap *) mutable l_weight : float; (** Weight (for the heap) *) mutable assigned : term option; (** Assignment *) } @@ -57,6 +58,7 @@ module type S = sig mutable used : int; (** Number of attached clause that contain the var *) mutable seen : seen; (** Boolean used during propagation *) mutable v_level : int; (** Level of decision/propagation *) + mutable v_idx: int; (** rank in variable heap *) mutable v_weight : float; (** Variable weight (for the heap) *) mutable v_assignable: lit list option; (** In mcsat, the list of lits that wraps subterms of the formula wrapped. *) @@ -144,8 +146,10 @@ module type S = sig val get_elt_id : elt -> int val get_elt_level : elt -> int + val get_elt_idx : elt -> int val get_elt_weight : elt -> float val set_elt_level : elt -> int -> unit + val set_elt_idx : elt -> int -> unit val set_elt_weight : elt -> float -> unit (** Accessors for elements *) diff --git a/src/util/vec.ml b/src/core/Vec.ml similarity index 77% rename from src/util/vec.ml rename to src/core/Vec.ml index 8a3c82b5..c8c68f84 100644 --- a/src/util/vec.ml +++ b/src/core/Vec.ml @@ -11,7 +11,11 @@ (* *) (**************************************************************************) -type 'a t = { mutable dummy: 'a; mutable data : 'a array; mutable sz : int } +type 'a t = { + mutable dummy: 'a; + mutable data : 'a array; + mutable sz : int; +} let _size_too_big()= failwith "Vec: capacity exceeds maximum array size" @@ -20,7 +24,7 @@ let make capa d = if capa > Sys.max_array_length then _size_too_big(); {data = Array.make capa d; sz = 0; dummy = d} -let make_empty d = {data = [||]; sz=0; dummy=d } +let[@inline] make_empty d = {data = [||]; sz=0; dummy=d } let init capa f d = if capa > Sys.max_array_length then _size_too_big(); @@ -30,10 +34,9 @@ let from_array data sz d = assert (sz <= Array.length data); {data = data; sz = sz; dummy = d} -let from_list l sz d = - let l = ref l in - let f_init i = match !l with [] -> assert false | e::r -> l := r; e in - {data = Array.init sz f_init; sz = sz; dummy = d} +let from_list l d = + let a = Array.of_list l in + from_array a (Array.length a) d let to_list s = let l = ref [] in @@ -42,20 +45,20 @@ let to_list s = done; List.rev !l -let clear s = s.sz <- 0 +let[@inline] clear s = s.sz <- 0 -let shrink t i = +let[@inline] shrink t i = assert (i >= 0); assert (i<=t.sz); - t.sz <- t.sz - i + t.sz <- i -let pop t = +let[@inline] pop t = if t.sz = 0 then invalid_arg "vec.pop"; t.sz <- t.sz - 1 -let size t = t.sz +let[@inline] size t = t.sz -let is_empty t = t.sz = 0 +let[@inline] is_empty t = t.sz = 0 let grow_to_exact t new_capa = assert (new_capa > Array.length t.data); @@ -81,33 +84,39 @@ let grow_to_at_least t new_capa = grow_to_exact t !capa ) -let is_full t = Array.length t.data = t.sz +let[@inline] is_full t = Array.length t.data = t.sz -let push t e = +let[@inline] push t e = if is_full t then grow_to_double_size t; t.data.(t.sz) <- e; t.sz <- t.sz + 1 -let last t = +let[@inline] last t = if t.sz = 0 then invalid_arg "vec.last"; t.data.(t.sz - 1) -let get t i = +let[@inline] pop_last t = + if t.sz = 0 then invalid_arg "vec.pop_last"; + let x = t.data.(t.sz - 1) in + t.sz <- t.sz - 1; + x + +let[@inline] get t i = if i < 0 || i >= t.sz then invalid_arg "vec.get"; Array.unsafe_get t.data i -let set t i v = +let[@inline] set t i v = if i < 0 || i > t.sz then invalid_arg "vec.set"; if i = t.sz then push t v else Array.unsafe_set t.data i v -let copy t = +let[@inline] copy t = let data = Array.copy t.data in {t with data; } -let move_to t t' = +let[@inline] move_to t t' = t'.data <- Array.copy t.data; t'.sz <- t.sz @@ -118,11 +127,17 @@ let remove t e = for i = !j to t.sz - 2 do t.data.(i) <- t.data.(i+1) done; pop t -let fast_remove t i = +let[@inline] fast_remove t i = assert (i < t.sz); t.data.(i) <- t.data.(t.sz - 1); t.sz <- t.sz - 1 +let filter_in_place f vec = + let i = ref 0 in + while !i < size vec do + if f (get vec !i) then incr i else fast_remove vec !i + done + let sort t f = let sub_arr = Array.sub t.data 0 t.sz in Array.fast_sort f sub_arr; @@ -130,16 +145,21 @@ let sort t f = let iter f t = for i = 0 to size t - 1 do - f t.data.(i) + f (Array.unsafe_get t.data i) done +let append a b = + grow_to_at_least a (size a + size b); + iter (push a) b + let fold f acc t = let rec _fold f acc t i = if i=t.sz then acc - else - let acc' = f acc (Array.get t.data i) in + else ( + let acc' = f acc (Array.unsafe_get t.data i) in _fold f acc' t (i+1) + ) in _fold f acc t 0 exception ExitVec @@ -147,7 +167,7 @@ exception ExitVec let exists p t = try for i = 0 to t.sz - 1 do - if p (Array.get t.data i) then raise ExitVec + if p (Array.unsafe_get t.data i) then raise ExitVec done; false with ExitVec -> true @@ -155,7 +175,7 @@ let exists p t = let for_all p t = try for i = 0 to t.sz - 1 do - if not (p (Array.get t.data i)) then raise ExitVec + if not (p (Array.unsafe_get t.data i)) then raise ExitVec done; true with ExitVec -> false diff --git a/src/util/vec.mli b/src/core/Vec.mli similarity index 88% rename from src/util/vec.mli rename to src/core/Vec.mli index d57b9cc3..914d4eba 100644 --- a/src/util/vec.mli +++ b/src/core/Vec.mli @@ -30,8 +30,7 @@ val from_array : 'a array -> int -> 'a -> 'a t to create a vector. [size] is the length of the slice of [data] that is used ([size <= Array.length data] must hold) *) -val from_list : 'a list -> int -> 'a -> 'a t -(** [from_list l n] takes the [n] first elements of list [l] to make a new vector *) +val from_list : 'a list -> 'a -> 'a t val to_list : 'a t -> 'a list (** Returns the list of elements of the vector *) @@ -40,6 +39,8 @@ val clear : 'a t -> unit (** Set size to 0, doesn't free elements *) val shrink : 'a t -> int -> unit +(** [shrink vec sz] resets size of [vec] to [sz]. + Assumes [sz >=0 && sz <= size vec] *) val pop : 'a t -> unit (** Pop last element @@ -62,10 +63,17 @@ val is_full : 'a t -> bool val push : 'a t -> 'a -> unit +val append : 'a t -> 'a t -> unit +(** [append v1 v2] pushes all elements of [v2] into [v1] *) + val last : 'a t -> 'a (** Last element, or @raise Invalid_argument if the vector is empty *) +val pop_last : 'a t -> 'a +(** Combine {!last} and {!pop}: remove last element and return it + @raise Invalid_argument if empty *) + val get : 'a t -> int -> 'a (** get the element at the given index, or @raise Invalid_argument if the index is not valid *) @@ -88,6 +96,10 @@ val fast_remove : 'a t -> int -> unit (** Remove element at index [i] without preserving order (swap with last element) *) +val filter_in_place : ('a -> bool) -> 'a t -> unit +(** [filter_in_place f v] removes from [v] the elements that do + not satisfy [f] *) + val sort : 'a t -> ('a -> 'a -> int) -> unit (** Sort in place the array *) diff --git a/src/core/jbuild b/src/core/jbuild index 777d98f1..22ef52fb 100644 --- a/src/core/jbuild +++ b/src/core/jbuild @@ -1,8 +1,5 @@ ; vim:ft=lisp: -(jbuild_version 1) - -; main binary (library ((name msat) (public_name msat) @@ -12,4 +9,3 @@ (ocamlopt_flags (:standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20)) )) - diff --git a/src/doc.txt b/src/core/msat.mld similarity index 100% rename from src/doc.txt rename to src/core/msat.mld diff --git a/src/main/jbuild b/src/main/jbuild new file mode 100644 index 00000000..08a42162 --- /dev/null +++ b/src/main/jbuild @@ -0,0 +1,13 @@ + +; vim:ft=lisp: + +; main binary +(executable + ((name main) + (public_name msat_solver) + (package msat_solver) + (libraries (msat msat.backend msat.sat msat.smt msat.mcsat dolmen)) + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) + (ocamlopt_flags (:standard -O3 -color always + -unbox-closures -unbox-closures-factor 20)) + )) diff --git a/src/main.ml b/src/main/main.ml similarity index 89% rename from src/main.ml rename to src/main/main.ml index 952a69e1..30611d77 100644 --- a/src/main.ml +++ b/src/main/main.ml @@ -4,6 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) +open Msat + exception Incorrect_model exception Out_of_time exception Out_of_space @@ -26,20 +28,20 @@ end module Make (S : External.S) - (T : Type.S with type atom := S.atom) + (T : Msat_solver.Type.S with type atom := S.atom) : sig val do_task : Dolmen.Statement.t -> unit end = struct - module D = Dot.Make(S.Proof)(Dot.Default(S.Proof)) + module D = Msat_backend.Dot.Make(S.Proof)(Msat_backend.Dot.Default(S.Proof)) let hyps = ref [] let check_model state = let check_clause c = let l = List.map (function a -> - Log.debugf 99 "Checking value of %a" - (fun k -> k S.St.pp_atom (S.St.add_atom a)); + Log.debugf 99 + (fun k -> k "Checking value of %a" S.St.pp_atom (S.St.add_atom a)); state.Solver_intf.eval a) c in List.exists (fun x -> x) l in @@ -47,7 +49,7 @@ module Make List.for_all (fun x -> x) l let prove ~assumptions = - let res = S.solve () in + let res = S.solve ~assumptions () in let t = Sys.time () in begin match res with | S.Sat state -> @@ -86,10 +88,10 @@ module Make hyps := cnf @ !hyps; S.assume cnf | Dolmen.Statement.Pack [ - { Dolmen.Statement.descr = Dolmen.Statement.Push 1; }; - { Dolmen.Statement.descr = Dolmen.Statement.Antecedent f; }; + { Dolmen.Statement.descr = Dolmen.Statement.Push 1;_ }; + { Dolmen.Statement.descr = Dolmen.Statement.Antecedent f;_ }; { Dolmen.Statement.descr = Dolmen.Statement.Prove []; }; - { Dolmen.Statement.descr = Dolmen.Statement.Pop 1; }; + { Dolmen.Statement.descr = Dolmen.Statement.Pop 1;_ }; ] -> let assumptions = T.assumptions f in prove ~assumptions @@ -104,9 +106,9 @@ module Make Dolmen.Statement.print s end -module Sat = Make(Sat.Make(struct end))(Type_sat) -module Smt = Make(Smt.Make(struct end))(Type_smt) -module Mcsat = Make(Mcsat.Make(struct end))(Type_smt) +module Sat = Make(Msat_sat.Sat.Make(struct end))(Msat_sat.Type_sat) +module Smt = Make(Msat_smt.Smt.Make(struct end))(Msat_smt.Type_smt) +module Mcsat = Make(Msat_mcsat.Mcsat.Make(struct end))(Msat_smt.Type_smt) let solver = ref (module Sat : S) let solver_list = [ @@ -225,8 +227,8 @@ let () = | Incorrect_model -> Format.printf "Internal error : incorrect *sat* model@."; exit 4 - | Type_sat.Typing_error (msg, t) - | Type_smt.Typing_error (msg, t) -> + | Msat_sat.Type_sat.Typing_error (msg, t) + | Msat_smt.Type_smt.Typing_error (msg, t) -> let b = Printexc.get_backtrace () in let loc = match t.Dolmen.Term.loc with | Some l -> l | None -> Dolmen.ParseLocation.mk "<>" 0 0 0 0 diff --git a/src/util/backtrack.ml b/src/mcsat/backtrack.ml similarity index 100% rename from src/util/backtrack.ml rename to src/mcsat/backtrack.ml diff --git a/src/util/backtrack.mli b/src/mcsat/backtrack.mli similarity index 100% rename from src/util/backtrack.mli rename to src/mcsat/backtrack.mli diff --git a/src/mcsat/eclosure.ml b/src/mcsat/eclosure.ml index 2fbbedb4..fb53655d 100644 --- a/src/mcsat/eclosure.ml +++ b/src/mcsat/eclosure.ml @@ -106,8 +106,9 @@ module Make(T : Key) = struct tag = (match mx.tag, my.tag with | Some (z, t1), Some (w, t2) -> if not (T.equal t1 t2) then begin - Log.debugf 3 "Tag shenanigan : %a (%a) <> %a (%a)" (fun k -> - k T.print t1 T.print z T.print t2 T.print w); + Log.debugf 3 + (fun k -> k "Tag shenanigan : %a (%a) <> %a (%a)" + T.print t1 T.print z T.print t2 T.print w); raise (Equal (z, w)) end else Some (z, t1) | Some t, None | None, Some t -> Some t @@ -223,7 +224,7 @@ module Make(T : Key) = struct | () -> () | exception Equal (a, b) -> raise (Unsat (a, b, expl t a b)) - | exception Same_tag (x, y) -> + | exception Same_tag (_, _) -> add_eq_aux t i j; let res = expl t i j in raise (Unsat (i, j, res)) diff --git a/src/mcsat/jbuild b/src/mcsat/jbuild new file mode 100644 index 00000000..5c4061d3 --- /dev/null +++ b/src/mcsat/jbuild @@ -0,0 +1,13 @@ +; vim:ft=lisp: + +(library + ((name msat_mcsat) + (public_name msat.mcsat) + (libraries (msat msat.solver msat.smt)) + (synopsis "mcsat interface") + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + + diff --git a/src/mcsat/mcsat.ml b/src/mcsat/mcsat.ml index 40d35e49..10ad9720 100644 --- a/src/mcsat/mcsat.ml +++ b/src/mcsat/mcsat.ml @@ -5,9 +5,9 @@ Copyright 2014 Simon Cruanes *) module Make(Dummy:sig end) = - Mcsolver.Make(struct + Msat_solver.Mcsolver.Make(struct type proof = unit - module Term = Expr_smt.Term - module Formula = Expr_smt.Atom + module Term = Msat_smt.Expr_smt.Term + module Formula = Msat_smt.Expr_smt.Atom end)(Plugin_mcsat)(struct end) diff --git a/src/mcsat/mcsat.mli b/src/mcsat/mcsat.mli index edb13401..ddd06949 100644 --- a/src/mcsat/mcsat.mli +++ b/src/mcsat/mcsat.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make(Dummy: sig end) : Solver.S with type St.formula = Expr_smt.atom +module Make(Dummy: sig end) : Msat_solver.Solver.S with type St.formula = Msat_smt.Expr_smt.atom diff --git a/src/mcsat/plugin_mcsat.ml b/src/mcsat/plugin_mcsat.ml index 06c09c2e..610d7fc2 100644 --- a/src/mcsat/plugin_mcsat.ml +++ b/src/mcsat/plugin_mcsat.ml @@ -1,6 +1,8 @@ (* Module initialization *) +open Msat_smt + module E = Eclosure.Make(Expr_smt.Term) module H = Backtrack.Hashtbl(Expr_smt.Term) module M = Hashtbl.Make(Expr_smt.Term) @@ -63,7 +65,7 @@ let update_job x ((t, watchees) as job) = with Not_found -> add_job job x; begin match t with - | { Expr_smt.term = Expr_smt.App (f, tys, l) } -> + | { Expr_smt.term = Expr_smt.App (f, tys, l);_ } -> let is_prop = Expr_smt.(Ty.equal t.t_type Ty.prop) in let t_v = H.find map t in let l' = List.map (H.find map) l in @@ -72,7 +74,7 @@ let update_job x ((t, watchees) as job) = let t', u_v = H.find interpretation u in if not (Expr_smt.Term.equal t_v u_v) then begin match t' with - | { Expr_smt.term = Expr_smt.App (_, _, r) } when is_prop -> + | { Expr_smt.term = Expr_smt.App (_, _, r); _ } when is_prop -> let eqs = List.map2 (fun a b -> Expr_smt.Atom.neg (Expr_smt.Atom.eq a b)) l r in if Expr_smt.(Term.equal u_v true_) then begin let res = Expr_smt.Atom.pred t :: @@ -83,7 +85,7 @@ let update_job x ((t, watchees) as job) = Expr_smt.Atom.neg (Expr_smt.Atom.pred t) :: eqs in raise (Absurd res) end - | { Expr_smt.term = Expr_smt.App (_, _, r) } -> + | { Expr_smt.term = Expr_smt.App (_, _, r); _ } -> let eqs = List.map2 (fun a b -> Expr_smt.Atom.neg (Expr_smt.Atom.eq a b)) l r in let res = Expr_smt.Atom.eq t t' :: eqs in raise (Absurd res) @@ -117,25 +119,25 @@ let add_assign t v = (* Assignemnts *) let rec iter_aux f = function - | { Expr_smt.term = Expr_smt.Var _ } as t -> - Log.debugf 10 "Adding %a as assignable" (fun k -> k Expr_smt.Term.print t); + | { Expr_smt.term = Expr_smt.Var _; _ } as t -> + Log.debugf 10 (fun k -> k "Adding %a as assignable" Expr_smt.Term.print t); f t - | { Expr_smt.term = Expr_smt.App (_, _, l) } as t -> + | { Expr_smt.term = Expr_smt.App (_, _, l); _ } as t -> if l <> [] then add_watch t (t :: l); List.iter (iter_aux f) l; - Log.debugf 10 "Adding %a as assignable" (fun k -> k Expr_smt.Term.print t); + Log.debugf 10 (fun k -> k "Adding %a as assignable" Expr_smt.Term.print t); f t let iter_assignable f = function - | { Expr_smt.atom = Expr_smt.Pred { Expr_smt.term = Expr_smt.Var _ } } -> () - | { Expr_smt.atom = Expr_smt.Pred ({ Expr_smt.term = Expr_smt.App (_, _, l) } as t) } -> + | { Expr_smt.atom = Expr_smt.Pred { Expr_smt.term = Expr_smt.Var _;_ }; _ } -> () + | { Expr_smt.atom = Expr_smt.Pred ({ Expr_smt.term = Expr_smt.App (_, _, l);_} as t); _ } -> if l <> [] then add_watch t (t :: l); List.iter (iter_aux f) l; - | { Expr_smt.atom = Expr_smt.Equal (a, b) } -> + | { Expr_smt.atom = Expr_smt.Equal (a, b);_ } -> iter_aux f a; iter_aux f b let eval = function - | { Expr_smt.atom = Expr_smt.Pred t } -> + | { Expr_smt.atom = Expr_smt.Pred t; _ } -> begin try let v = H.find map t in if Expr_smt.Term.equal v true_ then @@ -147,7 +149,7 @@ let eval = function with Not_found -> Plugin_intf.Unknown end - | { Expr_smt.atom = Expr_smt.Equal (a, b); sign } -> + | { Expr_smt.atom = Expr_smt.Equal (a, b); sign; _ } -> begin try let v_a = H.find map a in let v_b = H.find map b in @@ -164,7 +166,7 @@ let eval = function let rec chain_eq = function | [] | [_] -> [] - | a :: ((b :: r) as l) -> (Expr_smt.Atom.eq a b) :: chain_eq l + | a :: ((b :: _) as l) -> (Expr_smt.Atom.eq a b) :: chain_eq l let assume s = let open Plugin_intf in @@ -176,11 +178,11 @@ let assume s = E.add_tag uf t v | Lit f -> begin match f with - | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = true } -> + | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = true;_ } -> E.add_eq uf u v - | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = false } -> + | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = false;_ } -> E.add_neq uf u v - | { Expr_smt.atom = Expr_smt.Pred p; sign } -> + | { Expr_smt.atom = Expr_smt.Pred p; sign;_ } -> let v = if sign then true_ else false_ in add_assign p v end diff --git a/src/msat.mlpack b/src/msat.mlpack deleted file mode 100644 index 088df142..00000000 --- a/src/msat.mlpack +++ /dev/null @@ -1,34 +0,0 @@ -# Debug -Log - -# Interface definitions -Formula_intf -Theory_intf -Plugin_intf -Expr_intf -Tseitin_intf -Res_intf -Solver_intf -Solver_types_intf - -# Solver Modules -Internal -External -Solver -Mcsolver -Solver_types - -# Proofs & Backends -Res -Backend_intf -Dot -Coq -Dimacs -Dedukti - -# Auxiliary modules -Tseitin - -# Pure Sat solver -Sat - diff --git a/src/msat.odocl b/src/msat.odocl deleted file mode 100644 index 4e17798f..00000000 --- a/src/msat.odocl +++ /dev/null @@ -1,43 +0,0 @@ -# Log utilities -util/Log - -# Interfaces -core/Formula_intf -core/Theory_intf -core/Plugin_intf -core/Expr_intf -core/Res_intf -core/Solver_types_intf -core/Solver_intf -solver/Tseitin_intf - -# Main modules -core/Res -core/Internal -core/External -core/Solver_types - -solver/Solver -solver/Mcsolver -solver/Tseitin - -# Backends -backend/Dot -backend/Coq -backend/Dedukti -backend/Backend_intf - -# SAT solver frontend -sat/Sat -#sat/Type_sat - -# SMT solver frontend -#smt/Smt -#smt/Expr_smt -#smt/Type_smt -#smt/Unionfind - -# MCsat solver frontend -#mcsat/Mcsat -#mcsat/Eclosure -#mcsat/Plugin_mcsat diff --git a/src/msat_sat.mlpack b/src/msat_sat.mlpack deleted file mode 100644 index dc169686..00000000 --- a/src/msat_sat.mlpack +++ /dev/null @@ -1 +0,0 @@ -Sat diff --git a/src/msat_smt.mlpack b/src/msat_smt.mlpack deleted file mode 100644 index 1bba977f..00000000 --- a/src/msat_smt.mlpack +++ /dev/null @@ -1,3 +0,0 @@ -Smt -Cc -Explanation diff --git a/src/sat/jbuild b/src/sat/jbuild new file mode 100644 index 00000000..7ee505a7 --- /dev/null +++ b/src/sat/jbuild @@ -0,0 +1,13 @@ +; vim:ft=lisp: + +(library + ((name msat_sat) + (public_name msat.sat) + (libraries (msat msat.solver)) + (synopsis "sat interface") + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + + diff --git a/src/sat/sat.ml b/src/sat/sat.ml index 83de3bc9..5da9cebd 100644 --- a/src/sat/sat.ml +++ b/src/sat/sat.ml @@ -78,5 +78,5 @@ module Expr = struct end module Make(Dummy : sig end) = - Solver.Make(Expr)(Solver.DummyTheory(Expr))(struct end) + Msat_solver.Solver.Make(Expr)(Msat_solver.Solver.DummyTheory(Expr))(struct end) diff --git a/src/sat/sat.mli b/src/sat/sat.mli index ac49b6d4..500f740f 100644 --- a/src/sat/sat.mli +++ b/src/sat/sat.mli @@ -42,6 +42,6 @@ module Expr : sig end (** The module defining formulas *) -module Make(Dummy : sig end) : Solver.S with type St.formula = Expr.t +module Make(Dummy : sig end) : Msat_solver.Solver.S with type St.formula = Expr.t (** A functor that can generate as many solvers as needed. *) diff --git a/src/sat/type_sat.ml b/src/sat/type_sat.ml index 3c45b9cb..1b846e44 100644 --- a/src/sat/type_sat.ml +++ b/src/sat/type_sat.ml @@ -10,7 +10,7 @@ Copyright 2014 Simon Cruanes module Id = Dolmen.Id module Ast = Dolmen.Term module H = Hashtbl.Make(Id) -module Formula = Tseitin.Make(Sat.Expr) +module Formula = Msat_solver.Tseitin.Make(Sat.Expr) (* Exceptions *) (* ************************************************************************ *) @@ -33,6 +33,8 @@ let find_id id = (* Actual parsing *) (* ************************************************************************ *) +[@@@ocaml.warning "-9"] + let rec parse = function | { Ast.term = Ast.Builtin Ast.True } -> Formula.f_true @@ -59,6 +61,8 @@ let rec parse = function | t -> raise (Typing_error ("Term is not a pure proposition", t)) +[@@@ocaml.warning "+9"] + (* Exported functions *) (* ************************************************************************ *) diff --git a/src/sat/type_sat.mli b/src/sat/type_sat.mli index 56fced24..512d358b 100644 --- a/src/sat/type_sat.mli +++ b/src/sat/type_sat.mli @@ -8,5 +8,5 @@ Copyright 2014 Simon Cruanes This module provides functions to parse terms from the untyped syntax tree defined in Dolmen, and generate formulas as defined in the Expr_sat module. *) -include Type.S with type atom := Sat.Expr.t +include Msat_solver.Type.S with type atom := Sat.Expr.t diff --git a/src/smt/expr_smt.ml b/src/smt/expr_smt.ml index 53beee62..51d87cd2 100644 --- a/src/smt/expr_smt.ml +++ b/src/smt/expr_smt.ml @@ -85,7 +85,7 @@ module Print = struct let rec list f sep fmt = function | [] -> () | [x] -> f fmt x - | x :: ((y :: _) as r) -> + | x :: ((_ :: _) as r) -> Format.fprintf fmt "%a%s" f x sep; list f sep fmt r @@ -521,5 +521,5 @@ module Atom = struct end -module Formula = Tseitin.Make(Atom) +module Formula = Msat_solver.Tseitin.Make(Atom) diff --git a/src/smt/expr_smt.mli b/src/smt/expr_smt.mli index 229cf51b..7e410e8d 100644 --- a/src/smt/expr_smt.mli +++ b/src/smt/expr_smt.mli @@ -322,5 +322,5 @@ module Atom : sig end -module Formula : Tseitin.S with type atom = atom +module Formula : Msat_solver.Tseitin.S with type atom = atom diff --git a/src/smt/jbuild b/src/smt/jbuild new file mode 100644 index 00000000..f8d694fe --- /dev/null +++ b/src/smt/jbuild @@ -0,0 +1,13 @@ +; vim:ft=lisp: + +(library + ((name msat_smt) + (public_name msat.smt) + (libraries (msat msat.solver dolmen)) + (synopsis "smt interface") + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + + diff --git a/src/smt/smt.ml b/src/smt/smt.ml index 5acf092d..1321f5bd 100644 --- a/src/smt/smt.ml +++ b/src/smt/smt.ml @@ -4,8 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Th = Solver.DummyTheory(Expr_smt.Atom) +module Th = Msat_solver.Solver.DummyTheory(Expr_smt.Atom) module Make(Dummy:sig end) = - Solver.Make(Expr_smt.Atom)(Th)(struct end) + Msat_solver.Solver.Make(Expr_smt.Atom)(Th)(struct end) diff --git a/src/smt/smt.mli b/src/smt/smt.mli index edb13401..5cec2966 100644 --- a/src/smt/smt.mli +++ b/src/smt/smt.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make(Dummy: sig end) : Solver.S with type St.formula = Expr_smt.atom +module Make(Dummy: sig end) : Msat_solver.Solver.S with type St.formula = Expr_smt.atom diff --git a/src/smt/type_smt.ml b/src/smt/type_smt.ml index a3f55125..5dbe3837 100644 --- a/src/smt/type_smt.ml +++ b/src/smt/type_smt.ml @@ -73,29 +73,29 @@ let find_global name = (* Symbol declarations *) let decl_ty_cstr id c = if H.mem global_env id then - Log.debugf 0 "Symbol '%a' has already been defined, overwriting previous definition" - (fun k -> k Id.print id); + Log.debugf 0 + (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); H.add global_env id (`Ty c); - Log.debugf 1 "New type constructor : %a" (fun k -> k Expr.Print.const_ttype c) + Log.debugf 1 (fun k -> k "New type constructor : %a" Expr.Print.const_ttype c) let decl_term id c = if H.mem global_env id then - Log.debugf 0 "Symbol '%a' has already been defined, overwriting previous definition" - (fun k -> k Id.print id); + Log.debugf 0 + (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); H.add global_env id (`Term c); - Log.debugf 1 "New constant : %a" (fun k -> k Expr.Print.const_ty c) + Log.debugf 1 (fun k -> k "New constant : %a" Expr.Print.const_ty c) (* Symbol definitions *) let def_ty id args body = if H.mem global_env id then - Log.debugf 0 "Symbol '%a' has already been defined, overwriting previous definition" - (fun k -> k Id.print id); + Log.debugf 0 + (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); H.add global_env id (`Ty_alias (args, body)) let def_term id ty_args args body = if H.mem global_env id then - Log.debugf 0 "Symbol '%a' has already been defined, overwriting previous definition" - (fun k -> k Id.print id); + Log.debugf 0 + (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); H.add global_env id (`Term_alias (ty_args, args, body)) (* Local Environment *) @@ -126,8 +126,8 @@ let add_type_var env id v = else v in - Log.debugf 1 "New binding : %a -> %a" - (fun k -> k Id.print id Expr.Print.id_ttype v'); + Log.debugf 1 + (fun k -> k "New binding : %a -> %a" Id.print id Expr.Print.id_ttype v'); v', { env with type_vars = M.add id v' env.type_vars } let add_type_vars env l = @@ -143,8 +143,8 @@ let add_term_var env id v = else v in - Log.debugf 1 "New binding : %a -> %a" - (fun k -> k Id.print id Expr.Print.id_ty v'); + Log.debugf 1 + (fun k -> k "New binding : %a -> %a" Id.print id Expr.Print.id_ty v'); v', { env with term_vars = M.add id v' env.term_vars } let find_var env name = @@ -159,13 +159,13 @@ let find_var env name = (* Add local bound variables to env *) let add_let_term env id t = - Log.debugf 1 "New let-binding : %s -> %a" - (fun k -> k id.Id.name Expr.Print.term t); + Log.debugf 1 + (fun k -> k "New let-binding : %s -> %a" id.Id.name Expr.Print.term t); { env with term_lets = M.add id t env.term_lets } let add_let_prop env id t = - Log.debugf 1 "New let-binding : %s -> %a" - (fun k -> k id.Id.name Expr.Formula.print t); + Log.debugf 1 + (fun k -> k "New let-binding : %s -> %a" id.Id.name Expr.Formula.print t); { env with prop_lets = M.add id t env.prop_lets } let find_let env name = @@ -207,13 +207,13 @@ let arity f = List.length Expr.(f.id_type.fun_vars) + List.length Expr.(f.id_type.fun_args) -let ty_apply env ast f args = +let ty_apply _env ast f args = try Expr.Ty.apply f args with Expr.Bad_ty_arity _ -> _bad_arity Expr.(f.id_name) (arity f) ast -let term_apply env ast f ty_args t_args = +let term_apply _env ast f ty_args t_args = try Expr.Term.apply f ty_args t_args with @@ -277,6 +277,8 @@ let infer env s args = (* Expression parsing *) (* ************************************************************************ *) +[@@@ocaml.warning "-9"] + let rec parse_expr (env : env) t = match t with (* Base Types *) @@ -582,13 +584,15 @@ let rec parse_fun ty_args t_args env = function | Formula _ -> _expected "type or term" ast end +[@@@ocaml.warning "+9"] + (* High-level parsing functions *) (* ************************************************************************ *) let decl id t = let env = empty_env () in - Log.debugf 5 "Typing declaration: %s : %a" - (fun k -> k id.Id.name Ast.print t); + Log.debugf 5 + (fun k -> k "Typing declaration: %s : %a" id.Id.name Ast.print t); begin match parse_sig env t with | `Ty_cstr n -> decl_ty_cstr id (Expr.Id.ty_fun id.Id.name n) | `Fun_ty (vars, args, ret) -> @@ -597,8 +601,8 @@ let decl id t = let def id t = let env = empty_env () in - Log.debugf 5 "Typing definition: %s = %a" - (fun k -> k id.Id.name Ast.print t); + Log.debugf 5 + (fun k -> k "Typing definition: %s = %a" id.Id.name Ast.print t); begin match parse_fun [] [] env t with | `Ty (ty_args, body) -> def_ty id ty_args body | `Term (ty_args, t_args, body) -> def_term id ty_args t_args body @@ -606,7 +610,7 @@ let def id t = let formula t = let env = empty_env () in - Log.debugf 5 "Typing top-level formula: %a" (fun k -> k Ast.print t); + Log.debugf 5 (fun k -> k "Typing top-level formula: %a" Ast.print t); parse_formula env t let assumptions t = diff --git a/src/smt/type_smt.mli b/src/smt/type_smt.mli index 51957bc8..7593432b 100644 --- a/src/smt/type_smt.mli +++ b/src/smt/type_smt.mli @@ -3,5 +3,5 @@ This module provides functions to parse terms from the untyped syntax tree defined in Dolmen, and generate formulas as defined in the Expr_smt module. *) -include Type.S with type atom := Expr_smt.Atom.t +include Msat_solver.Type.S with type atom := Expr_smt.Atom.t diff --git a/src/solver/jbuild b/src/solver/jbuild new file mode 100644 index 00000000..f9e40772 --- /dev/null +++ b/src/solver/jbuild @@ -0,0 +1,12 @@ +; vim:ft=lisp: + +(library + ((name msat_solver) + (public_name msat.solver) + (libraries (msat dolmen)) + (synopsis "mcsat solver util") + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + diff --git a/src/solver/tseitin.ml b/src/solver/tseitin.ml index f5dacd21..e1901aa1 100644 --- a/src/solver/tseitin.ml +++ b/src/solver/tseitin.ml @@ -242,7 +242,7 @@ module Make (F : Tseitin_intf.Arg) = struct (fun (_, acc) f -> match cnf f with | _, [] -> assert false - | cmb, [a] -> Some And, a :: acc + | _cmb, [a] -> Some And, a :: acc | Some And, l -> Some And, l @@ acc (* let proxy = mk_proxy () in *) @@ -261,7 +261,7 @@ module Make (F : Tseitin_intf.Arg) = struct (fun (_, acc) f -> match cnf f with | _, [] -> assert false - | cmb, [a] -> Some Or, a :: acc + | _cmb, [a] -> Some Or, a :: acc | Some Or, l -> Some Or, l @@ acc (* let proxy = mk_proxy () in *) diff --git a/src/util/type.ml b/src/solver/type.ml similarity index 100% rename from src/util/type.ml rename to src/solver/type.ml diff --git a/src/util/iheap.ml b/src/util/iheap.ml deleted file mode 100644 index 25bbbb18..00000000 --- a/src/util/iheap.ml +++ /dev/null @@ -1,143 +0,0 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - -module V = Sparse_vec - -type t = {heap : int Vec.t; indices : int V.t } - -let _absent_index = -1 - -let init sz = - { heap = Vec.init sz (fun i -> i) 0; - indices = V.init sz (fun i -> i) _absent_index} - -let left i = (i lsl 1) + 1 (* i*2 + 1 *) -let right i = (i + 1) lsl 1 (* (i+1)*2 *) -let parent i = (i - 1) asr 1 (* (i-1) / 2 *) - -(* -let rec heap_property cmp ({heap=heap} as s) i = - i >= (Vec.size heap) || - ((i = 0 || not(cmp (Vec. get heap i) (Vec.get heap (parent i)))) - && heap_property cmp s (left i) && heap_property cmp s (right i)) - -let heap_property cmp s = heap_property cmp s 1 -*) - -let percolate_up cmp {heap=heap;indices=indices} i = - let x = Vec.get heap i in - let pi = ref (parent i) in - let i = ref i in - while !i <> 0 && cmp x (Vec.get heap !pi) do - Vec.set heap !i (Vec.get heap !pi); - V.set indices (Vec.get heap !i) !i; - i := !pi; - pi := parent !i - done; - Vec.set heap !i x; - V.set indices x !i - -let percolate_down cmp {heap=heap;indices=indices} i = - let x = Vec.get heap i in - let sz = Vec.size heap in - let li = ref (left i) in - let ri = ref (right i) in - let i = ref i in - (try - while !li < sz do - let child = - if !ri < sz && cmp (Vec.get heap !ri) (Vec.get heap !li) then !ri - else !li - in - if not (cmp (Vec.get heap child) x) then raise Exit; - Vec.set heap !i (Vec.get heap child); - V.set indices (Vec.get heap !i) !i; - i := child; - li := left !i; - ri := right !i - done; - with Exit -> ()); - Vec.set heap !i x; - V.set indices x !i - -let in_heap s n = n < V.length s.indices && V.get s.indices n >= 0 - -let decrease cmp s n = - assert (in_heap s n); percolate_up cmp s (V.get s.indices n) - -(* -let increase cmp s n = - assert (in_heap s n); percolate_down cmp s (V.get s.indices n) -*) - -let filter s filt cmp = - let j = ref 0 in - let lim = Vec.size s.heap in - for i = 0 to lim - 1 do - if filt (Vec.get s.heap i) then begin - Vec.set s.heap !j (Vec.get s.heap i); - V.set s.indices (Vec.get s.heap i) !j; - incr j; - end - else V.set s.indices (Vec.get s.heap i) _absent_index; - done; - Vec.shrink s.heap (lim - !j); - for i = (lim / 2) - 1 downto 0 do - percolate_down cmp s i - done - -let size s = Vec.size s.heap - -let is_empty s = Vec.is_empty s.heap - -let clear {heap; indices} = - Vec.clear heap; - V.clear indices; - () - -let insert cmp s n = - if not (in_heap s n) then - begin - V.set s.indices n (Vec.size s.heap); - Vec.push s.heap n; - percolate_up cmp s (V.get s.indices n) - end - -let grow_to_at_least s sz = - V.resize s.indices sz; - Vec.grow_to_at_least s.heap sz - -(* -let update cmp s n = - assert (heap_property cmp s); - begin - if in_heap s n then - begin - percolate_up cmp s (Vec.get s.indices n); - percolate_down cmp s (Vec.get s.indices n) - end - else insert cmp s n - end; - assert (heap_property cmp s) -*) - -let remove_min cmp ({heap=heap; indices=indices} as s) = - if Vec.size heap=0 then raise Not_found; - let x = Vec.get heap 0 in - Vec.set heap 0 (Vec.last heap); (*heap.last()*) - V.set indices (Vec.get heap 0) 0; - V.set indices x (-1); - Vec.pop s.heap; - if Vec.size s.heap > 1 then percolate_down cmp s 0; - x - diff --git a/src/util/iheap.mli b/src/util/iheap.mli deleted file mode 100644 index 6c1004a5..00000000 --- a/src/util/iheap.mli +++ /dev/null @@ -1,55 +0,0 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - -type t -(** Heap of integers, whose priority is increased or decreased - incrementally (see {!decrease} for instance) *) - -val init : int -> t -(** Create a heap with the given number of values inside. - [init len] contains integers from [0] to [len-1]. *) - -val in_heap : t -> int -> bool -(** [in_heap h x] returns [true] iff [x] is among the integers that belong to - the heap. *) - -val decrease : (int -> int -> bool) -> t -> int -> unit -(** [decrease cmp h x] decreases the value associated to [x] within [h], - according to the comparison function [cmp] *) - -(*val increase : (int -> int -> bool) -> t -> int -> unit*) - -val size : t -> int -(** Number of integers within the heap *) - -val is_empty : t -> bool - -val clear : t -> unit -(** Clear the content of the heap *) - -val insert : (int -> int -> bool) -> t -> int -> unit -(** Insert a new integer into the heap, according to the given comparison *) - -val grow_to_at_least: t -> int -> unit -(** Hint: augment the internal capacity of the heap until it reaches at - least the given integer *) - -(*val update : (int -> int -> bool) -> t -> int -> unit*) - -val remove_min : (int -> int -> bool) -> t -> int -(** Remove and return the integer that has the lowest value from the heap - @raise Not_found if the heap is empty *) - -val filter : t -> (int -> bool) -> (int -> int -> bool) -> unit -(** Filter out values that don't satisfy the predicate. A comparison - function is used to re-order the heap *) diff --git a/tests/jbuild b/tests/jbuild new file mode 100644 index 00000000..988fae01 --- /dev/null +++ b/tests/jbuild @@ -0,0 +1,16 @@ +; vim:ft=lisp: + +(executable + ((name test_api) + (libraries (msat msat.backend msat.sat msat.smt msat.mcsat dolmen)) + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) + (ocamlopt_flags (:standard -O3 -color always + -unbox-closures -unbox-closures-factor 20)) + )) + +(alias + ((name runtest) + (deps (test_api.exe)) + (action (run ${<})))) + + diff --git a/tests/run b/tests/run index 4d90b8a3..463ab373 100755 --- a/tests/run +++ b/tests/run @@ -1,7 +1,7 @@ #!/bin/bash CURDIR=`dirname $0` -SOLVER="$CURDIR/../msat" +SOLVER="$CURDIR/../msat.exe" solvertest () { for f in `find -L $1 -type f -name '*.cnf' -o -name '*.smt2'` diff --git a/tests/test_api.ml b/tests/test_api.ml index e575758e..158dc30b 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -6,8 +6,11 @@ Copyright 2014 Simon Cruanes (* Tests that require the API *) +open Msat +open Msat_sat + module F = Sat.Expr -module T = Tseitin.Make(F) +module T = Msat_solver.Tseitin.Make(F) let (|>) x f = f x From f64a1556b1d555c828c98bccb5ba4f49431583f5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 15:55:00 +0100 Subject: [PATCH 017/182] details --- Makefile | 2 +- msat.exe | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 120000 msat.exe diff --git a/Makefile b/Makefile index f58123ad..3995e022 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ OPTS= -j $(J) LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) -all: lib test +all: build test build: jbuilder build $(OPTS) @install diff --git a/msat.exe b/msat.exe new file mode 120000 index 00000000..cceb9ac3 --- /dev/null +++ b/msat.exe @@ -0,0 +1 @@ +_build/default/src/main/main.exe \ No newline at end of file From 2707215aa28d5750c4f67362876d1c612e6a9972 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 16:01:36 +0100 Subject: [PATCH 018/182] move tseitin transformation into its own lib --- Makefile | 4 ++-- src/core/Res.ml | 2 +- src/core/msat.mld | 15 ++++----------- src/sat/jbuild | 2 +- src/sat/type_sat.ml | 2 +- src/smt/expr_smt.ml | 2 +- src/smt/expr_smt.mli | 2 +- src/smt/jbuild | 2 +- .../tseitin.ml => tseitin/Msat_tseitin.ml} | 0 .../tseitin.mli => tseitin/Msat_tseitin.mli} | 0 .../tseitin_intf.ml => tseitin/Tseitin_intf.ml} | 0 src/tseitin/jbuild | 12 ++++++++++++ tests/test_api.ml | 2 +- 13 files changed, 25 insertions(+), 20 deletions(-) rename src/{solver/tseitin.ml => tseitin/Msat_tseitin.ml} (100%) rename src/{solver/tseitin.mli => tseitin/Msat_tseitin.mli} (100%) rename src/{solver/tseitin_intf.ml => tseitin/Tseitin_intf.ml} (100%) create mode 100644 src/tseitin/jbuild diff --git a/Makefile b/Makefile index 3995e022..171013cd 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ OPTS= -j $(J) LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) -all: build test +all: build-dev test build: jbuilder build $(OPTS) @install @@ -58,7 +58,7 @@ reindent: ocp-indent @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 echo "reindenting: " @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 ocp-indent -i -WATCH=build-dev +WATCH=all watch: while find src/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ echo "============ at `date` ==========" ; \ diff --git a/src/core/Res.ml b/src/core/Res.ml index f29dc568..5d9f5e26 100644 --- a/src/core/Res.ml +++ b/src/core/Res.ml @@ -201,7 +201,7 @@ module Make(St : Solver_types.S) = struct let duplicates, res = analyze (list c) in assert (cmp_cl res (list conclusion) = 0); { conclusion; step = Duplicate (c, List.concat duplicates) } - | St.History ( c :: ([d] as r)) -> + | St.History ( c :: ([_] as r)) -> let (l, c', d', a) = chain_res (c, to_list c) r in assert (cmp_cl l (to_list conclusion) = 0); { conclusion; step = Resolution (c', d', a); } diff --git a/src/core/msat.mld b/src/core/msat.mld index da9f38a9..622fc9c6 100644 --- a/src/core/msat.mld +++ b/src/core/msat.mld @@ -21,19 +21,13 @@ The following modules allow to easily create a SAT or SMT solver (remark: a SAT simply an SMT solver with an empty theory). {!modules: -Solver -Solver_intf -Formula_intf -Theory_intf +Msat_solver } The following modules allow the creation of a McSat solver (Model Constructing solver): {!modules: -Mcsolver -Solver_intf -Expr_intf -Plugin_intf +Msat_mcsolver } {4 Useful tools} @@ -41,14 +35,13 @@ Plugin_intf An instanciation of a pure sat solver is also provided: {!modules: -Sat +Msat_sat } Lastly, mSAT also provides an implementation of Tseitin's CNF conversion: {!modules: -Tseitin -Tseitin_intf +Msat_tseitin } {4 Proof Management} diff --git a/src/sat/jbuild b/src/sat/jbuild index 7ee505a7..86386d7a 100644 --- a/src/sat/jbuild +++ b/src/sat/jbuild @@ -3,7 +3,7 @@ (library ((name msat_sat) (public_name msat.sat) - (libraries (msat msat.solver)) + (libraries (msat msat.tseitin msat.solver)) (synopsis "sat interface") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot diff --git a/src/sat/type_sat.ml b/src/sat/type_sat.ml index 1b846e44..426b16d7 100644 --- a/src/sat/type_sat.ml +++ b/src/sat/type_sat.ml @@ -10,7 +10,7 @@ Copyright 2014 Simon Cruanes module Id = Dolmen.Id module Ast = Dolmen.Term module H = Hashtbl.Make(Id) -module Formula = Msat_solver.Tseitin.Make(Sat.Expr) +module Formula = Msat_tseitin.Make(Sat.Expr) (* Exceptions *) (* ************************************************************************ *) diff --git a/src/smt/expr_smt.ml b/src/smt/expr_smt.ml index 51d87cd2..f3b8961f 100644 --- a/src/smt/expr_smt.ml +++ b/src/smt/expr_smt.ml @@ -521,5 +521,5 @@ module Atom = struct end -module Formula = Msat_solver.Tseitin.Make(Atom) +module Formula = Msat_tseitin.Make(Atom) diff --git a/src/smt/expr_smt.mli b/src/smt/expr_smt.mli index 7e410e8d..03f0b07c 100644 --- a/src/smt/expr_smt.mli +++ b/src/smt/expr_smt.mli @@ -322,5 +322,5 @@ module Atom : sig end -module Formula : Msat_solver.Tseitin.S with type atom = atom +module Formula : Msat_tseitin.S with type atom = atom diff --git a/src/smt/jbuild b/src/smt/jbuild index f8d694fe..843fa9c3 100644 --- a/src/smt/jbuild +++ b/src/smt/jbuild @@ -3,7 +3,7 @@ (library ((name msat_smt) (public_name msat.smt) - (libraries (msat msat.solver dolmen)) + (libraries (msat msat.solver msat.tseitin dolmen)) (synopsis "smt interface") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot diff --git a/src/solver/tseitin.ml b/src/tseitin/Msat_tseitin.ml similarity index 100% rename from src/solver/tseitin.ml rename to src/tseitin/Msat_tseitin.ml diff --git a/src/solver/tseitin.mli b/src/tseitin/Msat_tseitin.mli similarity index 100% rename from src/solver/tseitin.mli rename to src/tseitin/Msat_tseitin.mli diff --git a/src/solver/tseitin_intf.ml b/src/tseitin/Tseitin_intf.ml similarity index 100% rename from src/solver/tseitin_intf.ml rename to src/tseitin/Tseitin_intf.ml diff --git a/src/tseitin/jbuild b/src/tseitin/jbuild new file mode 100644 index 00000000..e70bd0e7 --- /dev/null +++ b/src/tseitin/jbuild @@ -0,0 +1,12 @@ +; vim:ft=lisp: + +(library + ((name msat_tseitin) + (public_name msat.tseitin) + (synopsis "Tseitin transformation for msat") + (libraries ()) + (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) + (ocamlopt_flags (:standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20)) + )) + diff --git a/tests/test_api.ml b/tests/test_api.ml index 158dc30b..8984c12d 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -10,7 +10,7 @@ open Msat open Msat_sat module F = Sat.Expr -module T = Msat_solver.Tseitin.Make(F) +module T = Msat_tseitin.Make(F) let (|>) x f = f x From ec7fa9e01ab5e9c79503d3f114d5a9cd5b92b5ea Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 16:02:47 +0100 Subject: [PATCH 019/182] fix warnings --- src/backend/Dimacs.ml | 6 +++--- src/backend/Dot.ml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index 239e8604..cbec4c32 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -42,12 +42,12 @@ module Make(St : Solver_types.S)(Dummy: sig end) = struct Format.fprintf fmt "c Local assumptions@,a %a@," St.pp_dimacs vec let export_icnf_aux r name map_filter fmt vec = - let aux fmt v = + let aux fmt _ = for i = !r to (Vec.size vec) - 1 do let x = Vec.get vec i in match map_filter x with | None -> () - | Some y -> Format.fprintf fmt "%a@," St.pp_dimacs (Vec.get vec i) + | Some _ -> Format.fprintf fmt "%a@," St.pp_dimacs (Vec.get vec i) done; r := Vec.size vec in @@ -108,7 +108,7 @@ module Make(St : Solver_types.S)(Dummy: sig end) = struct let lemmas = history in (* Local assertions *) let l = List.map (function - | {St.cpremise = St.Local; atoms = [| a |] } -> a + | {St.cpremise = St.Local; atoms = [| a |];_ } -> a | _ -> assert false) (Vec.to_list local) in let local = St.make_clause "local (tmp)" l St.Local in (* Number of atoms and clauses *) diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 559b3fa0..78c160cb 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -115,7 +115,7 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom let rule, color, l = A.assumption_info S.(n.conclusion) in let color = match color with None -> "LIGHTBLUE" | Some c -> c in print_dot_node fmt (node_id n) "LIGHTBLUE" S.(n.conclusion) rule color l - | S.Lemma lemma -> + | S.Lemma _ -> let rule, color, l = A.lemma_info S.(n.conclusion) in let color = match color with None -> "YELLOW" | Some c -> c in print_dot_node fmt (node_id n) "LIGHTBLUE" S.(n.conclusion) rule color l From a978ec97a3a6562e7853a33c0f07855cf479c19e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 16:08:49 +0100 Subject: [PATCH 020/182] update travis --- .travis.yml | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1e014fc..75ecf62d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,17 @@ language: c env: - # Check that the package installs correctly with and without its test dependencies using opam - - TO_TEST=install OPAMBUILDTEST=false OCAML_VERSION=4.03.0 - - TO_TEST=install OPAMBUILDTEST=true OCAML_VERSION=4.03.0 - # Check build and unit tests - # NOTE: testing needs OPAMBUILDTEST=true so that test deps are installed - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.03.0 - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.03.0+flambda - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.04.2 - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.04.2+flambda - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.05.0 - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.05.0+flambda - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0 - - TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0+flambda -#matrix: -# allow_failures: -# # opam-installer fails on these versions (will probably be fixed before the official opam2 release -# - env: TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0 -# - env: TO_TEST=tests OPAMBUILDTEST=true OCAML_VERSION=4.06.0+flambda -# Caching may take a lot of space with so many ocaml versions -#cache: -# directories: -# - $HOME/.opam + - RUN_TEST=true OCAML_VERSION=4.03.0 + - RUN_TEST=true OCAML_VERSION=4.03.0+flambda + - RUN_TEST=true OCAML_VERSION=4.04.0 + - RUN_TEST=true OCAML_VERSION=4.04.0+flambda + - RUN_TEST=true OCAML_VERSION=4.06.0 +addons: + apt: + sources: + - avsm + packages: + - opam + - time before_install: # Download and use opam2 - wget -O ${HOME}/opam https://github.com/ocaml/opam/releases/download/2.0.0-beta6/opam-2.0.0-beta6-x86_64-linux From cb45782eed1a1d93fa6fd8d24fca10e227bf5c0a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 17:33:03 +0100 Subject: [PATCH 021/182] travis --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75ecf62d..d56fd66e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,13 @@ before_install: - ${HOME}/opam init --compiler=${OCAML_VERSION} - eval `${HOME}/opam config env` - export OPAMVERBOSE=1 - # Testing requires the dev version of dolmen - - if [ "$OPAMBUILDTEST" = "true" ]; then ${HOME}/opam pin add --dev-repo dolmen; fi + - opam init + - opam switch ${OCAML_VERSION} + - eval `opam config env` + - opam install ocamlfind jbuilder + - if ${RUN_TEST}; then opam pin add dolmen https://github.com/Gbury/dolmen.git; fi install: - # Install dependencies - - ${HOME}/opam pin add --no-action msat . - - ${HOME}/opam install --deps-only msat.dev + - make build script: # Build and launch the tests - if [ "$TO_TEST" = "tests" ]; then make lib && make bin && make test; fi From d9ceba72d47f3cd00e6e6a2aed312b42211287ab Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 18:03:00 +0100 Subject: [PATCH 022/182] cleanup in fields --- src/core/BitField.ml | 135 ++++++++++++++++++++++++++++++++++ src/core/BitField.mli | 69 +++++++++++++++++ src/core/Internal.ml | 24 +++--- src/core/Solver_types.ml | 40 +++++----- src/core/Solver_types.mli | 2 + src/core/Solver_types_intf.ml | 14 +++- 6 files changed, 246 insertions(+), 38 deletions(-) create mode 100644 src/core/BitField.ml create mode 100644 src/core/BitField.mli diff --git a/src/core/BitField.ml b/src/core/BitField.ml new file mode 100644 index 00000000..bbb1f632 --- /dev/null +++ b/src/core/BitField.ml @@ -0,0 +1,135 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Bit Field} *) + +exception TooManyFields +exception Frozen + +let max_width = Sys.word_size - 2 + +(*$R + let module B = CCBitField.Make(struct end) in + let x = B.mk_field () in + let y = B.mk_field () in + let z = B.mk_field () in + + let f = B.empty |> B.set x true |> B.set y true in + + assert_bool "z_false" (not (B.get z f)) ; + + assert_bool "z_true" (f |> B.set z true |> B.get z); +*) + +(*$R + let module B = CCBitField.Make(struct end) in + let _ = B.mk_field () in + B.freeze(); + assert_bool "must raise" + (try ignore (B.mk_field()); false with Frozen -> true); + +*) + +(*$R + let module B = CCBitField.Make(struct end) in + + let x = B.mk_field () in + let y = B.mk_field () in + let z = B.mk_field () in + let u = B.mk_field () in + B.freeze(); + + let f = B.empty + |> B.set y true + |> B.set z true + in + + assert_equal ~printer:CCInt.to_string 6 (f :> int) ; + + assert_equal false (B.get x f) ; + assert_equal true (B.get y f) ; + assert_equal true (B.get z f); + + let f' = B.set u true f in + + assert_equal false (B.get x f') ; + assert_equal true (B.get y f') ; + assert_equal true (B.get z f'); + assert_equal true (B.get u f'); + + () +*) + + +module type S = sig + type t = private int + (** Generative type of bitfields. Each instantiation of the functor + should create a new, incompatible type *) + + val empty : t + (** Empty bitfields (all bits 0) *) + + type field + + val get : field -> t -> bool + (** Get the value of this field *) + + val set : field -> bool -> t -> t + (** Set the value of this field *) + + val mk_field : unit -> field + (** Make a new field *) + + val freeze : unit -> unit + (** Prevent new fields from being added. From now on, creating + a field will raise Frozen *) + + val total_width : unit -> int + (** Current width of the bitfield *) +end + +(* all bits from 0 to w-1 set to true *) +let rec all_bits_ acc w = + if w=0 then acc + else + let acc = acc lor (1 lsl w-1) in + all_bits_ acc (w-1) + +(*$T + all_bits_ 0 1 = 1 + all_bits_ 0 2 = 3 + all_bits_ 0 3 = 7 + all_bits_ 0 4 = 15 +*) + +(* increment and return previous value *) +let get_then_incr n = + let x = !n in + incr n; + x + +module Make(X : sig end) : S = struct + type t = int + + let empty = 0 + + let width_ = ref 0 + let frozen_ = ref false + + type field = int (* a mask *) + + let[@inline] get field x = (x land field) <> 0 + + let[@inline] set field b x = + if b then x lor field else x land (lnot field) + + let mk_field () = + if !frozen_ then raise Frozen; + let n = get_then_incr width_ in + if n > max_width then raise TooManyFields; + let mask = 1 lsl n in + mask + + let freeze () = frozen_ := true + + let total_width () = !width_ +end diff --git a/src/core/BitField.mli b/src/core/BitField.mli new file mode 100644 index 00000000..6821855e --- /dev/null +++ b/src/core/BitField.mli @@ -0,0 +1,69 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Bit Field} + + This module defines efficient bitfields + up to 30 or 62 bits (depending on the architecture) in + a relatively type-safe way. + + {[ + module B = CCBitField.Make(struct end);; + + #install_printer B.pp;; + + let x = B.mk_field () + let y = B.mk_field () + let z = B.mk_field () + + let f = B.empty |> B.set x true |> B.set y true;; + + assert (not (B.get z f)) ;; + + assert (f |> B.set z true |> B.get z);; + + ]} +*) + +exception TooManyFields +(** Raised when too many fields are packed into one bitfield *) + +exception Frozen +(** Raised when a frozen bitfield is modified *) + +val max_width : int +(** System-dependent maximum width for a bitfield, typically 30 or 62 *) + +(** {2 Bitfield Signature} *) +module type S = sig + type t = private int + (** Generative type of bitfields. Each instantiation of the functor + should create a new, incompatible type *) + + val empty : t + (** Empty bitfields (all bits 0) *) + + type field + + val get : field -> t -> bool + (** Get the value of this field *) + + val set : field -> bool -> t -> t + (** Set the value of this field *) + + val mk_field : unit -> field + (** Make a new field *) + + val freeze : unit -> unit + (** Prevent new fields from being added. From now on, creating + a field will raise Frozen *) + + val total_width : unit -> int + (** Current width of the bitfield *) +end + +(** Create a new bitfield type *) +module Make(X : sig end) : S + +(**/**) +val all_bits_ : int -> int -> int +(**/**) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index f2227514..158770c3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -250,9 +250,6 @@ module Make let new_atom p = let a = atom p in - (* This is necessary to ensure that the var will not be dropped - during the next backtrack. *) - a.var.used <- a.var.used + 1; insert_var_order (E_var a.var) (* Rather than iterate over all the heap when we want to decrease all the @@ -335,12 +332,11 @@ module Make if seen a then duplicates := a :: !duplicates else (mark a; res := a :: !res) ) clause.atoms; - List.iter (fun a -> - begin match a.var.seen with - | Both -> trivial := true - | _ -> () - end; - clear a.var) !res; + List.iter + (fun a -> + if seen_both a.var then trivial := true; + clear a.var) + !res; if !trivial then raise Trivial else if !duplicates = [] then @@ -417,7 +413,6 @@ module Make let attach_clause c = assert (not c.attached); Log.debugf debug (fun k -> k "Attaching %a" St.pp_clause c); - Array.iter (fun a -> a.var.used <- a.var.used + 1) c.atoms; Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; c.attached <- true; @@ -661,8 +656,9 @@ module Make | Some Bcp cl -> history := cl :: !history | _ -> assert false end; - if not (q.var.seen = Both) then begin - q.var.seen <- Both; + if not (seen_both q.var) then ( + mark q; + mark q.neg; seen := q :: !seen; if q.var.v_level > 0 then begin var_bump_activity q.var; @@ -673,7 +669,7 @@ module Make blevel := max !blevel q.var.v_level end end - end + ) done end; @@ -683,7 +679,7 @@ module Make Log.debugf debug (fun k -> k " looking at: %a" St.pp a); match a with | Atom q -> - (not (q.var.seen = Both)) || + (not (seen_both q.var)) || (q.var.v_level < conflict_level) | Lit _ -> true do diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 1fd4b7b7..7d39f029 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -18,6 +18,12 @@ Copyright 2016 Simon Cruanes module type S = Solver_types_intf.S +module Var_fields = Solver_types_intf.Var_fields + +let v_field_seen_neg = Var_fields.mk_field() +let v_field_seen_pos = Var_fields.mk_field() +let () = Var_fields.freeze() + (* Solver types for McSat Solving *) (* ************************************************************************ *) @@ -49,8 +55,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct vid : int; pa : atom; na : atom; - mutable used : int; - mutable seen : seen; + mutable v_fields : Var_fields.t; mutable v_level : int; mutable v_idx: int; (** position in heap *) mutable v_weight : float; (** Weight (for the heap), tracking activity *) @@ -99,8 +104,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct { vid = -101; pa = dummy_atom; na = dummy_atom; - used = 0; - seen = Nope; + v_fields = Var_fields.empty; v_level = -1; v_weight = -1.; v_idx= -1; @@ -169,8 +173,7 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct { vid = !cpt_mk_var; pa = pa; na = na; - used = 0; - seen = Nope; + v_fields = Var_fields.empty; v_level = -1; v_idx= -1; v_weight = 0.; @@ -217,28 +220,21 @@ module McMake (E : Expr_intf.S)(Dummy : sig end) = struct let empty_clause = make_clause "Empty" [] (History []) (* Marking helpers *) - let clear v = v.seen <- Nope + let clear v = v.v_fields <- Var_fields.empty let seen a = let pos = (a == a.var.pa) in - match a.var.seen, pos with - | Nope, _ -> false - | Both, _ - | Positive, true - | Negative, false -> true - | Positive, false - | Negative, true -> false + let field = if pos then v_field_seen_pos else v_field_seen_neg in + Var_fields.get field a.var.v_fields + + let seen_both v = + Var_fields.get v_field_seen_pos v.v_fields && + Var_fields.get v_field_seen_neg v.v_fields let mark a = let pos = (a == a.var.pa) in - match a.var.seen with - | Both -> () - | Nope -> - a.var.seen <- (if pos then Positive else Negative) - | Positive -> - if pos then () else a.var.seen <- Both - | Negative -> - if pos then a.var.seen <- Both else () + let field = if pos then v_field_seen_pos else v_field_seen_neg in + a.var.v_fields <- Var_fields.set field true a.var.v_fields (* Decisions & propagations *) type t = diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli index b9e76a7a..1892d53c 100644 --- a/src/core/Solver_types.mli +++ b/src/core/Solver_types.mli @@ -28,6 +28,8 @@ Copyright 2016 Simon Cruanes module type S = Solver_types_intf.S (** Interface for the internal types. *) +module Var_fields = Solver_types_intf.Var_fields + module McMake (E : Expr_intf.S)(Dummy : sig end): S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 24022f7d..ee97b4fb 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -22,6 +22,8 @@ Copyright 2016 Simon Cruanes used in the core solver. *) +module Var_fields = BitField.Make(struct end) + module type S = sig (** The signatures of clauses used in the Solver. *) @@ -41,6 +43,10 @@ module type S = sig | Positive | Negative + (* TODO: hide these types (from the outside of [Msat]); + instead, provide well defined modules [module Lit : sig type t val …] + that define their API in Msat itself (not here) *) + type lit = { lid : int; (** Unique identifier *) term : term; (** Wrapped term *) @@ -55,8 +61,7 @@ module type S = sig vid : int; (** Unique identifier *) pa : atom; (** Link for the positive atom *) na : atom; (** Link for the negative atom *) - mutable used : int; (** Number of attached clause that contain the var *) - mutable seen : seen; (** Boolean used during propagation *) + mutable v_fields : Var_fields.t; (** bool fields *) mutable v_level : int; (** Level of decision/propagation *) mutable v_idx: int; (** rank in variable heap *) mutable v_weight : float; (** Variable weight (for the heap) *) @@ -178,8 +183,13 @@ module type S = sig val mark : atom -> unit (** Mark the atom as seen, using the 'seen' field in the variable. *) + val seen : atom -> bool (** Returns wether the atom has been marked as seen. *) + + val seen_both : var -> bool + (** both atoms have been seen? *) + val clear : var -> unit (** Clear the 'seen' field of the variable. *) From f5b4f5d0cbc4825ee2a3b584ad45b16f79abc086 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 18:14:32 +0100 Subject: [PATCH 023/182] update opam files --- msat.opam | 16 ++++------------ msat_solver.opam | 16 ++++------------ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/msat.opam b/msat.opam index 4131742c..fb16c28a 100644 --- a/msat.opam +++ b/msat.opam @@ -4,18 +4,10 @@ license: "Apache" version: "dev" author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: [ - [make "build"] -] -build-doc: [ - [make "doc"] -] -install: [ - [make "install"] -] -remove: [ - [make "uninstall"] -] +build: ["jbuilder" "build" "@install" "-p" name] +build-doc: ["jbuilder" "build" "@doc" "-p" name] +install: ["jbuilder" "install" name] +remove: ["jbuilder" "uninstall" name] depends: [ "ocamlfind" {build} "jbuilder" {build} diff --git a/msat_solver.opam b/msat_solver.opam index 8b090d0b..dfa006db 100644 --- a/msat_solver.opam +++ b/msat_solver.opam @@ -4,18 +4,10 @@ license: "Apache" version: "dev" author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: [ - [make "build"] -] -build-doc: [ - [make "doc"] -] -install: [ - [make "install"] -] -remove: [ - [make "uninstall"] -] +build: ["jbuilder" "build" "@install" "-p" name] +build-doc: ["jbuilder" "build" "@doc" "-p" name] +install: ["jbuilder" "install" name] +remove: ["jbuilder" "uninstall" name] depends: [ "ocamlfind" {build} "jbuilder" {build} From b92d8b39e7b84ddfcc9feb45171a5be96a613e3c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 18:48:21 +0100 Subject: [PATCH 024/182] remove useless dir --- src/util/array_util.ml | 24 --------------- src/util/hashcons.ml | 28 ----------------- src/util/sparse_vec.ml | 67 ----------------------------------------- src/util/sparse_vec.mli | 45 --------------------------- 4 files changed, 164 deletions(-) delete mode 100644 src/util/array_util.ml delete mode 100644 src/util/hashcons.ml delete mode 100644 src/util/sparse_vec.ml delete mode 100644 src/util/sparse_vec.mli diff --git a/src/util/array_util.ml b/src/util/array_util.ml deleted file mode 100644 index 2c3894ee..00000000 --- a/src/util/array_util.ml +++ /dev/null @@ -1,24 +0,0 @@ - -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** {1 Utils for Arrays} *) - -let exists p a = - let n = Array.length a in - let rec loop i = - if i = n then false - else if p (Array.unsafe_get a i) then true - else loop (succ i) in - loop 0 - -let for_all p a = - let n = Array.length a in - let rec loop i = - if i = n then true - else if p (Array.unsafe_get a i) then loop (succ i) - else false in - loop 0 diff --git a/src/util/hashcons.ml b/src/util/hashcons.ml deleted file mode 100644 index 2f2fa7e7..00000000 --- a/src/util/hashcons.ml +++ /dev/null @@ -1,28 +0,0 @@ - -module type ARG = sig - type t - val equal : t -> t -> bool - val hash : t -> int - val set_id : t -> int -> unit -end - -module Make(A : ARG): sig - val hashcons : A.t -> A.t - val iter : (A.t -> unit) -> unit -end = struct - module W = Weak.Make(A) - - let tbl_ = W.create 1024 - let n_ = ref 0 - - (* hashcons terms *) - let hashcons t = - let t' = W.merge tbl_ t in - if t == t' then ( - incr n_; - A.set_id t' !n_; - ); - t' - - let iter yield = W.iter yield tbl_ -end diff --git a/src/util/sparse_vec.ml b/src/util/sparse_vec.ml deleted file mode 100644 index 8ca1bb0c..00000000 --- a/src/util/sparse_vec.ml +++ /dev/null @@ -1,67 +0,0 @@ - -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** {1 Sparse vector, filled with default value} *) - -let _size_too_big()= - failwith "Sparse_vec: capacity exceeds maximum array size" - -type 'a t = { default : 'a; mutable data : 'a array; mutable sz : int } - -let make sz default = - if sz > Sys.max_array_length then _size_too_big(); - { default; sz; data=Array.make sz default; } - -let init sz f default = - if sz > Sys.max_array_length then _size_too_big(); - {data = Array.init sz (fun i -> f i); sz ; default} - -let length {sz} = sz - -let grow_to t new_capa = - assert (new_capa >= Array.length t.data); - let data = t.data in - let capa = Array.length data in - t.data <- Array.init new_capa (fun i -> if i < capa then data.(i) else t.default) - -let grow_to_by_double t new_capa = - if new_capa > Sys.max_array_length then _size_too_big (); - let data = t.data in - let capa = ref (Array.length data + 1) in - while !capa < new_capa do - capa := min (2 * !capa) Sys.max_array_length; - done; - grow_to t !capa - -let resize v len = - assert (len >= 0); - if len <= v.sz - then v.sz <- len - else ( - grow_to_by_double v len; - v.sz <- len - ) - -let incr v = resize v (v.sz + 1) - -let decr v = - if v.sz = 0 then invalid_arg "Sparse_vec.decr"; - resize v (v.sz - 1) - -let is_empty {sz} = sz=0 - -let clear v = v.sz <- 0 - -let get t i = - if i < 0 || i >= t.sz then invalid_arg "Sparse_vec.get"; - Array.unsafe_get t.data i - -let set t i v = - if i < 0 || i >= t.sz then invalid_arg "Sparse_vec.set"; - t.sz <- max t.sz i; - Array.unsafe_set t.data i v - diff --git a/src/util/sparse_vec.mli b/src/util/sparse_vec.mli deleted file mode 100644 index f547b792..00000000 --- a/src/util/sparse_vec.mli +++ /dev/null @@ -1,45 +0,0 @@ - -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** {1 Sparse vector, filled with default value} *) - -type 'a t -(** Abstract type of sparse vectors of 'a *) - -val make : int -> 'a -> 'a t -(** [make cap default] creates a new vector filled with [default]. The vector's - initial length is [cap] *) - -val init : int -> (int -> 'a) -> 'a -> 'a t -(** Same as {!Array.init}, but also with a default element *) - -val length : 'a t -> int -(** Range of valid indices *) - -val resize : 'a t -> int -> unit -(** Set the length of the array to [i] *) - -val incr : 'a t -> unit -(** Increment length size by one *) - -val decr : 'a t -> unit -(** Decrement length by one *) - -val is_empty : 'a t -> bool -(** Check whether length=0 *) - -val clear : 'a t -> unit -(** Set length to 0 *) - -val get : 'a t -> int -> 'a -(** get the element at the given index, or - @raise Invalid_argument if the index is not valid *) - -val set : 'a t -> int -> 'a -> unit -(** set the element at the given index, either already set or the first - free slot if [not (is_full vec)], or - @raise Invalid_argument if the index is not valid *) From 2b1bef9e3cfb9832bdc1b9a29aa1bc1ec9ffedc5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 18:55:01 +0100 Subject: [PATCH 025/182] ocpindent config --- .ocp-indent | 1 + 1 file changed, 1 insertion(+) diff --git a/.ocp-indent b/.ocp-indent index 5641e31d..1bfc9294 100644 --- a/.ocp-indent +++ b/.ocp-indent @@ -1,3 +1,4 @@ base=2 +with=0 type=2 max_indent=4 From 797e1b86fe5eff9ab7cd9e6bf6d16848e0d6358d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 19:12:32 +0100 Subject: [PATCH 026/182] makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 171013cd..f789c987 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ reindent: ocp-indent WATCH=all watch: - while find src/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ + while find src/ tests/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ echo "============ at `date` ==========" ; \ make $(WATCH); \ done From cbe3750b0d1b78e45b24bae1d8259e1ee0064baa Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 19:12:41 +0100 Subject: [PATCH 027/182] use generative functors, remove a layer of nesting for SMT libs --- src/core/BitField.ml | 2 +- src/core/BitField.mli | 2 +- src/core/External.ml | 3 +- src/core/External.mli | 2 +- src/core/Solver_types.ml | 4 +- src/core/Solver_types.mli | 4 +- src/core/Solver_types_intf.ml | 2 +- src/main/main.ml | 10 +-- src/mcsat/{mcsat.ml => Msat_mcsat.ml} | 8 +-- src/{smt/smt.mli => mcsat/Msat_mcsat.mli} | 2 +- src/mcsat/plugin_mcsat.ml | 2 +- src/sat/Expr_sat.ml | 70 +++++++++++++++++++ src/sat/Expr_sat.mli | 31 +++++++++ src/sat/Msat_sat.ml | 11 +++ src/sat/Msat_sat.mli | 17 +++++ src/sat/sat.ml | 82 ----------------------- src/sat/sat.mli | 47 ------------- src/sat/type_sat.ml | 4 +- src/sat/type_sat.mli | 2 +- src/smt/{smt.ml => Msat_smt.ml} | 7 +- src/{mcsat/mcsat.mli => smt/Msat_smt.mli} | 5 +- src/solver/mcsolver.ml | 4 +- src/solver/mcsolver.mli | 2 +- src/solver/solver.ml | 4 +- src/solver/solver.mli | 2 +- tests/test_api.ml | 5 +- 26 files changed, 170 insertions(+), 164 deletions(-) rename src/mcsat/{mcsat.ml => Msat_mcsat.ml} (56%) rename src/{smt/smt.mli => mcsat/Msat_mcsat.mli} (60%) create mode 100644 src/sat/Expr_sat.ml create mode 100644 src/sat/Expr_sat.mli create mode 100644 src/sat/Msat_sat.ml create mode 100644 src/sat/Msat_sat.mli delete mode 100644 src/sat/sat.ml delete mode 100644 src/sat/sat.mli rename src/smt/{smt.ml => Msat_smt.ml} (63%) rename src/{mcsat/mcsat.mli => smt/Msat_smt.mli} (52%) diff --git a/src/core/BitField.ml b/src/core/BitField.ml index bbb1f632..697320a8 100644 --- a/src/core/BitField.ml +++ b/src/core/BitField.ml @@ -107,7 +107,7 @@ let get_then_incr n = incr n; x -module Make(X : sig end) : S = struct +module Make() : S = struct type t = int let empty = 0 diff --git a/src/core/BitField.mli b/src/core/BitField.mli index 6821855e..ac92ffd8 100644 --- a/src/core/BitField.mli +++ b/src/core/BitField.mli @@ -62,7 +62,7 @@ module type S = sig end (** Create a new bitfield type *) -module Make(X : sig end) : S +module Make() : S (**/**) val all_bits_ : int -> int -> int diff --git a/src/core/External.ml b/src/core/External.ml index e14e03a7..5cc0994b 100644 --- a/src/core/External.ml +++ b/src/core/External.ml @@ -42,7 +42,8 @@ module Make (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - (Dummy : sig end) = struct + () += struct module St = St diff --git a/src/core/External.mli b/src/core/External.mli index 7a2c9ed8..38c2ca9e 100644 --- a/src/core/External.mli +++ b/src/core/External.mli @@ -18,7 +18,7 @@ module Make (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - (Dummy : sig end) : + () : S with module St = St (** Functor to make a safe external interface. *) diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 7d39f029..29245d33 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -27,7 +27,7 @@ let () = Var_fields.freeze() (* Solver types for McSat Solving *) (* ************************************************************************ *) -module McMake (E : Expr_intf.S)(Dummy : sig end) = struct +module McMake (E : Expr_intf.S)() = struct (* Flag for Mcsat v.s Pure Sat *) let mcsat = true @@ -375,7 +375,7 @@ end (* Solver types for pure SAT Solving *) (* ************************************************************************ *) -module SatMake (E : Formula_intf.S)(Dummy : sig end) = struct +module SatMake (E : Formula_intf.S)() = struct include McMake(struct include E module Term = E diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli index 1892d53c..62f1baaa 100644 --- a/src/core/Solver_types.mli +++ b/src/core/Solver_types.mli @@ -30,11 +30,11 @@ module type S = Solver_types_intf.S module Var_fields = Solver_types_intf.Var_fields -module McMake (E : Expr_intf.S)(Dummy : sig end): +module McMake (E : Expr_intf.S)(): S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) -module SatMake (E : Formula_intf.S)(Dummy : sig end): +module SatMake (E : Formula_intf.S)(): S with type term = E.t and type formula = E.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index ee97b4fb..6e69c570 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -22,7 +22,7 @@ Copyright 2016 Simon Cruanes used in the core solver. *) -module Var_fields = BitField.Make(struct end) +module Var_fields = BitField.Make() module type S = sig (** The signatures of clauses used in the Solver. *) diff --git a/src/main/main.ml b/src/main/main.ml index 30611d77..e098dac2 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -106,9 +106,9 @@ module Make Dolmen.Statement.print s end -module Sat = Make(Msat_sat.Sat.Make(struct end))(Msat_sat.Type_sat) -module Smt = Make(Msat_smt.Smt.Make(struct end))(Msat_smt.Type_smt) -module Mcsat = Make(Msat_mcsat.Mcsat.Make(struct end))(Msat_smt.Type_smt) +module Sat = Make(Msat_sat.Make(struct end))(Msat_sat.Type) +module Smt = Make(Msat_smt.Make(struct end))(Msat_smt.Type) +module Mcsat = Make(Msat_mcsat.Make(struct end))(Msat_smt.Type) let solver = ref (module Sat : S) let solver_list = [ @@ -227,8 +227,8 @@ let () = | Incorrect_model -> Format.printf "Internal error : incorrect *sat* model@."; exit 4 - | Msat_sat.Type_sat.Typing_error (msg, t) - | Msat_smt.Type_smt.Typing_error (msg, t) -> + | Msat_sat.Type.Typing_error (msg, t) + | Msat_smt.Type.Typing_error (msg, t) -> let b = Printexc.get_backtrace () in let loc = match t.Dolmen.Term.loc with | Some l -> l | None -> Dolmen.ParseLocation.mk "<>" 0 0 0 0 diff --git a/src/mcsat/mcsat.ml b/src/mcsat/Msat_mcsat.ml similarity index 56% rename from src/mcsat/mcsat.ml rename to src/mcsat/Msat_mcsat.ml index 10ad9720..b0e2e2b8 100644 --- a/src/mcsat/mcsat.ml +++ b/src/mcsat/Msat_mcsat.ml @@ -4,10 +4,10 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make(Dummy:sig end) = +module Make() = Msat_solver.Mcsolver.Make(struct type proof = unit - module Term = Msat_smt.Expr_smt.Term - module Formula = Msat_smt.Expr_smt.Atom - end)(Plugin_mcsat)(struct end) + module Term = Msat_smt.Expr.Term + module Formula = Msat_smt.Expr.Atom + end)(Plugin_mcsat)() diff --git a/src/smt/smt.mli b/src/mcsat/Msat_mcsat.mli similarity index 60% rename from src/smt/smt.mli rename to src/mcsat/Msat_mcsat.mli index 5cec2966..8055abc1 100644 --- a/src/smt/smt.mli +++ b/src/mcsat/Msat_mcsat.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make(Dummy: sig end) : Msat_solver.Solver.S with type St.formula = Expr_smt.atom +module Make() : Msat_solver.Solver.S with type St.formula = Msat_smt.Expr.atom diff --git a/src/mcsat/plugin_mcsat.ml b/src/mcsat/plugin_mcsat.ml index 610d7fc2..460b49fe 100644 --- a/src/mcsat/plugin_mcsat.ml +++ b/src/mcsat/plugin_mcsat.ml @@ -1,7 +1,7 @@ (* Module initialization *) -open Msat_smt +module Expr_smt = Msat_smt.Expr module E = Eclosure.Make(Expr_smt.Term) module H = Backtrack.Hashtbl(Expr_smt.Term) diff --git a/src/sat/Expr_sat.ml b/src/sat/Expr_sat.ml new file mode 100644 index 00000000..efb92778 --- /dev/null +++ b/src/sat/Expr_sat.ml @@ -0,0 +1,70 @@ + +exception Bad_atom +(** Exception raised if an atom cannot be created *) + +type proof +(** A empty type for proofs *) + +type t = int +(** Atoms are represented as integers. [-i] begin the negation of [i]. + Additionally, since we nee dot be able to create fresh atoms, we + use even integers for user-created atoms, and odd integers for the + fresh atoms. *) + +let max_lit = max_int + +(* Counters *) +let max_index = ref 0 +let max_fresh = ref (-1) + +(** Internal function for creating atoms. + Updates the internal counters *) +let _make i = + if i <> 0 && (abs i) < max_lit then begin + max_index := max !max_index (abs i); + i + end else + raise Bad_atom + +(** A dummy atom *) +let dummy = 0 + +(** *) +let neg a = - a + +let norm a = + abs a, if a < 0 then + Formula_intf.Negated + else + Formula_intf.Same_sign + +let abs = abs + +let sign i = i > 0 + +let apply_sign b i = if b then i else neg i + +let set_sign b i = if b then abs i else neg (abs i) + +let hash (a:int) = a land max_int +let equal (a:int) b = a=b +let compare (a:int) b = Pervasives.compare a b + +let make i = _make (2 * i) + +let fresh () = + incr max_fresh; + _make (2 * !max_fresh + 1) + +(* +let iter: (t -> unit) -> unit = fun f -> + for j = 1 to !max_index do + f j + done +*) + +let print fmt a = + Format.fprintf fmt "%s%s%d" + (if a < 0 then "~" else "") + (if a mod 2 = 0 then "v" else "f") + ((abs a) / 2) diff --git a/src/sat/Expr_sat.mli b/src/sat/Expr_sat.mli new file mode 100644 index 00000000..a48603af --- /dev/null +++ b/src/sat/Expr_sat.mli @@ -0,0 +1,31 @@ + +(** The module defining formulas *) + +(** SAT Formulas + + This modules implements formuals adequate for use in a pure SAT Solver. + Atomic formuals are represented using integers, that should allow + near optimal efficiency (both in terms of space and time). +*) + +include Formula_intf.S +(** This modules implements the requirements for implementing an SAT Solver. *) + +val make : int -> t +(** Make a proposition from an integer. *) + +val fresh : unit -> t +(** Make a fresh atom *) + +val compare : t -> t -> int +(** Compare atoms *) + +val sign : t -> bool +(** Is the given atom positive ? *) + +val apply_sign : bool -> t -> t +(** [apply_sign b] is the identity if [b] is [true], and the negation + function if [b] is [false]. *) + +val set_sign : bool -> t -> t +(** Return the atom with the sign set. *) diff --git a/src/sat/Msat_sat.ml b/src/sat/Msat_sat.ml new file mode 100644 index 00000000..a706355a --- /dev/null +++ b/src/sat/Msat_sat.ml @@ -0,0 +1,11 @@ +(* +MSAT is free software, using the Apache license, see file LICENSE +Copyright 2016 Guillaume Bury +*) + +module Expr = Expr_sat +module Type = Type_sat + +module Make() = + Msat_solver.Solver.Make(Expr)(Msat_solver.Solver.DummyTheory(Expr))() + diff --git a/src/sat/Msat_sat.mli b/src/sat/Msat_sat.mli new file mode 100644 index 00000000..a8c5fa5d --- /dev/null +++ b/src/sat/Msat_sat.mli @@ -0,0 +1,17 @@ +(* +MSAT is free software, using the Apache license, see file LICENSE +Copyright 2016 Guillaume Bury +*) + +(** Sat solver + + This modules instanciates a pure sat solver using integers to represent + atomic propositions. +*) + +module Expr = Expr_sat +module Type = Type_sat + +module Make() : Msat_solver.Solver.S with type St.formula = Expr.t +(** A functor that can generate as many solvers as needed. *) + diff --git a/src/sat/sat.ml b/src/sat/sat.ml deleted file mode 100644 index 5da9cebd..00000000 --- a/src/sat/sat.ml +++ /dev/null @@ -1,82 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -*) - -module Expr = struct - - exception Bad_atom - (** Exception raised if an atom cannot be created *) - - type proof - (** A empty type for proofs *) - - type t = int - (** Atoms are represented as integers. [-i] begin the negation of [i]. - Additionally, since we nee dot be able to create fresh atoms, we - use even integers for user-created atoms, and odd integers for the - fresh atoms. *) - - let max_lit = max_int - - (* Counters *) - let max_index = ref 0 - let max_fresh = ref (-1) - - (** Internal function for creating atoms. - Updates the internal counters *) - let _make i = - if i <> 0 && (abs i) < max_lit then begin - max_index := max !max_index (abs i); - i - end else - raise Bad_atom - - (** A dummy atom *) - let dummy = 0 - - (** *) - let neg a = - a - - let norm a = - abs a, if a < 0 then - Formula_intf.Negated - else - Formula_intf.Same_sign - - let abs = abs - - let sign i = i > 0 - - let apply_sign b i = if b then i else neg i - - let set_sign b i = if b then abs i else neg (abs i) - - let hash (a:int) = a land max_int - let equal (a:int) b = a=b - let compare (a:int) b = Pervasives.compare a b - - let make i = _make (2 * i) - - let fresh () = - incr max_fresh; - _make (2 * !max_fresh + 1) - - (* - let iter: (t -> unit) -> unit = fun f -> - for j = 1 to !max_index do - f j - done - *) - - let print fmt a = - Format.fprintf fmt "%s%s%d" - (if a < 0 then "~" else "") - (if a mod 2 = 0 then "v" else "f") - ((abs a) / 2) - -end - -module Make(Dummy : sig end) = - Msat_solver.Solver.Make(Expr)(Msat_solver.Solver.DummyTheory(Expr))(struct end) - diff --git a/src/sat/sat.mli b/src/sat/sat.mli deleted file mode 100644 index 500f740f..00000000 --- a/src/sat/sat.mli +++ /dev/null @@ -1,47 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -*) - -(** Sat solver - - This modules instanciates a pure sat solver using integers to represent - atomic propositions. -*) - -module Expr : sig - (** SAT Formulas - - This modules implements formuals adequate for use in a pure SAT Solver. - Atomic formuals are represented using integers, that should allow - near optimal efficiency (both in terms of space and time). - *) - - include Formula_intf.S - (** This modules implements the requirements for implementing an SAT Solver. *) - - val make : int -> t - (** Make a proposition from an integer. *) - - val fresh : unit -> t - (** Make a fresh atom *) - - val compare : t -> t -> int - (** Compare atoms *) - - val sign : t -> bool - (** Is the given atom positive ? *) - - val apply_sign : bool -> t -> t - (** [apply_sign b] is the identity if [b] is [true], and the negation - function if [b] is [false]. *) - - val set_sign : bool -> t -> t - (** Return the atom with the sign set. *) - -end -(** The module defining formulas *) - -module Make(Dummy : sig end) : Msat_solver.Solver.S with type St.formula = Expr.t -(** A functor that can generate as many solvers as needed. *) - diff --git a/src/sat/type_sat.ml b/src/sat/type_sat.ml index 426b16d7..b809cb4f 100644 --- a/src/sat/type_sat.ml +++ b/src/sat/type_sat.ml @@ -10,7 +10,7 @@ Copyright 2014 Simon Cruanes module Id = Dolmen.Id module Ast = Dolmen.Term module H = Hashtbl.Make(Id) -module Formula = Msat_tseitin.Make(Sat.Expr) +module Formula = Msat_tseitin.Make(Expr_sat) (* Exceptions *) (* ************************************************************************ *) @@ -26,7 +26,7 @@ let find_id id = try H.find symbols id with Not_found -> - let res = Sat.Expr.fresh () in + let res = Expr_sat.fresh () in H.add symbols id res; res diff --git a/src/sat/type_sat.mli b/src/sat/type_sat.mli index 512d358b..ae32ddd2 100644 --- a/src/sat/type_sat.mli +++ b/src/sat/type_sat.mli @@ -8,5 +8,5 @@ Copyright 2014 Simon Cruanes This module provides functions to parse terms from the untyped syntax tree defined in Dolmen, and generate formulas as defined in the Expr_sat module. *) -include Msat_solver.Type.S with type atom := Sat.Expr.t +include Msat_solver.Type.S with type atom := Expr_sat.t diff --git a/src/smt/smt.ml b/src/smt/Msat_smt.ml similarity index 63% rename from src/smt/smt.ml rename to src/smt/Msat_smt.ml index 1321f5bd..2cae3821 100644 --- a/src/smt/smt.ml +++ b/src/smt/Msat_smt.ml @@ -4,8 +4,11 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) +module Expr = Expr_smt +module Type = Type_smt + module Th = Msat_solver.Solver.DummyTheory(Expr_smt.Atom) -module Make(Dummy:sig end) = - Msat_solver.Solver.Make(Expr_smt.Atom)(Th)(struct end) +module Make() = + Msat_solver.Solver.Make(Expr_smt.Atom)(Th)() diff --git a/src/mcsat/mcsat.mli b/src/smt/Msat_smt.mli similarity index 52% rename from src/mcsat/mcsat.mli rename to src/smt/Msat_smt.mli index ddd06949..39f1231d 100644 --- a/src/mcsat/mcsat.mli +++ b/src/smt/Msat_smt.mli @@ -4,5 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make(Dummy: sig end) : Msat_solver.Solver.S with type St.formula = Msat_smt.Expr_smt.atom +module Expr = Expr_smt +module Type = Type_smt + +module Make() : Msat_solver.Solver.S with type St.formula = Expr_smt.atom diff --git a/src/solver/mcsolver.ml b/src/solver/mcsolver.ml index a0d7f62a..4c68e07c 100644 --- a/src/solver/mcsolver.ml +++ b/src/solver/mcsolver.ml @@ -10,10 +10,10 @@ module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) - (Dummy: sig end) = + () = External.Make (Solver_types.McMake(E)(struct end)) (Th) - (struct end) + () diff --git a/src/solver/mcsolver.mli b/src/solver/mcsolver.mli index 455eb92e..383fcc8f 100644 --- a/src/solver/mcsolver.mli +++ b/src/solver/mcsolver.mli @@ -16,7 +16,7 @@ module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) - (Dummy: sig end) : + () : S with type St.term = E.Term.t and type St.formula = E.Formula.t and type St.proof = E.proof diff --git a/src/solver/solver.ml b/src/solver/solver.ml index eee3afba..8f86a1f1 100644 --- a/src/solver/solver.ml +++ b/src/solver/solver.ml @@ -76,10 +76,10 @@ end module Make (E : Formula_intf.S) (Th : Theory_intf.S with type formula = E.t and type proof = E.proof) - (Dummy: sig end) = + () = External.Make (Solver_types.SatMake(E)(struct end)) (Plugin(E)(Th)) - (struct end) + () diff --git a/src/solver/solver.mli b/src/solver/solver.mli index e3400d36..2480e453 100644 --- a/src/solver/solver.mli +++ b/src/solver/solver.mli @@ -23,7 +23,7 @@ module DummyTheory(F : Formula_intf.S) : module Make (F : Formula_intf.S) (Th : Theory_intf.S with type formula = F.t and type proof = F.proof) - (Dummy: sig end) : + () : S with type St.formula = F.t and type St.proof = F.proof (** Functor to create a SMT Solver parametrised by the atomic diff --git a/tests/test_api.ml b/tests/test_api.ml index 8984c12d..fc41286e 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -7,9 +7,8 @@ Copyright 2014 Simon Cruanes (* Tests that require the API *) open Msat -open Msat_sat -module F = Sat.Expr +module F = Msat_sat.Expr module T = Msat_tseitin.Make(F) let (|>) x f = f x @@ -47,7 +46,7 @@ end let mk_solver (): (module BASIC_SOLVER) = let module S = struct - include Sat.Make(struct end) + include Msat_sat.Make(struct end) let solve ?assumptions ()= match solve ?assumptions() with | Sat _ -> R_sat From 9bc85160b8a96ac704ac537278ef561f28947da9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 19:23:36 +0100 Subject: [PATCH 028/182] restrict what Msat core lib exposes, provide shortcuts --- src/backend/Dimacs.ml | 2 +- src/backend/Dimacs.mli | 2 +- src/core/Msat.ml | 39 +++++++++++++++++++++++++++ src/core/{External.ml => Solver.ml} | 0 src/core/{External.mli => Solver.mli} | 0 src/main/main.ml | 6 ++--- src/solver/mcsolver.ml | 6 ++--- src/solver/mcsolver.mli | 2 +- src/solver/solver.ml | 6 ++--- src/solver/solver.mli | 2 +- tests/test_api.ml | 2 +- 11 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 src/core/Msat.ml rename src/core/{External.ml => Solver.ml} (100%) rename src/core/{External.mli => Solver.mli} (100%) diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index cbec4c32..f2445d9f 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -32,7 +32,7 @@ module type S = sig end -module Make(St : Solver_types.S)(Dummy: sig end) = struct +module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct (* Dimacs & iCNF export *) let export_vec name fmt vec = diff --git a/src/backend/Dimacs.mli b/src/backend/Dimacs.mli index dcd11b47..0ad94e63 100644 --- a/src/backend/Dimacs.mli +++ b/src/backend/Dimacs.mli @@ -42,6 +42,6 @@ module type S = sig end -module Make(St: Solver_types.S)(Dummy: sig end) : S with type clause := St.clause +module Make(St: Solver_types_intf.S)(Dummy: sig end) : S with type clause := St.clause (** Functor to create a module for exporting probems to the dimacs (& iCNF) formats. *) diff --git a/src/core/Msat.ml b/src/core/Msat.ml new file mode 100644 index 00000000..daa470c2 --- /dev/null +++ b/src/core/Msat.ml @@ -0,0 +1,39 @@ + +(** Main API *) + +module Formula_intf = Formula_intf +module Plugin_intf = Plugin_intf +module Theory_intf = Theory_intf +module Expr_intf = Expr_intf +module Solver_types_intf = Solver_types_intf + +module Res = Res + +module type S = Solver_intf.S + +type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { + eval : 'form -> bool; + eval_level : 'form -> bool * int; + iter_trail : ('form -> unit) -> ('term -> unit) -> unit; + model : unit -> ('term * 'term) list; +} + +type ('clause, 'proof) unsat_state = ('clause, 'proof) Solver_intf.unsat_state = { + unsat_conflict : unit -> 'clause; + get_proof : unit -> 'proof; +} +type 'clause export = 'clause Solver_intf.export = { + hyps : 'clause Vec.t; + history : 'clause Vec.t; + local : 'clause Vec.t; +} + +module Make_smt_expr(E : Formula_intf.S) = Solver_types.SatMake(E) +module Make_mcsat_expr(E : Expr_intf.S) = Solver_types.McMake(E) + +module Make = Solver.Make + +(**/**) +module Vec = Vec +module Log = Log +(**/**) diff --git a/src/core/External.ml b/src/core/Solver.ml similarity index 100% rename from src/core/External.ml rename to src/core/Solver.ml diff --git a/src/core/External.mli b/src/core/Solver.mli similarity index 100% rename from src/core/External.mli rename to src/core/Solver.mli diff --git a/src/main/main.ml b/src/main/main.ml index e098dac2..69a2e727 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -27,7 +27,7 @@ module type S = sig end module Make - (S : External.S) + (S : Msat.S) (T : Msat_solver.Type.S with type atom := S.atom) : sig val do_task : Dolmen.Statement.t -> unit @@ -42,7 +42,7 @@ module Make let l = List.map (function a -> Log.debugf 99 (fun k -> k "Checking value of %a" S.St.pp_atom (S.St.add_atom a)); - state.Solver_intf.eval a) c in + state.Msat.eval a) c in List.exists (fun x -> x) l in let l = List.map check_clause !hyps in @@ -60,7 +60,7 @@ module Make Format.printf "Sat (%f/%f)@." t t' | S.Unsat state -> if !p_check then begin - let p = state.Solver_intf.get_proof () in + let p = state.Msat.get_proof () in S.Proof.check p; if !p_dot_proof <> "" then begin let fmt = Format.formatter_of_out_channel (open_out !p_dot_proof) in diff --git a/src/solver/mcsolver.ml b/src/solver/mcsolver.ml index 4c68e07c..a81bf7ae 100644 --- a/src/solver/mcsolver.ml +++ b/src/solver/mcsolver.ml @@ -4,15 +4,15 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module type S = Solver_intf.S +module type S = Msat.S module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) () = - External.Make - (Solver_types.McMake(E)(struct end)) + Msat.Make + (Make_mcsat_expr(E)()) (Th) () diff --git a/src/solver/mcsolver.mli b/src/solver/mcsolver.mli index 383fcc8f..7ae92fad 100644 --- a/src/solver/mcsolver.mli +++ b/src/solver/mcsolver.mli @@ -9,7 +9,7 @@ Copyright 2014 Simon Cruanes This module provides a functor to create an McSAt solver. *) -module type S = Solver_intf.S +module type S = Msat.S (** The interface exposed by the solver. *) module Make (E : Expr_intf.S) diff --git a/src/solver/solver.ml b/src/solver/solver.ml index 8f86a1f1..8ad787c5 100644 --- a/src/solver/solver.ml +++ b/src/solver/solver.ml @@ -10,7 +10,7 @@ (* *) (**************************************************************************) -module type S = Solver_intf.S +module type S = Msat.S module DummyTheory(F : Formula_intf.S) = struct (* We don't have anything to do since the SAT Solver already @@ -77,8 +77,8 @@ end module Make (E : Formula_intf.S) (Th : Theory_intf.S with type formula = E.t and type proof = E.proof) () = - External.Make - (Solver_types.SatMake(E)(struct end)) + Msat.Make + (Make_smt_expr(E)(struct end)) (Plugin(E)(Th)) () diff --git a/src/solver/solver.mli b/src/solver/solver.mli index 2480e453..37840eb7 100644 --- a/src/solver/solver.mli +++ b/src/solver/solver.mli @@ -11,7 +11,7 @@ Copyright 2014 Simon Cruanes functor in order to create a pure SAT solver. *) -module type S = Solver_intf.S +module type S = Msat.S (** The interface of instantiated solvers. *) module DummyTheory(F : Formula_intf.S) : diff --git a/tests/test_api.ml b/tests/test_api.ml index fc41286e..2d9aa422 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -51,7 +51,7 @@ let mk_solver (): (module BASIC_SOLVER) = | Sat _ -> R_sat | Unsat us -> - let p = us.Solver_intf.get_proof () in + let p = us.Msat.get_proof () in Proof.check p; R_unsat end From eff8ed1c4ffab26df6170c024557855494618b11 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 19:43:54 +0100 Subject: [PATCH 029/182] split some features into `minismt` lib --- minismt.opam | 23 +++++++++++++++++++ src/main/jbuild | 2 +- src/main/main.ml | 12 +++++----- src/mcsat/{Msat_mcsat.ml => Minismt_mcsat.ml} | 6 ++--- .../{Msat_mcsat.mli => Minismt_mcsat.mli} | 2 +- src/mcsat/jbuild | 6 ++--- src/mcsat/plugin_mcsat.ml | 2 +- src/sat/{Msat_sat.ml => Minismt_sat.ml} | 2 +- src/sat/{Msat_sat.mli => Minismt_sat.mli} | 2 +- src/sat/jbuild | 6 ++--- src/sat/type_sat.mli | 2 +- src/smt/{Msat_smt.ml => Minismt_smt.ml} | 5 ++-- src/smt/{Msat_smt.mli => Minismt_smt.mli} | 2 +- src/smt/jbuild | 6 ++--- src/smt/type_smt.mli | 2 +- src/solver/jbuild | 6 ++--- tests/jbuild | 2 +- tests/test_api.ml | 4 ++-- 18 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 minismt.opam rename src/mcsat/{Msat_mcsat.ml => Minismt_mcsat.ml} (62%) rename src/mcsat/{Msat_mcsat.mli => Minismt_mcsat.mli} (62%) rename src/sat/{Msat_sat.ml => Minismt_sat.ml} (69%) rename src/sat/{Msat_sat.mli => Minismt_sat.mli} (83%) rename src/smt/{Msat_smt.ml => Minismt_smt.ml} (59%) rename src/smt/{Msat_smt.mli => Minismt_smt.mli} (70%) diff --git a/minismt.opam b/minismt.opam new file mode 100644 index 00000000..1236ffbb --- /dev/null +++ b/minismt.opam @@ -0,0 +1,23 @@ +opam-version: "1.2" +name: "minismt" +license: "Apache" +version: "dev" +author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] +maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] +build: ["jbuilder" "build" "@install" "-p" name] +build-doc: ["jbuilder" "build" "@doc" "-p" name] +install: ["jbuilder" "install" name] +remove: ["jbuilder" "uninstall" name] +depends: [ + "ocamlfind" {build} + "jbuilder" {build} + "dolmen" +] +available: [ + ocaml-version >= "4.03.0" +] +tags: [ "sat" "smt" ] +homepage: "https://github.com/Gbury/mSAT" +dev-repo: "https://github.com/Gbury/mSAT.git" +bug-reports: "https://github.com/Gbury/mSAT/issues/" + diff --git a/src/main/jbuild b/src/main/jbuild index 08a42162..19dae05d 100644 --- a/src/main/jbuild +++ b/src/main/jbuild @@ -6,7 +6,7 @@ ((name main) (public_name msat_solver) (package msat_solver) - (libraries (msat msat.backend msat.sat msat.smt msat.mcsat dolmen)) + (libraries (msat msat.backend minismt.sat minismt.smt minismt.mcsat dolmen)) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) (ocamlopt_flags (:standard -O3 -color always -unbox-closures -unbox-closures-factor 20)) diff --git a/src/main/main.ml b/src/main/main.ml index 69a2e727..f018d8fa 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -28,7 +28,7 @@ end module Make (S : Msat.S) - (T : Msat_solver.Type.S with type atom := S.atom) + (T : Minismt.Type.S with type atom := S.atom) : sig val do_task : Dolmen.Statement.t -> unit end = struct @@ -106,9 +106,9 @@ module Make Dolmen.Statement.print s end -module Sat = Make(Msat_sat.Make(struct end))(Msat_sat.Type) -module Smt = Make(Msat_smt.Make(struct end))(Msat_smt.Type) -module Mcsat = Make(Msat_mcsat.Make(struct end))(Msat_smt.Type) +module Sat = Make(Minismt_sat.Make(struct end))(Minismt_sat.Type) +module Smt = Make(Minismt_smt.Make(struct end))(Minismt_smt.Type) +module Mcsat = Make(Minismt_mcsat.Make(struct end))(Minismt_smt.Type) let solver = ref (module Sat : S) let solver_list = [ @@ -227,8 +227,8 @@ let () = | Incorrect_model -> Format.printf "Internal error : incorrect *sat* model@."; exit 4 - | Msat_sat.Type.Typing_error (msg, t) - | Msat_smt.Type.Typing_error (msg, t) -> + | Minismt_sat.Type.Typing_error (msg, t) + | Minismt_smt.Type.Typing_error (msg, t) -> let b = Printexc.get_backtrace () in let loc = match t.Dolmen.Term.loc with | Some l -> l | None -> Dolmen.ParseLocation.mk "<>" 0 0 0 0 diff --git a/src/mcsat/Msat_mcsat.ml b/src/mcsat/Minismt_mcsat.ml similarity index 62% rename from src/mcsat/Msat_mcsat.ml rename to src/mcsat/Minismt_mcsat.ml index b0e2e2b8..fcd928e3 100644 --- a/src/mcsat/Msat_mcsat.ml +++ b/src/mcsat/Minismt_mcsat.ml @@ -5,9 +5,9 @@ Copyright 2014 Simon Cruanes *) module Make() = - Msat_solver.Mcsolver.Make(struct + Minismt.Mcsolver.Make(struct type proof = unit - module Term = Msat_smt.Expr.Term - module Formula = Msat_smt.Expr.Atom + module Term = Minismt_smt.Expr.Term + module Formula = Minismt_smt.Expr.Atom end)(Plugin_mcsat)() diff --git a/src/mcsat/Msat_mcsat.mli b/src/mcsat/Minismt_mcsat.mli similarity index 62% rename from src/mcsat/Msat_mcsat.mli rename to src/mcsat/Minismt_mcsat.mli index 8055abc1..254699a6 100644 --- a/src/mcsat/Msat_mcsat.mli +++ b/src/mcsat/Minismt_mcsat.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make() : Msat_solver.Solver.S with type St.formula = Msat_smt.Expr.atom +module Make() : Minismt.Solver.S with type St.formula = Minismt_smt.Expr.atom diff --git a/src/mcsat/jbuild b/src/mcsat/jbuild index 5c4061d3..692db5f8 100644 --- a/src/mcsat/jbuild +++ b/src/mcsat/jbuild @@ -1,9 +1,9 @@ ; vim:ft=lisp: (library - ((name msat_mcsat) - (public_name msat.mcsat) - (libraries (msat msat.solver msat.smt)) + ((name minismt_mcsat) + (public_name minismt.mcsat) + (libraries (msat minismt minismt.smt)) (synopsis "mcsat interface") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot diff --git a/src/mcsat/plugin_mcsat.ml b/src/mcsat/plugin_mcsat.ml index 460b49fe..d1c588c7 100644 --- a/src/mcsat/plugin_mcsat.ml +++ b/src/mcsat/plugin_mcsat.ml @@ -1,7 +1,7 @@ (* Module initialization *) -module Expr_smt = Msat_smt.Expr +module Expr_smt = Minismt_smt.Expr module E = Eclosure.Make(Expr_smt.Term) module H = Backtrack.Hashtbl(Expr_smt.Term) diff --git a/src/sat/Msat_sat.ml b/src/sat/Minismt_sat.ml similarity index 69% rename from src/sat/Msat_sat.ml rename to src/sat/Minismt_sat.ml index a706355a..d1e8b933 100644 --- a/src/sat/Msat_sat.ml +++ b/src/sat/Minismt_sat.ml @@ -7,5 +7,5 @@ module Expr = Expr_sat module Type = Type_sat module Make() = - Msat_solver.Solver.Make(Expr)(Msat_solver.Solver.DummyTheory(Expr))() + Minismt.Solver.Make(Expr)(Minismt.Solver.DummyTheory(Expr))() diff --git a/src/sat/Msat_sat.mli b/src/sat/Minismt_sat.mli similarity index 83% rename from src/sat/Msat_sat.mli rename to src/sat/Minismt_sat.mli index a8c5fa5d..8c34b184 100644 --- a/src/sat/Msat_sat.mli +++ b/src/sat/Minismt_sat.mli @@ -12,6 +12,6 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat module Type = Type_sat -module Make() : Msat_solver.Solver.S with type St.formula = Expr.t +module Make() : Minismt.Solver.S with type St.formula = Expr.t (** A functor that can generate as many solvers as needed. *) diff --git a/src/sat/jbuild b/src/sat/jbuild index 86386d7a..82068bfb 100644 --- a/src/sat/jbuild +++ b/src/sat/jbuild @@ -1,9 +1,9 @@ ; vim:ft=lisp: (library - ((name msat_sat) - (public_name msat.sat) - (libraries (msat msat.tseitin msat.solver)) + ((name minismt_sat) + (public_name minismt.sat) + (libraries (msat msat.tseitin minismt dolmen)) (synopsis "sat interface") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot diff --git a/src/sat/type_sat.mli b/src/sat/type_sat.mli index ae32ddd2..a088602c 100644 --- a/src/sat/type_sat.mli +++ b/src/sat/type_sat.mli @@ -8,5 +8,5 @@ Copyright 2014 Simon Cruanes This module provides functions to parse terms from the untyped syntax tree defined in Dolmen, and generate formulas as defined in the Expr_sat module. *) -include Msat_solver.Type.S with type atom := Expr_sat.t +include Minismt.Type.S with type atom := Expr_sat.t diff --git a/src/smt/Msat_smt.ml b/src/smt/Minismt_smt.ml similarity index 59% rename from src/smt/Msat_smt.ml rename to src/smt/Minismt_smt.ml index 2cae3821..6d25bf9b 100644 --- a/src/smt/Msat_smt.ml +++ b/src/smt/Minismt_smt.ml @@ -7,8 +7,7 @@ Copyright 2014 Simon Cruanes module Expr = Expr_smt module Type = Type_smt -module Th = Msat_solver.Solver.DummyTheory(Expr_smt.Atom) +module Th = Minismt.Solver.DummyTheory(Expr.Atom) -module Make() = - Msat_solver.Solver.Make(Expr_smt.Atom)(Th)() +module Make() = Minismt.Solver.Make(Expr.Atom)(Th)() diff --git a/src/smt/Msat_smt.mli b/src/smt/Minismt_smt.mli similarity index 70% rename from src/smt/Msat_smt.mli rename to src/smt/Minismt_smt.mli index 39f1231d..d2e99f39 100644 --- a/src/smt/Msat_smt.mli +++ b/src/smt/Minismt_smt.mli @@ -7,5 +7,5 @@ Copyright 2014 Simon Cruanes module Expr = Expr_smt module Type = Type_smt -module Make() : Msat_solver.Solver.S with type St.formula = Expr_smt.atom +module Make() : Minismt.Solver.S with type St.formula = Expr_smt.atom diff --git a/src/smt/jbuild b/src/smt/jbuild index 843fa9c3..9999f849 100644 --- a/src/smt/jbuild +++ b/src/smt/jbuild @@ -1,9 +1,9 @@ ; vim:ft=lisp: (library - ((name msat_smt) - (public_name msat.smt) - (libraries (msat msat.solver msat.tseitin dolmen)) + ((name minismt_smt) + (public_name minismt.smt) + (libraries (msat minismt msat.tseitin dolmen)) (synopsis "smt interface") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot diff --git a/src/smt/type_smt.mli b/src/smt/type_smt.mli index 7593432b..1613debb 100644 --- a/src/smt/type_smt.mli +++ b/src/smt/type_smt.mli @@ -3,5 +3,5 @@ This module provides functions to parse terms from the untyped syntax tree defined in Dolmen, and generate formulas as defined in the Expr_smt module. *) -include Msat_solver.Type.S with type atom := Expr_smt.Atom.t +include Minismt.Type.S with type atom := Expr_smt.Atom.t diff --git a/src/solver/jbuild b/src/solver/jbuild index f9e40772..f3176323 100644 --- a/src/solver/jbuild +++ b/src/solver/jbuild @@ -1,10 +1,10 @@ ; vim:ft=lisp: (library - ((name msat_solver) - (public_name msat.solver) + ((name minismt) + (public_name minismt) (libraries (msat dolmen)) - (synopsis "mcsat solver util") + (synopsis "minismt") (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) (ocamlopt_flags (:standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20)) diff --git a/tests/jbuild b/tests/jbuild index 988fae01..deab296d 100644 --- a/tests/jbuild +++ b/tests/jbuild @@ -2,7 +2,7 @@ (executable ((name test_api) - (libraries (msat msat.backend msat.sat msat.smt msat.mcsat dolmen)) + (libraries (msat msat.tseitin msat.backend minismt.sat minismt.smt minismt.mcsat dolmen)) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) (ocamlopt_flags (:standard -O3 -color always -unbox-closures -unbox-closures-factor 20)) diff --git a/tests/test_api.ml b/tests/test_api.ml index 2d9aa422..69c1e526 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -8,7 +8,7 @@ Copyright 2014 Simon Cruanes open Msat -module F = Msat_sat.Expr +module F = Minismt_sat.Expr module T = Msat_tseitin.Make(F) let (|>) x f = f x @@ -46,7 +46,7 @@ end let mk_solver (): (module BASIC_SOLVER) = let module S = struct - include Msat_sat.Make(struct end) + include Minismt_sat.Make(struct end) let solve ?assumptions ()= match solve ?assumptions() with | Sat _ -> R_sat From 8550102ea6e0bd81a275e8326e730e7739d51b4d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2017 19:47:05 +0100 Subject: [PATCH 030/182] dependencies in opam files; put binary in `minismt` package --- minismt.opam | 1 + msat_solver.opam | 23 ----------------------- src/main/jbuild | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 msat_solver.opam diff --git a/minismt.opam b/minismt.opam index 1236ffbb..a38b471a 100644 --- a/minismt.opam +++ b/minismt.opam @@ -12,6 +12,7 @@ depends: [ "ocamlfind" {build} "jbuilder" {build} "dolmen" + "msat" ] available: [ ocaml-version >= "4.03.0" diff --git a/msat_solver.opam b/msat_solver.opam deleted file mode 100644 index dfa006db..00000000 --- a/msat_solver.opam +++ /dev/null @@ -1,23 +0,0 @@ -opam-version: "1.2" -name: "msat" -license: "Apache" -version: "dev" -author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] -maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: ["jbuilder" "build" "@install" "-p" name] -build-doc: ["jbuilder" "build" "@doc" "-p" name] -install: ["jbuilder" "install" name] -remove: ["jbuilder" "uninstall" name] -depends: [ - "ocamlfind" {build} - "jbuilder" {build} - "dolmen" -] -available: [ - ocaml-version >= "4.03.0" -] -tags: [ "sat" "smt" ] -homepage: "https://github.com/Gbury/mSAT" -dev-repo: "https://github.com/Gbury/mSAT.git" -bug-reports: "https://github.com/Gbury/mSAT/issues/" - diff --git a/src/main/jbuild b/src/main/jbuild index 19dae05d..8de51a2e 100644 --- a/src/main/jbuild +++ b/src/main/jbuild @@ -5,7 +5,7 @@ (executable ((name main) (public_name msat_solver) - (package msat_solver) + (package minismt) (libraries (msat msat.backend minismt.sat minismt.smt minismt.mcsat dolmen)) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) (ocamlopt_flags (:standard -O3 -color always From 8eef2deebd09d024c41cbfbad3e1551e9a91d276 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 12:32:27 +0100 Subject: [PATCH 031/182] faster addition of clauses' watch literals instead of sorting the whole clause, just select two highest level lits --- src/core/Internal.ml | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 158770c3..75482078 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -566,6 +566,33 @@ module Make Log.debugf debug (fun k -> k "Enqueue (%d): %a" (Vec.size env.elt_queue) pp_lit l) + (* swap elements of array *) + let[@inline] swap_arr a i j = + if i<>j then ( + let tmp = a.(i) in + a.(i) <- a.(j); + a.(j) <- tmp; + ) + + (* move atoms assigned at high levels first *) + let[@inline] put_high_level_atoms_first (arr:atom array) : unit = + Array.iteri + (fun i a -> + if i>0 && a.var.v_level > arr.(0).var.v_level then ( + (* move first to second, [i]-th to first, second to [i] *) + if i=1 then ( + swap_arr arr 0 1; + ) else ( + let tmp = arr.(1) in + arr.(1) <- arr.(0); + arr.(0) <- arr.(i); + arr.(i) <- tmp; + ); + ) else if i>1 && a.var.v_level > arr.(1).var.v_level then ( + swap_arr arr 1 i; + )) + arr + (* evaluate an atom for MCsat, if it's not assigned by boolean propagation/decision *) let th_eval a : bool option = @@ -830,9 +857,7 @@ module Make if a.neg.is_true then begin (* Atoms need to be sorted in decreasing order of decision level, or we might watch the wrong literals. *) - Array.sort - (fun a b -> compare b.var.v_level a.var.v_level) - clause.atoms; + put_high_level_atoms_first clause.atoms; attach_clause clause; add_boolean_conflict clause end else begin From eff3f8024f997462bca20adf2f5c04853c93f3e7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 15:29:04 +0100 Subject: [PATCH 032/182] wip: use submodules of Solver_types to clean up code --- src/backend/Coq.ml | 22 +- src/backend/Dimacs.ml | 35 ++- src/backend/Dot.ml | 24 +- src/core/Internal.ml | 430 ++++++++++++++++---------------- src/core/Internal.mli | 2 +- src/core/Res.ml | 54 ++-- src/core/Solver.ml | 43 +--- src/core/Solver_types.ml | 450 +++++++++++++++++++--------------- src/core/Solver_types_intf.ml | 177 ++++++++----- src/main/main.ml | 2 +- 10 files changed, 669 insertions(+), 570 deletions(-) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index efdbe8b3..47fa6e3e 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -22,12 +22,11 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct - module M = Map.Make(struct - type t = S.St.atom - let compare a b = compare a.S.St.aid b.S.St.aid - end) + module Atom = S.St.Atom + module Clause = S.St.Clause + module M = Map.Make(S.St.Atom) - let name c = c.S.St.name + let name = S.St.Clause.name let clause_map c = let rec aux acc a i = @@ -70,27 +69,26 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause )) h1.S.St.atoms let resolution fmt goal hyp1 hyp2 atom = - let a = S.St.(atom.var.pa) in + let a = Atom.abs atom in let h1, h2 = - if Array.exists ((==) a) hyp1.S.St.atoms then hyp1, hyp2 - else (assert (Array.exists ((==) a) hyp2.S.St.atoms); hyp2, hyp1) + if Array.exists (Atom.equal a) hyp1.S.St.atoms then hyp1, hyp2 + else (assert (Array.exists (Atom.equal a) hyp2.S.St.atoms); hyp2, hyp1) in (** Print some debug info *) Format.fprintf fmt "(* Clausal resolution. Goal : %s ; Hyps : %s, %s *)@\n" (name goal) (name h1) (name h2); (** Prove the goal: intro the axioms, then perform resolution *) - if Array.length goal.S.St.atoms = 0 then begin + if Array.length goal.S.St.atoms = 0 then ( let m = M.empty in Format.fprintf fmt "exact @[(%a)@].@\n" (resolution_aux m a h1 h2) (); false - end else begin + ) else ( let m = clause_map goal in Format.fprintf fmt "pose proof @[(fun %a=>@ %a)@ as %s.@]@\n" (clause_iter m "%s@ ") goal (resolution_aux m a h1 h2) () (name goal); true - end - + ) (* Count uses of hypotheses *) let incr_use h c = diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index f2445d9f..a4e35b0f 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -36,10 +36,10 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct (* Dimacs & iCNF export *) let export_vec name fmt vec = - Format.fprintf fmt "c %s@,%a@," name (Vec.print ~sep:"" St.pp_dimacs) vec + Format.fprintf fmt "c %s@,%a@," name (Vec.print ~sep:"" St.Clause.pp_dimacs) vec let export_assumption fmt vec = - Format.fprintf fmt "c Local assumptions@,a %a@," St.pp_dimacs vec + Format.fprintf fmt "c Local assumptions@,a %a@," St.Clause.pp_dimacs vec let export_icnf_aux r name map_filter fmt vec = let aux fmt _ = @@ -47,28 +47,28 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct let x = Vec.get vec i in match map_filter x with | None -> () - | Some _ -> Format.fprintf fmt "%a@," St.pp_dimacs (Vec.get vec i) + | Some _ -> Format.fprintf fmt "%a@," St.Clause.pp_dimacs (Vec.get vec i) done; r := Vec.size vec in Format.fprintf fmt "c %s@,%a" name aux vec let map_filter_learnt c = - match c.St.cpremise with + match St.Clause.premise c with | St.Hyp | St.Local -> assert false | St.Lemma _ -> Some c | St.History l -> begin match l with | [] -> assert false | d :: _ -> - begin match d.St.cpremise with + begin match St.Clause.premise d with | St.Lemma _ -> Some d | St.Hyp | St.Local | St.History _ -> None end end let filter_vec learnt = - let lemmas = Vec.make (Vec.size learnt) St.dummy_clause in + let lemmas = Vec.make (Vec.size learnt) St.Clause.dummy in Vec.iter (fun c -> match map_filter_learnt c with | None -> () @@ -77,17 +77,13 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct lemmas let export fmt ~hyps ~history ~local = - assert (Vec.for_all (function - | { St.cpremise = St.Hyp; _} -> true | _ -> false - ) hyps); + assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); (* Learnt clauses, then filtered to only keep only the theory lemmas; all other learnt clauses should be logical consequences of the rest. *) let lemmas = filter_vec history in (* Local assertions *) - assert (Vec.for_all (function - | { St.cpremise = St.Local; _} -> true | _ -> false - ) local); + assert (Vec.for_all (fun c -> St.Local = St.Clause.premise c) local); (* Number of atoms and clauses *) let n = St.nb_elt () in let m = Vec.size local + Vec.size hyps + Vec.size lemmas in @@ -102,15 +98,16 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct let icnf_lemmas = ref 0 let export_icnf fmt ~hyps ~history ~local = - assert (Vec.for_all (function - | { St.cpremise = St.Hyp; _} -> true | _ -> false - ) hyps); + assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); let lemmas = history in (* Local assertions *) - let l = List.map (function - | {St.cpremise = St.Local; atoms = [| a |];_ } -> a - | _ -> assert false) (Vec.to_list local) in - let local = St.make_clause "local (tmp)" l St.Local in + let l = List.map + (fun c -> match St.Clause.premise c, St.Clause.atoms c with + | St.Local, [| a |] -> a + | _ -> assert false) + (Vec.to_list local) + in + let local = St.Clause.make l St.Local in (* Number of atoms and clauses *) Format.fprintf fmt "@[%s@,%a%a%a@]@." diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 78c160cb..2fde8287 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -31,20 +31,22 @@ module type Arg = sig end module Default(S : Res.S) = struct + module Atom = S.St.Atom + module Clause = S.St.Clause - let print_atom = S.St.print_atom + let print_atom = Atom.pp let hyp_info c = "hypothesis", Some "LIGHTBLUE", - [ fun fmt () -> Format.fprintf fmt "%s" c.S.St.name] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] let lemma_info c = "lemma", Some "BLUE", - [ fun fmt () -> Format.fprintf fmt "%s" c.S.St.name] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] let assumption_info c = "assumption", Some "PURPLE", - [ fun fmt () -> Format.fprintf fmt "%s" c.S.St.name] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] end @@ -53,15 +55,17 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct + module Atom = S.St.Atom + module Clause = S.St.Clause - let node_id n = n.S.conclusion.S.St.name + let node_id n = Clause.name n.S.conclusion let res_node_id n = (node_id n) ^ "_res" let proof_id p = node_id (S.expand p) let print_clause fmt c = - let v = c.S.St.atoms in + let v = Clause.atoms c in if Array.length v = 0 then Format.fprintf fmt "⊥" else @@ -149,9 +153,11 @@ module Simple(S : Res.S) and type lemma := S.lemma and type assumption = S.St.formula) = Make(S)(struct + module Atom = S.St.Atom + module Clause = S.St.Clause (* Some helpers *) - let lit a = a.S.St.lit + let lit = Atom.lit let get_assumption c = match S.to_list c with @@ -159,13 +165,13 @@ module Simple(S : Res.S) | _ -> assert false let get_lemma c = - match c.S.St.cpremise with + match Clause.premise c with | S.St.Lemma p -> p | _ -> assert false (* Actual functions *) let print_atom fmt a = - A.print_atom fmt a.S.St.lit + A.print_atom fmt (Atom.lit a) let hyp_info c = A.hyp_info (List.map lit (S.to_list c)) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 75482078..b88f8fdd 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -16,11 +16,11 @@ module Make open St module H = Heap.Make(struct - type t = St.elt - let[@inline] cmp i j = get_elt_weight j < get_elt_weight i (* comparison by weight *) - let dummy = elt_of_var St.dummy_var - let idx = get_elt_idx - let set_idx = set_elt_idx + type t = St.Elt.t + let[@inline] cmp i j = Elt.weight j < Elt.weight i (* comparison by weight *) + let dummy = Elt.of_var St.Var.dummy + let idx = Elt.idx + let set_idx = Elt.set_idx end) exception Sat @@ -61,9 +61,8 @@ module Make mutable next_decision : atom option; (* When the last conflict was a semantic one, this stores the next decision to make *) - elt_queue : t Vec.t; - (* decision stack + propagated elements (atoms or assignments). - Also called "trail" in some solvers. *) + trail : trail_elt Vec.t; + (* decision stack + propagated elements (atoms or assignments). *) elt_levels : int Vec.t; (* decision levels in [trail] *) @@ -74,19 +73,19 @@ module Make (* user levels in [clauses_temp] *) mutable th_head : int; - (* Start offset in the queue {!elt_queue} of + (* Start offset in the queue {!trail} of unit facts not yet seen by the theory. *) mutable elt_head : int; - (* Start offset in the queue {!elt_queue} of + (* Start offset in the queue {!trail} of unit facts to propagate, within the trail *) (* invariant: - during propagation, th_head <= elt_head - - then, once elt_head reaches length elt_queue, Th.assume is + - then, once elt_head reaches length trail, Th.assume is called so that th_head can catch up with elt_head - this is repeated until a fixpoint is reached; - before a decision (and after the fixpoint), - th_head = elt_head = length elt_queue + th_head = elt_head = length trail *) @@ -139,9 +138,9 @@ module Make unsat_conflict = None; next_decision = None; - clauses_hyps = Vec.make 0 dummy_clause; - clauses_learnt = Vec.make 0 dummy_clause; - clauses_temp = Vec.make 0 dummy_clause; + clauses_hyps = Vec.make 0 Clause.dummy; + clauses_learnt = Vec.make 0 Clause.dummy; + clauses_temp = Vec.make 0 Clause.dummy; clauses_root = Stack.create (); clauses_to_add = Stack.create (); @@ -149,7 +148,7 @@ module Make th_head = 0; elt_head = 0; - elt_queue = Vec.make 601 (of_atom dummy_atom); + trail = Vec.make 601 (Trail_elt.of_atom Atom.dummy); elt_levels = Vec.make 601 (-1); th_levels = Vec.make 100 Plugin.dummy; user_levels = Vec.make 10 (-1); @@ -212,15 +211,18 @@ module Make (* When we have a new literal, we need to first create the list of its subterms. *) let atom (f:St.formula) : atom = - let res = add_atom f in - if St.mcsat then + let res = Atom.make f in + if St.mcsat then ( begin match res.var.v_assignable with | Some _ -> () | None -> let l = ref [] in - Plugin.iter_assignable (fun t -> l := add_term t :: !l) res.var.pa.lit; + Plugin.iter_assignable + (fun t -> l := Lit.make t :: !l) + res.var.pa.lit; res.var.v_assignable <- Some !l; end; + ); res (* Variable and literal activity. @@ -238,14 +240,14 @@ module Make end and insert_subterms_order (v:St.var) : unit = - iter_sub (fun t -> insert_var_order (elt_of_lit t)) v + iter_sub (fun t -> insert_var_order (Elt.of_lit t)) v (* Add new litterals/atoms on which to decide on, even if there is no clause that constrains it. We could maybe check if they have already has been decided before inserting them into the heap, if it appears that it helps performance. *) let new_lit t = - let l = add_term t in + let l = Lit.make t in insert_var_order (E_lit l) let new_atom p = @@ -264,13 +266,13 @@ module Make (* increase activity of [v] *) let var_bump_activity_aux v = v.v_weight <- v.v_weight +. env.var_incr; - if v.v_weight > 1e100 then begin + if v.v_weight > 1e100 then ( for i = 0 to (St.nb_elt ()) - 1 do - set_elt_weight (St.get_elt i) ((get_elt_weight (St.get_elt i)) *. 1e-100) + Elt.set_weight (St.get_elt i) ((Elt.weight (St.get_elt i)) *. 1e-100) done; env.var_incr <- env.var_incr *. 1e-100; - end; - let elt = elt_of_var v in + ); + let elt = Elt.of_var v in if H.in_heap elt then ( H.decrease env.order elt ) @@ -278,13 +280,13 @@ module Make (* increase activity of literal [l] *) let lit_bump_activity_aux (l:lit): unit = l.l_weight <- l.l_weight +. env.var_incr; - if l.l_weight > 1e100 then begin + if l.l_weight > 1e100 then ( for i = 0 to (St.nb_elt ()) - 1 do - set_elt_weight (St.get_elt i) ((get_elt_weight (St.get_elt i)) *. 1e-100) + Elt.set_weight (St.get_elt i) ((Elt.weight (St.get_elt i)) *. 1e-100) done; env.var_incr <- env.var_incr *. 1e-100; - end; - let elt = elt_of_lit l in + ); + let elt = Elt.of_lit l in if H.in_heap elt then ( H.decrease env.order elt ) @@ -297,13 +299,13 @@ module Make (* increase activity of clause [c] *) let clause_bump_activity (c:clause) : unit = c.activity <- c.activity +. env.clause_incr; - if c.activity > 1e20 then begin + if c.activity > 1e20 then ( for i = 0 to (Vec.size env.clauses_learnt) - 1 do (Vec.get env.clauses_learnt i).activity <- (Vec.get env.clauses_learnt i).activity *. 1e-20; done; env.clause_incr <- env.clause_incr *. 1e-20 - end + ) (* Simplification of clauses. @@ -329,20 +331,23 @@ module Make let duplicates = ref [] in let res = ref [] in Array.iter (fun a -> - if seen a then duplicates := a :: !duplicates - else (mark a; res := a :: !res) - ) clause.atoms; + if Atom.seen a then duplicates := a :: !duplicates + else ( + Atom.mark a; + res := a :: !res + )) + clause.atoms; List.iter (fun a -> - if seen_both a.var then trivial := true; - clear a.var) + if Var.seen_both a.var then trivial := true; + Var.clear a.var) !res; if !trivial then raise Trivial else if !duplicates = [] then clause else - make_clause (fresh_lname ()) !res (History [clause]) + Clause.make !res (History [clause]) (* Partition literals for new clauses, into: - true literals (maybe makes the clause trivial if the lit is proved true at level 0) @@ -353,20 +358,20 @@ module Make *) let partition atoms : atom list * clause list = let rec partition_aux trues unassigned falses history i = - if i >= Array.length atoms then + if i >= Array.length atoms then ( trues @ unassigned @ falses, history - else begin + ) else ( let a = atoms.(i) in - if a.is_true then + if a.is_true then ( let l = a.var.v_level in if l = 0 then raise Trivial (* A var true at level 0 gives a trivially true clause *) else (a :: trues) @ unassigned @ falses @ (arr_to_list atoms (i + 1)), history - else if a.neg.is_true then + ) else if a.neg.is_true then ( let l = a.var.v_level in - if l = 0 then begin + if l = 0 then ( match a.var.reason with | Some (Bcp cl) -> partition_aux trues unassigned falses (cl :: history) (i + 1) @@ -381,11 +386,13 @@ module Make | None | Some Decision -> assert false (* The var must have a reason, and it cannot be a decision/assumption, since its level is 0. *) - end else + ) else ( partition_aux trues unassigned (a::falses) history (i + 1) - else + ) + ) else ( partition_aux trues (a::unassigned) falses history (i + 1) - end + ) + ) in partition_aux [] [] [] [] 0 @@ -398,9 +405,9 @@ module Make i.e we have indeed reached a propagation fixpoint before making a new decision *) let new_decision_level() = - assert (env.th_head = Vec.size env.elt_queue); - assert (env.elt_head = Vec.size env.elt_queue); - Vec.push env.elt_levels (Vec.size env.elt_queue); + assert (env.th_head = Vec.size env.trail); + assert (env.elt_head = Vec.size env.trail); + Vec.push env.elt_levels (Vec.size env.trail); Vec.push env.th_levels (Plugin.current_level ()); (* save the current theory state *) () @@ -411,11 +418,11 @@ module Make *) let attach_clause c = - assert (not c.attached); - Log.debugf debug (fun k -> k "Attaching %a" St.pp_clause c); + assert (not @@ Clause.attached c); + Log.debugf debug (fun k -> k "Attaching %a" Clause.debug c); Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; - c.attached <- true; + Clause.set_attached c true; () (* Backtracking. @@ -425,9 +432,9 @@ module Make let cancel_until lvl = assert (lvl >= base_level ()); (* Nothing to do if we try to backtrack to a non-existent level. *) - if decision_level () <= lvl then + if decision_level () <= lvl then ( Log.debugf debug (fun k -> k "Already at level <= %d" lvl) - else begin + ) else ( Log.debugf info (fun k -> k "Backtracking to lvl %d" lvl); (* We set the head of the solver and theory queue to what it was. *) let head = ref (Vec.get env.elt_levels lvl) in @@ -435,29 +442,29 @@ module Make env.th_head <- !head; (* Now we need to cleanup the vars that are not valid anymore (i.e to the right of elt_head in the queue. *) - for c = env.elt_head to Vec.size env.elt_queue - 1 do - match (Vec.get env.elt_queue c) with + for c = env.elt_head to Vec.size env.trail - 1 do + match (Vec.get env.trail c) with (* A literal is unassigned, we nedd to add it back to the heap of potentially assignable literals, unless it has a level lower than [lvl], in which case we just move it back. *) | Lit l -> - if l.l_level <= lvl then begin - Vec.set env.elt_queue !head (of_lit l); + if l.l_level <= lvl then ( + Vec.set env.trail !head (Trail_elt.of_lit l); head := !head + 1 - end else begin + ) else ( l.assigned <- None; l.l_level <- -1; - insert_var_order (elt_of_lit l) - end + insert_var_order (Elt.of_lit l) + ) (* A variable is not true/false anymore, one of two things can happen: *) | Atom a -> - if a.var.v_level <= lvl then begin + if a.var.v_level <= lvl then ( (* It is a late propagation, which has a level lower than where we backtrack, so we just move it to the head of the queue, to be propagated again. *) - Vec.set env.elt_queue !head (of_atom a); + Vec.set env.trail !head (Trail_elt.of_atom a); head := !head + 1 - end else begin + ) else ( (* it is a result of bolean propagation, or a semantic propagation with a level higher than the level to which we backtrack, in that case, we simply unset its value and reinsert it into the heap. *) @@ -465,23 +472,23 @@ module Make a.neg.is_true <- false; a.var.v_level <- -1; a.var.reason <- None; - insert_var_order (elt_of_var a.var) - end + insert_var_order (Elt.of_var a.var) + ) done; (* Recover the right theory state. *) Plugin.backtrack (Vec.get env.th_levels lvl); (* Resize the vectors according to their new size. *) - Vec.shrink env.elt_queue !head; + Vec.shrink env.trail !head; Vec.shrink env.elt_levels lvl; Vec.shrink env.th_levels lvl; - end; + ); assert (Vec.size env.elt_levels = Vec.size env.th_levels); () (* Unsatisfiability is signaled through an exception, since it can happen in multiple places (adding new clauses, or solving for instance). *) let report_unsat confl : _ = - Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" St.pp_clause confl); + Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" Clause.debug confl); env.unsat_conflict <- Some confl; raise Unsat @@ -505,18 +512,18 @@ module Make with only one formula (which is [a]). So we explicitly create that clause and set it as the cause for the propagation of [a], that way we can rebuild the whole resolution tree when we want to prove [a]. *) - let c' = make_clause (fresh_lname ()) l (History (cl :: history)) in + let c' = Clause.make l (History (cl :: history)) in Log.debugf debug - (fun k -> k "Simplified reason: @[%a@,%a@]" St.pp_clause cl St.pp_clause c'); + (fun k -> k "Simplified reason: @[%a@,%a@]" Clause.debug cl Clause.debug c'); Bcp c' ) | _ -> Log.debugf error (fun k -> k "@[Failed at reason simplification:@,%a@,%a@]" - (Vec.print ~sep:"" St.pp_atom) - (Vec.from_list l St.dummy_atom) - St.pp_clause cl); + (Vec.print ~sep:"" Atom.debug) + (Vec.from_list l Atom.dummy) + Clause.debug cl); assert false end | r -> r @@ -524,10 +531,10 @@ module Make (* Boolean propagation. Wrapper function for adding a new propagated formula. *) let enqueue_bool a ~level:lvl reason : unit = - if a.neg.is_true then begin - Log.debugf error (fun k->k "Trying to enqueue a false literal: %a" St.pp_atom a); + if a.neg.is_true then ( + Log.debugf error (fun k->k "Trying to enqueue a false literal: %a" Atom.debug a); assert false - end; + ); assert (not a.is_true && a.var.v_level < 0 && a.var.reason = None && lvl >= 0); let reason = @@ -537,34 +544,35 @@ module Make a.is_true <- true; a.var.v_level <- lvl; a.var.reason <- Some reason; - Vec.push env.elt_queue (of_atom a); + Vec.push env.trail (Trail_elt.of_atom a); Log.debugf debug - (fun k->k "Enqueue (%d): %a" (Vec.size env.elt_queue) pp_atom a) + (fun k->k "Enqueue (%d): %a" (Vec.size env.trail) Atom.debug a); + () let enqueue_semantic a terms = - if a.is_true then () - else begin - let l = List.map St.add_term terms in + if not a.is_true then ( + let l = List.map Lit.make terms in let lvl = List.fold_left (fun acc {l_level; _} -> assert (l_level > 0); max acc l_level) 0 l in H.grow_to_at_least env.order (St.nb_elt ()); enqueue_bool a ~level:lvl Semantic - end + ) (* MCsat semantic assignment *) let enqueue_assign l value lvl = match l.assigned with | Some _ -> Log.debugf error - (fun k -> k "Trying to assign an already assigned literal: %a" St.pp_lit l); + (fun k -> k "Trying to assign an already assigned literal: %a" Lit.debug l); assert false | None -> assert (l.l_level < 0); l.assigned <- Some value; l.l_level <- lvl; - Vec.push env.elt_queue (of_lit l); + Vec.push env.trail (Trail_elt.of_lit l); Log.debugf debug - (fun k -> k "Enqueue (%d): %a" (Vec.size env.elt_queue) pp_lit l) + (fun k -> k "Enqueue (%d): %a" (Vec.size env.trail) Lit.debug l); + () (* swap elements of array *) let[@inline] swap_arr a i j = @@ -574,11 +582,17 @@ module Make a.(j) <- tmp; ) + let[@inline] put_high_level_atoms_first (arr:atom array) : unit = + Array.sort + (fun a b -> compare b.var.v_level a.var.v_level) + arr + + (* FIXME (* move atoms assigned at high levels first *) let[@inline] put_high_level_atoms_first (arr:atom array) : unit = Array.iteri (fun i a -> - if i>0 && a.var.v_level > arr.(0).var.v_level then ( + if i>0 && Atom.level a > Atom.level arr.(0) then ( (* move first to second, [i]-th to first, second to [i] *) if i=1 then ( swap_arr arr 0 1; @@ -588,10 +602,11 @@ module Make arr.(0) <- arr.(i); arr.(i) <- tmp; ); - ) else if i>1 && a.var.v_level > arr.(1).var.v_level then ( + ) else if i>1 && Atom.level a > Atom.level arr.(1) then ( swap_arr arr 1 i; )) arr + *) (* evaluate an atom for MCsat, if it's not assigned by boolean propagation/decision *) @@ -640,7 +655,7 @@ module Make } let get_atom i = - match Vec.get env.elt_queue i with + match Vec.get env.trail i with | Lit _ -> assert false | Atom x -> x (* conflict analysis for SAT @@ -654,20 +669,20 @@ module Make let blevel = ref 0 in let seen = ref [] in let c = ref (Some c_clause) in - let tr_ind = ref (Vec.size env.elt_queue - 1) in + let tr_ind = ref (Vec.size env.trail - 1) in let history = ref [] in assert (decision_level () > 0); let conflict_level = Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in Log.debugf debug - (fun k -> k "Analyzing conflict (%d): %a" conflict_level St.pp_clause c_clause); + (fun k -> k "Analyzing conflict (%d): %a" conflict_level Clause.debug c_clause); while !cond do begin match !c with | None -> Log.debug debug " skipping resolution for semantic propagation" | Some clause -> - Log.debugf debug (fun k->k" Resolving clause: %a" St.pp_clause clause); + Log.debugf debug (fun k->k" Resolving clause: %a" Clause.debug clause); begin match clause.cpremise with | History _ -> clause_bump_activity clause | Hyp | Local | Lemma _ -> () @@ -677,36 +692,36 @@ module Make for j = 0 to Array.length clause.atoms - 1 do let q = clause.atoms.(j) in assert (q.is_true || q.neg.is_true && q.var.v_level >= 0); (* unsure? *) - if q.var.v_level <= 0 then begin + if q.var.v_level <= 0 then ( assert (q.neg.is_true); match q.var.reason with | Some Bcp cl -> history := cl :: !history | _ -> assert false - end; - if not (seen_both q.var) then ( - mark q; - mark q.neg; + ); + if not (Var.seen_both q.var) then ( + Atom.mark q; + Atom.mark q.neg; seen := q :: !seen; - if q.var.v_level > 0 then begin + if q.var.v_level > 0 then ( var_bump_activity q.var; - if q.var.v_level >= conflict_level then begin + if q.var.v_level >= conflict_level then ( incr pathC; - end else begin + ) else ( learnt := q :: !learnt; blevel := max !blevel q.var.v_level - end - end + ) + ) ) done end; (* look for the next node to expand *) while - let a = Vec.get env.elt_queue !tr_ind in - Log.debugf debug (fun k -> k " looking at: %a" St.pp a); + let a = Vec.get env.trail !tr_ind in + Log.debugf debug (fun k -> k " looking at: %a" Trail_elt.debug a); match a with | Atom q -> - (not (seen_both q.var)) || + (not (Var.seen_both q.var)) || (q.var.v_level < conflict_level) | Lit _ -> true do @@ -729,7 +744,7 @@ module Make c := Some cl | _ -> assert false done; - List.iter (fun q -> clear q.var) !seen; + List.iter (fun q -> Var.clear q.var) !seen; let l = List.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) !learnt in let level, is_uip = backtrack_lvl l in { cr_backtrack_lvl = level; @@ -752,26 +767,24 @@ module Make | [] -> assert false | [fuip] -> assert (cr.cr_backtrack_lvl = 0); - if fuip.neg.is_true then + if fuip.neg.is_true then ( report_unsat confl - else begin - let name = fresh_lname () in - let uclause = make_clause name cr.cr_learnt (History cr.cr_history) in + ) else ( + let uclause = Clause.make cr.cr_learnt (History cr.cr_history) in Vec.push env.clauses_learnt uclause; (* no need to attach [uclause], it is true at level 0 *) enqueue_bool fuip ~level:0 (Bcp uclause) - end + ) | fuip :: _ -> - let name = fresh_lname () in - let lclause = make_clause name cr.cr_learnt (History cr.cr_history) in + let lclause = Clause.make cr.cr_learnt (History cr.cr_history) in Vec.push env.clauses_learnt lclause; attach_clause lclause; clause_bump_activity lclause; - if cr.cr_is_uip then + if cr.cr_is_uip then ( enqueue_bool fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) - else begin + ) else ( env.next_decision <- Some fuip.neg - end + ) end; var_decay_activity (); clause_decay_activity () @@ -782,7 +795,7 @@ module Make - report unsat if conflict at level 0 *) let add_boolean_conflict (confl:clause): unit = - Log.debugf info (fun k -> k "Boolean conflict: %a" St.pp_clause confl); + Log.debugf info (fun k -> k "Boolean conflict: %a" Clause.debug confl); env.next_decision <- None; env.conflicts <- env.conflicts + 1; assert (decision_level() >= base_level ()); @@ -803,14 +816,14 @@ module Make (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) let add_clause (init:clause) : unit = - Log.debugf debug (fun k -> k "Adding clause: @[%a@]" St.pp_clause init); + Log.debugf debug (fun k -> k "Adding clause: @[%a@]" Clause.debug init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) - Array.iter (fun x -> insert_var_order (elt_of_var x.var)) init.atoms; + Array.iter (fun x -> insert_var_order (Elt.of_var x.var)) init.atoms; let vec = clause_vector init in try let c = eliminate_doublons init in - Log.debugf debug (fun k -> k "Doublons eliminated: %a" St.pp_clause c); + Log.debugf debug (fun k -> k "Doublons eliminated: %a" Clause.debug c); let atoms, history = partition c.atoms in let clause = if history = [] @@ -819,9 +832,9 @@ module Make List.iteri (fun i a -> c.atoms.(i) <- a) atoms; c ) - else make_clause (fresh_name ()) atoms (History (c :: history)) + else Clause.make atoms (History (c :: history)) in - Log.debugf info (fun k->k "New clause: @[%a@]" St.pp_clause clause); + Log.debugf info (fun k->k "New clause: @[%a@]" Clause.debug clause); match atoms with | [] -> (* Report_unsat will raise, and the current clause will be lost if we do not @@ -831,14 +844,14 @@ module Make report_unsat clause | [a] -> cancel_until (base_level ()); - if a.neg.is_true then begin + if a.neg.is_true then ( (* Since we cannot propagate the atom [a], in order to not lose the information that [a] must be true, we add clause to the list of clauses to add, so that it will be e-examined later. *) Log.debug debug "Unit clause, adding to clauses to add"; Stack.push clause env.clauses_to_add; report_unsat clause - end else if a.is_true then begin + ) else if a.is_true then ( (* If the atom is already true, then it should be because of a local hyp. However it means we can't propagate it at level 0. In order to not lose that information, we store the clause in a stack of clauses that we will @@ -847,30 +860,30 @@ module Make assert (0 < a.var.v_level && a.var.v_level <= base_level ()); Stack.push clause env.clauses_root; () - end else begin - Log.debugf debug (fun k->k "Unit clause, propagating: %a" St.pp_atom a); + ) else ( + Log.debugf debug (fun k->k "Unit clause, propagating: %a" Atom.debug a); Vec.push vec clause; enqueue_bool a ~level:0 (Bcp clause) - end + ) | a::b::_ -> Vec.push vec clause; - if a.neg.is_true then begin + if a.neg.is_true then ( (* Atoms need to be sorted in decreasing order of decision level, or we might watch the wrong literals. *) put_high_level_atoms_first clause.atoms; attach_clause clause; add_boolean_conflict clause - end else begin + ) else ( attach_clause clause; - if b.neg.is_true && not a.is_true && not a.neg.is_true then begin + if b.neg.is_true && not a.is_true && not a.neg.is_true then ( let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in cancel_until (max lvl (base_level ())); enqueue_bool a ~level:lvl (Bcp clause) - end - end + ) + ) with Trivial -> Vec.push vec init; - Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" St.pp_clause init) + Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) let flush_clauses () = if not (Stack.is_empty env.clauses_to_add) then begin @@ -904,13 +917,13 @@ module Make atoms.(1) <- first ) else assert (a.neg == atoms.(1)); let first = atoms.(0) in - if first.is_true + if Atom.is_true first then Watch_kept (* true clause, keep it in watched *) else ( try (* look for another watch lit *) for k = 2 to Array.length atoms - 1 do let ak = atoms.(k) in - if not (ak.neg.is_true) then begin + if not (ak.neg.is_true) then ( (* watch lit found: update and exit *) atoms.(1) <- ak; atoms.(k) <- a.neg; @@ -919,22 +932,22 @@ module Make assert (Vec.get a.watched i == c); Vec.fast_remove a.watched i; raise Exit - end + ) done; (* no watch lit found *) - if first.neg.is_true then begin + if first.neg.is_true then ( (* clause is false *) - env.elt_head <- Vec.size env.elt_queue; + env.elt_head <- Vec.size env.trail; raise (Conflict c) - end else begin + ) else ( match th_eval first with | None -> (* clause is unit, keep the same watches, but propagate *) enqueue_bool first ~level:(decision_level ()) (Bcp c) | Some true -> () | Some false -> - env.elt_head <- Vec.size env.elt_queue; + env.elt_head <- Vec.size env.trail; raise (Conflict c) - end; + ); Watch_kept with Exit -> Watch_removed @@ -952,7 +965,7 @@ module Make if i >= Vec.size watched then () else ( let c = Vec.get watched i in - assert c.attached; + assert (Clause.attached c); let j = match propagate_in_clause a c i with | Watch_kept -> i+1 | Watch_removed -> i (* clause at this index changed *) @@ -974,7 +987,7 @@ module Make a let slice_get i = - match Vec.get env.elt_queue i with + match Vec.get env.trail i with | Atom a -> Plugin_intf.Lit a.lit | Lit {term; assigned = Some v; _} -> @@ -983,8 +996,8 @@ module Make let slice_push (l:formula list) (lemma:proof): unit = let atoms = List.rev_map create_atom l in - let c = make_clause (fresh_tname ()) atoms (Lemma lemma) in - Log.debugf info (fun k->k "Pushing clause %a" St.pp_clause c); + let c = Clause.make atoms (Lemma lemma) in + Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); Stack.push c env.clauses_to_add let slice_propagate f = function @@ -993,25 +1006,25 @@ module Make enqueue_semantic a l | Plugin_intf.Consequence (causes, proof) -> let l = List.rev_map atom causes in - if List.for_all (fun a -> a.is_true) l then + if List.for_all (fun a -> a.is_true) l then ( let p = atom f in - let c = make_clause (fresh_tname ()) - (p :: List.map (fun a -> a.neg) l) (Lemma proof) in + let c = Clause.make (p :: List.map Atom.neg l) (Lemma proof) in if p.is_true then () - else if p.neg.is_true then + else if p.neg.is_true then ( Stack.push c env.clauses_to_add - else begin + ) else ( H.grow_to_at_least env.order (St.nb_elt ()); insert_subterms_order p.var; let lvl = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in enqueue_bool p ~level:lvl (Bcp c) - end - else - raise (Invalid_argument "Msat.Internal.slice_propagate") + ) + ) else ( + invalid_arg "Msat.Internal.slice_propagate" + ) let current_slice (): (_,_,_) Plugin_intf.slice = { Plugin_intf.start = env.th_head; - length = (Vec.size env.elt_queue) - env.th_head; + length = (Vec.size env.trail) - env.th_head; get = slice_get; push = slice_push; propagate = slice_propagate; @@ -1020,7 +1033,7 @@ module Make (* full slice, for [if_sat] final check *) let full_slice () : (_,_,_) Plugin_intf.slice = { Plugin_intf.start = 0; - length = Vec.size env.elt_queue; + length = Vec.size env.trail; get = slice_get; push = slice_push; propagate = (fun _ -> assert false); @@ -1030,11 +1043,11 @@ module Make need to inform the theory of those assumptions, so it can do its job. @return the conflict clause, if the theory detects unsatisfiability *) let rec theory_propagate (): clause option = - assert (env.elt_head = Vec.size env.elt_queue); + assert (env.elt_head = Vec.size env.trail); assert (env.th_head <= env.elt_head); - if env.th_head = env.elt_head then + if env.th_head = env.elt_head then ( None (* fixpoint/no propagation *) - else begin + ) else ( let slice = current_slice () in env.th_head <- env.elt_head; (* catch up *) match Plugin.assume slice with @@ -1049,11 +1062,11 @@ module Make ); (* Insert elements for decision (and ensure the heap is big enough) *) H.grow_to_at_least env.order (St.nb_elt ()); - List.iter (fun a -> insert_var_order (elt_of_var a.var)) l; + List.iter (fun a -> insert_var_order (Elt.of_var a.var)) l; (* Create the clause and return it. *) - let c = St.make_clause (St.fresh_tname ()) l (Lemma p) in + let c = St.Clause.make l (Lemma p) in Some c - end + ) (* fixpoint between boolean propagation and theory propagation @return a conflict clause, if any *) @@ -1061,14 +1074,14 @@ module Make (* First, treat the stack of lemmas added by the theory, if any *) flush_clauses (); (* Now, check that the situation is sane *) - assert (env.elt_head <= Vec.size env.elt_queue); - if env.elt_head = Vec.size env.elt_queue then + assert (env.elt_head <= Vec.size env.trail); + if env.elt_head = Vec.size env.trail then theory_propagate () else begin let num_props = ref 0 in let res = ref None in - while env.elt_head < Vec.size env.elt_queue do - begin match Vec.get env.elt_queue env.elt_head with + while env.elt_head < Vec.size env.trail do + begin match Vec.get env.trail env.elt_head with | Lit _ -> () | Atom a -> incr num_props; @@ -1091,10 +1104,10 @@ module Make (* Decide on a new literal, and enqueue it into the trail *) let rec pick_branch_aux atom: unit = let v = atom.var in - if v.v_level >= 0 then begin + if v.v_level >= 0 then ( assert (v.pa.is_true || v.na.is_true); pick_branch_lit () - end else match Plugin.eval atom.lit with + ) else match Plugin.eval atom.lit with | Plugin_intf.Unknown -> env.decisions <- env.decisions + 1; new_decision_level(); @@ -1110,22 +1123,20 @@ module Make env.next_decision <- None; pick_branch_aux atom | None -> - begin try - begin match H.remove_min env.order with - | E_lit l -> - if l.l_level >= 0 then - pick_branch_lit () - else begin - let value = Plugin.assign l.term in - env.decisions <- env.decisions + 1; - new_decision_level(); - let current_level = decision_level () in - enqueue_assign l value current_level - end - | E_var v -> - pick_branch_aux v.pa - end - with Not_found -> raise Sat + begin match H.remove_min env.order with + | E_lit l -> + if Lit.level l >= 0 then + pick_branch_lit () + else ( + let value = Plugin.assign l.term in + env.decisions <- env.decisions + 1; + new_decision_level(); + let current_level = decision_level () in + enqueue_assign l value current_level + ) + | E_var v -> + pick_branch_aux v.pa + | exception Not_found -> raise Sat end (* do some amount of search, until the number of conflicts or clause learnt @@ -1141,32 +1152,32 @@ module Make might 'forget' the initial conflict clause, and only add the analyzed backtrack clause. So in those case, we use add_clause to make sure the initial conflict clause is also added. *) - if confl.attached then + if Clause.attached confl then add_boolean_conflict confl else add_clause confl | None -> (* No Conflict *) - assert (env.elt_head = Vec.size env.elt_queue); + assert (env.elt_head = Vec.size env.trail); assert (env.elt_head = env.th_head); - if Vec.size env.elt_queue = St.nb_elt () + if Vec.size env.trail = St.nb_elt () then raise Sat; - if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then begin + if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( Log.debug info "Restarting..."; cancel_until (base_level ()); raise Restart - end; + ); (* if decision_level() = 0 then simplify (); *) if n_of_learnts >= 0 && - Vec.size env.clauses_learnt - Vec.size env.elt_queue >= n_of_learnts + Vec.size env.clauses_learnt - Vec.size env.trail >= n_of_learnts then reduce_db(); pick_branch_lit () done let eval_level lit = - let var, negated = make_boolean_var lit in + let var, negated = Var.make lit in if not var.pa.is_true && not var.na.is_true then raise UndecidedLit else assert (var.v_level >= 0); @@ -1187,7 +1198,7 @@ module Make (fun acc e -> match e with | Lit v -> (v.term, opt v.assigned) :: acc | Atom _ -> acc) - [] env.elt_queue + [] env.trail (* fixpoint of propagation and decisions until a model is found, or a conflict is reached *) @@ -1205,13 +1216,13 @@ module Make n_of_conflicts := !n_of_conflicts *. env.restart_inc; n_of_learnts := !n_of_learnts *. env.learntsize_inc | Sat -> - assert (env.elt_head = Vec.size env.elt_queue); + assert (env.elt_head = Vec.size env.trail); begin match Plugin.if_sat (full_slice ()) with | Plugin_intf.Sat -> () | Plugin_intf.Unsat (l, p) -> let atoms = List.rev_map create_atom l in - let c = make_clause (fresh_tname ()) atoms (Lemma p) in - Log.debugf info (fun k -> k "Theory conflict clause: %a" St.pp_clause c); + let c = Clause.make atoms (Lemma p) in + Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); Stack.push c env.clauses_to_add end; if Stack.is_empty env.clauses_to_add then raise Sat @@ -1223,8 +1234,8 @@ module Make List.iter (fun l -> let atoms = List.rev_map atom l in - let c = make_clause ?tag (fresh_hname ()) atoms Hyp in - Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" pp_clause c); + let c = Clause.make ?tag atoms Hyp in + Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" Clause.debug c); Stack.push c env.clauses_to_add) cnf @@ -1237,13 +1248,14 @@ module Make cancel_until (base_level ()); Log.debugf debug (fun k -> k "@[Status:@,@[trail: %d - %d@,%a@]" - env.elt_head env.th_head (Vec.print ~sep:"" St.pp) env.elt_queue); + env.elt_head env.th_head (Vec.print ~sep:"" Trail_elt.debug) env.trail); begin match propagate () with | Some confl -> report_unsat confl | None -> Log.debugf debug - (fun k -> k "@[Current trail:@,@[%a@]@]" (Vec.print ~sep:"" St.pp) env.elt_queue); + (fun k -> k "@[Current trail:@,@[%a@]@]" + (Vec.print ~sep:"" Trail_elt.debug) env.trail); Log.debug info "Creating new user level"; new_decision_level (); Vec.push env.user_levels (Vec.size env.clauses_temp); @@ -1274,24 +1286,24 @@ module Make let local l = let aux lit = let a = atom lit in - Log.debugf info (fun k-> k "Local assumption: @[%a@]" pp_atom a); + Log.debugf info (fun k-> k "Local assumption: @[%a@]" Atom.debug a); assert (decision_level () = base_level ()); - if a.is_true then () - else - let c = make_clause (fresh_hname ()) [a] Local in - Log.debugf debug (fun k -> k "Temp clause: @[%a@]" pp_clause c); + if not a.is_true then ( + let c = Clause.make [a] Local in + Log.debugf debug (fun k -> k "Temp clause: @[%a@]" Clause.debug c); Vec.push env.clauses_temp c; - if a.neg.is_true then begin + if a.neg.is_true then ( (* conflict between assumptions: UNSAT *) report_unsat c; - end else begin + ) else ( (* Grow the heap, because when the lit is backtracked, it will be added to the heap. *) H.grow_to_at_least env.order (St.nb_elt ()); (* make a decision, propagate *) let level = decision_level() in enqueue_bool a ~level (Bcp c); - end + ) + ) in assert (base_level () > 0); match env.unsat_conflict with @@ -1309,11 +1321,11 @@ module Make else if a.neg.is_true then false else raise UndecidedLit) c.atoms in let res = Array.exists (fun x -> x) tmp in - if not res then begin + if not res then ( Log.debugf debug - (fun k -> k "Clause not satisfied: @[%a@]" St.pp_clause c); + (fun k -> k "Clause not satisfied: @[%a@]" Clause.debug c); false - end else + ) else true let check_vec v = @@ -1341,7 +1353,7 @@ module Make let temp () = env.clauses_temp - let trail () = env.elt_queue + let trail () = env.trail end diff --git a/src/core/Internal.mli b/src/core/Internal.mli index ca0aef17..bdbbb29e 100644 --- a/src/core/Internal.mli +++ b/src/core/Internal.mli @@ -92,7 +92,7 @@ module Make These functions expose some internal data stored by the solver, as such great care should be taken to ensure not to mess with the values returned. *) - val trail : unit -> St.t Vec.t + val trail : unit -> St.trail_elt Vec.t (** Returns the current trail. *DO NOT MUTATE* *) diff --git a/src/core/Res.ml b/src/core/Res.ml index 5d9f5e26..f164b940 100644 --- a/src/core/Res.ml +++ b/src/core/Res.ml @@ -24,11 +24,10 @@ module Make(St : Solver_types.S) = struct let info = 10 let debug = 80 - (* Misc functions *) let equal_atoms a b = St.(a.aid) = St.(b.aid) let compare_atoms a b = Pervasives.compare St.(a.aid) St.(b.aid) - let print_clause = St.pp_clause + let print_clause = St.Clause.pp let merge = List.merge compare_atoms @@ -52,19 +51,19 @@ module Make(St : Solver_types.S) = struct resolved, List.rev new_clause (* Compute the set of doublons of a clause *) - let list c = List.sort compare_atoms (Array.to_list St.(c.atoms)) + let list c = List.sort St.Atom.compare (Array.to_list St.(c.atoms)) let analyze cl = let rec aux duplicates free = function | [] -> duplicates, free | [ x ] -> duplicates, x :: free | x :: ((y :: r) as l) -> - if equal_atoms x y then + if x == y then count duplicates (x :: free) x [y] r else aux duplicates (x :: free) l and count duplicates free x acc = function - | (y :: r) when equal_atoms x y -> + | (y :: r) when x == y -> count duplicates free x (y :: acc) r | l -> aux (acc :: duplicates) free l @@ -96,7 +95,8 @@ module Make(St : Solver_types.S) = struct let cmp_cl c d = let rec aux = function | [], [] -> 0 - | a :: r, a' :: r' -> begin match compare_atoms a a' with + | a :: r, a' :: r' -> + begin match compare_atoms a a' with | 0 -> aux (r, r') | x -> x end @@ -117,32 +117,32 @@ module Make(St : Solver_types.S) = struct assert St.(a.var.v_level >= 0); match St.(a.var.reason) with | Some St.Bcp c -> - Log.debugf debug (fun k->k "Analysing: @[%a@ %a@]" St.pp_atom a St.pp_clause c); - if Array.length c.St.atoms = 1 then begin - Log.debugf debug (fun k -> k "Old reason: @[%a@]" St.pp_atom a); + Log.debugf debug (fun k->k "Analysing: @[%a@ %a@]" St.Atom.debug a St.Clause.debug c); + if Array.length c.St.atoms = 1 then ( + Log.debugf debug (fun k -> k "Old reason: @[%a@]" St.Atom.debug a); c - end else begin + ) else ( assert (a.St.neg.St.is_true); let r = St.History (c :: (Array.fold_left aux [] c.St.atoms)) in - let c' = St.make_clause (fresh_pcl_name ()) [a.St.neg] r in + let c' = St.Clause.make [a.St.neg] r in a.St.var.St.reason <- Some St.(Bcp c'); Log.debugf debug - (fun k -> k "New reason: @[%a@ %a@]" St.pp_atom a St.pp_clause c'); + (fun k -> k "New reason: @[%a@ %a@]" St.Atom.debug a St.Clause.debug c'); c' - end + ) | _ -> - Log.debugf error (fun k -> k "Error while proving atom %a" St.pp_atom a); + Log.debugf error (fun k -> k "Error while proving atom %a" St.Atom.debug a); raise (Resolution_error "Cannot prove atom") let prove_unsat conflict = if Array.length conflict.St.atoms = 0 then conflict - else begin - Log.debugf info (fun k -> k "Proving unsat from: @[%a@]" St.pp_clause conflict); + else ( + Log.debugf info (fun k -> k "Proving unsat from: @[%a@]" St.Clause.debug conflict); let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.St.atoms in - let res = St.make_clause (fresh_pcl_name ()) [] (St.History (conflict :: l)) in - Log.debugf info (fun k -> k "Proof found: @[%a@]" St.pp_clause res); + let res = St.Clause.make [] (St.History (conflict :: l)) in + Log.debugf info (fun k -> k "Proof found: @[%a@]" St.Clause.debug res); res - end + ) let prove_atom a = if St.(a.is_true && a.var.v_level = 0) then @@ -166,27 +166,26 @@ module Make(St : Solver_types.S) = struct let rec chain_res (c, cl) = function | d :: r -> Log.debugf debug - (fun k -> k " Resolving clauses : @[%a@\n%a@]" St.pp_clause c St.pp_clause d); + (fun k -> k " Resolving clauses : @[%a@\n%a@]" St.Clause.debug c St.Clause.debug d); let dl = to_list d in begin match resolve (merge cl dl) with | [ a ], l -> begin match r with | [] -> (l, c, d, a) | _ -> - let new_clause = St.make_clause (fresh_pcl_name ()) - l (St.History [c; d]) in + let new_clause = St.Clause.make l (St.History [c; d]) in chain_res (new_clause, l) r end | _ -> Log.debugf error - (fun k -> k "While resolving clauses:@[%a@\n%a@]" St.pp_clause c St.pp_clause d); + (fun k -> k "While resolving clauses:@[%a@\n%a@]" St.Clause.debug c St.Clause.debug d); raise (Resolution_error "Clause mismatch") end | _ -> raise (Resolution_error "Bad history") let expand conclusion = - Log.debugf debug (fun k -> k "Expanding : @[%a@]" St.pp_clause conclusion); + Log.debugf debug (fun k -> k "Expanding : @[%a@]" St.Clause.debug conclusion); match conclusion.St.cpremise with | St.Lemma l -> {conclusion; step = Lemma l; } @@ -195,7 +194,7 @@ module Make(St : Solver_types.S) = struct | St.Local -> { conclusion; step = Assumption; } | St.History [] -> - Log.debugf error (fun k -> k "Empty history for clause: %a" St.pp_clause conclusion); + Log.debugf error (fun k -> k "Empty history for clause: %a" St.Clause.debug conclusion); raise (Resolution_error "Empty history") | St.History [ c ] -> let duplicates, res = analyze (list c) in @@ -240,7 +239,7 @@ module Make(St : Solver_types.S) = struct let rec aux res acc = function | [] -> res, acc | c :: r -> - if not c.St.visited then begin + if not c.St.visited then ( c.St.visited <- true; match c.St.cpremise with | St.Hyp | St.Local | St.Lemma _ -> aux (c :: res) acc r @@ -248,8 +247,9 @@ module Make(St : Solver_types.S) = struct let l = List.fold_left (fun acc c -> if not c.St.visited then c :: acc else acc) r h in aux res (c :: acc) l - end else + ) else ( aux res acc r + ) in let res, tmp = aux [] [] [proof] in List.iter (fun c -> c.St.visited <- false) res; diff --git a/src/core/Solver.ml b/src/core/Solver.ml index 5cc0994b..3dab855e 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -6,36 +6,7 @@ Copyright 2016 Simon Cruanes module type S = Solver_intf.S -type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { - eval: 'form -> bool; - (** Returns the valuation of a formula in the current state - of the sat solver. - @raise UndecidedLit if the literal is not decided *) - eval_level: 'form -> bool * int; - (** Return the current assignement of the literals, as well as its - decision level. If the level is 0, then it is necessary for - the atom to have this value; otherwise it is due to choices - that can potentially be backtracked. - @raise UndecidedLit if the literal is not decided *) - iter_trail : ('form -> unit) -> ('term -> unit) -> unit; - (** Iter thorugh the formulas and terms in order of decision/propagation - (starting from the first propagation, to the last propagation). *) - model: unit -> ('term * 'term) list; - (** Returns the model found if the formula is satisfiable. *) -} - -type ('clause, 'proof) unsat_state = ('clause, 'proof) Solver_intf.unsat_state = { - unsat_conflict : unit -> 'clause; - (** Returns the unsat clause found at the toplevel *) - get_proof : unit -> 'proof; - (** returns a persistent proof of the empty clause from the Unsat result. *) -} - -type 'clause export = 'clause Solver_intf.export = { - hyps: 'clause Vec.t; - history: 'clause Vec.t; - local: 'clause Vec.t; -} +open Solver_intf module Make (St : Solver_types.S) @@ -65,10 +36,10 @@ module Make (fun k -> k "@[%s - Full resume:@,@[Trail:@\n%a@]@,@[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." status - (Vec.print ~sep:"" St.pp) (S.trail ()) - (Vec.print ~sep:"" St.pp_clause) (S.temp ()) - (Vec.print ~sep:"" St.pp_clause) (S.hyps ()) - (Vec.print ~sep:"" St.pp_clause) (S.history ()) + (Vec.print ~sep:"" St.Trail_elt.debug) (S.trail ()) + (Vec.print ~sep:"" St.Clause.debug) (S.temp ()) + (Vec.print ~sep:"" St.Clause.debug) (S.hyps ()) + (Vec.print ~sep:"" St.Clause.debug) (S.history ()) ) let mk_sat () : (_,_) sat_state = @@ -77,8 +48,8 @@ module Make let iter f f' = Vec.iter (function | St.Atom a -> f a.St.lit - | St.Lit l -> f' l.St.term - ) t + | St.Lit l -> f' l.St.term) + t in { eval = S.eval; diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 29245d33..9f46d043 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -73,7 +73,7 @@ module McMake (E : Expr_intf.S)() = struct } and clause = { - name : string; + name : int; tag : int option; atoms : atom array; mutable cpremise : premise; @@ -97,8 +97,9 @@ module McMake (E : Expr_intf.S)() = struct | E_lit of lit | E_var of var - (* Dummy values *) - let dummy_lit = E.Formula.dummy + type trail_elt = + | Lit of lit + | Atom of atom let rec dummy_var = { vid = -101; @@ -113,7 +114,7 @@ module McMake (E : Expr_intf.S)() = struct } and dummy_atom = { var = dummy_var; - lit = dummy_lit; + lit = E.Formula.dummy; watched = Obj.magic 0; (* should be [Vec.make_empty dummy_clause] but we have to break the cycle *) @@ -121,7 +122,7 @@ module McMake (E : Expr_intf.S)() = struct is_true = false; aid = -102 } let dummy_clause = - { name = ""; + { name = -1; tag = None; atoms = [| |]; activity = -1.; @@ -129,13 +130,13 @@ module McMake (E : Expr_intf.S)() = struct visited = false; cpremise = History [] } - let () = - dummy_atom.watched <- Vec.make_empty dummy_clause + let () = dummy_atom.watched <- Vec.make_empty dummy_clause (* Constructors *) module MF = Hashtbl.Make(E.Formula) module MT = Hashtbl.Make(E.Term) + (* TODO: embed a state `t` with these inside *) let f_map = MF.create 4096 let t_map = MT.create 4096 @@ -146,229 +147,286 @@ module McMake (E : Expr_intf.S)() = struct let cpt_mk_var = ref 0 - let make_semantic_var t = - try MT.find t_map t - with Not_found -> - let res = { - lid = !cpt_mk_var; - term = t; - l_weight = 1.; - l_idx= -1; - l_level = -1; - assigned = None; - } in - incr cpt_mk_var; - MT.add t_map t res; - Vec.push vars (E_lit res); - res + let name_of_clause c = match c.cpremise with + | Hyp -> "H" ^ string_of_int c.name + | Local -> "L" ^ string_of_int c.name + | Lemma _ -> "T" ^ string_of_int c.name + | History _ -> "C" ^ string_of_int c.name - let make_boolean_var : formula -> var * Expr_intf.negated = - fun t -> - let lit, negated = E.Formula.norm t in - try - MF.find f_map lit, negated + module Lit = struct + type t = lit + let[@inline] term l = l.term + let[@inline] level l = l.l_level + let[@inline] set_level l lvl = l.l_level <- lvl + + let[@inline] assigned l = l.assigned + let[@inline] set_assigned l t = l.assigned <- t + + let[@inline] weight l = l.l_weight + let[@inline] set_weight l w = l.l_weight <- w + + let make t = + try MT.find t_map t with Not_found -> - let cpt_fois_2 = !cpt_mk_var lsl 1 in - let rec var = - { vid = !cpt_mk_var; - pa = pa; - na = na; - v_fields = Var_fields.empty; - v_level = -1; - v_idx= -1; - v_weight = 0.; - v_assignable = None; - reason = None; - } - and pa = - { var = var; - lit = lit; - watched = Vec.make 10 dummy_clause; - neg = na; - is_true = false; - aid = cpt_fois_2 (* aid = vid*2 *) } - and na = - { var = var; - lit = E.Formula.neg lit; - watched = Vec.make 10 dummy_clause; - neg = pa; - is_true = false; - aid = cpt_fois_2 + 1 (* aid = vid*2+1 *) } in - MF.add f_map lit var; + let res = { + lid = !cpt_mk_var; + term = t; + l_weight = 1.; + l_idx= -1; + l_level = -1; + assigned = None; + } in incr cpt_mk_var; - Vec.push vars (E_var var); - var, negated + MT.add t_map t res; + Vec.push vars (E_lit res); + res - let add_term t = make_semantic_var t + let debug_assign fmt v = + match v.assigned with + | None -> + Format.fprintf fmt "" + | Some t -> + Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level E.Term.print t - let add_atom lit = - let var, negated = make_boolean_var lit in - match negated with - | Formula_intf.Negated -> var.na - | Formula_intf.Same_sign -> var.pa + let pp out v = E.Term.print out v.term + let debug out v = + Format.fprintf out "%d[%a][lit:@[%a@]]" + (v.lid+1) debug_assign v E.Term.print v.term + end - let make_clause ?tag name ali premise = - let atoms = Array.of_list ali in - { name = name; - tag = tag; - atoms = atoms; - attached = false; - visited = false; - activity = 0.; - cpremise = premise} + module Var = struct + type t = var + let dummy = dummy_var + let[@inline] level v = v.v_level + let[@inline] set_level v lvl = v.v_level <- lvl + let[@inline] pos v = v.pa + let[@inline] neg v = v.na + let[@inline] reason v = v.reason + let[@inline] set_reason v r = v.reason <- r + let[@inline] assignable v = v.v_assignable + let[@inline] set_assignable v x = v.v_assignable <- x + let[@inline] weight v = v.v_weight + let[@inline] set_weight v w = v.v_weight <- w - let empty_clause = make_clause "Empty" [] (History []) + let make : formula -> var * Expr_intf.negated = + fun t -> + let lit, negated = E.Formula.norm t in + try + MF.find f_map lit, negated + with Not_found -> + let cpt_fois_2 = !cpt_mk_var lsl 1 in + let rec var = + { vid = !cpt_mk_var; + pa = pa; + na = na; + v_fields = Var_fields.empty; + v_level = -1; + v_idx= -1; + v_weight = 0.; + v_assignable = None; + reason = None; + } + and pa = + { var = var; + lit = lit; + watched = Vec.make 10 dummy_clause; + neg = na; + is_true = false; + aid = cpt_fois_2 (* aid = vid*2 *) } + and na = + { var = var; + lit = E.Formula.neg lit; + watched = Vec.make 10 dummy_clause; + neg = pa; + is_true = false; + aid = cpt_fois_2 + 1 (* aid = vid*2+1 *) } in + MF.add f_map lit var; + incr cpt_mk_var; + Vec.push vars (E_var var); + var, negated - (* Marking helpers *) - let clear v = v.v_fields <- Var_fields.empty + (* Marking helpers *) + let[@inline] clear v = + v.v_fields <- Var_fields.empty - let seen a = - let pos = (a == a.var.pa) in - let field = if pos then v_field_seen_pos else v_field_seen_neg in - Var_fields.get field a.var.v_fields + let[@inline] seen_both v = + Var_fields.get v_field_seen_pos v.v_fields && + Var_fields.get v_field_seen_neg v.v_fields + end - let seen_both v = - Var_fields.get v_field_seen_pos v.v_fields && - Var_fields.get v_field_seen_neg v.v_fields + module Atom = struct + type t = atom + let dummy = dummy_atom + let[@inline] level a = a.var.v_level + let[@inline] var a = a.var + let[@inline] neg a = a.neg + let[@inline] abs a = a.var.pa + let[@inline] lit a = a.lit + let[@inline] equal a b = a == b + let[@inline] compare a b = Pervasives.compare a.aid b.aid + let[@inline] reason a = Var.reason a.var + let[@inline] id a = a.aid + let[@inline] is_true a = a.is_true + let[@inline] is_false a = a.neg.is_true - let mark a = - let pos = (a == a.var.pa) in - let field = if pos then v_field_seen_pos else v_field_seen_neg in - a.var.v_fields <- Var_fields.set field true a.var.v_fields + let[@inline] seen a = + let pos = equal a (abs a) in + if pos + then Var_fields.get v_field_seen_pos a.var.v_fields + else Var_fields.get v_field_seen_neg a.var.v_fields - (* Decisions & propagations *) - type t = - | Lit of lit - | Atom of atom + let[@inline] mark a = + let pos = equal a (abs a) in + if pos + then a.var.v_fields <- Var_fields.set v_field_seen_pos true a.var.v_fields + else a.var.v_fields <- Var_fields.set v_field_seen_neg true a.var.v_fields - let of_lit l = Lit l - let of_atom a = Atom a + let[@inline] make lit = + let var, negated = Var.make lit in + match negated with + | Formula_intf.Negated -> var.na + | Formula_intf.Same_sign -> var.pa + + let pp fmt a = E.Formula.print fmt a.lit + + let pp_a fmt v = + if Array.length v = 0 then ( + Format.fprintf fmt "∅" + ) else ( + pp fmt v.(0); + if (Array.length v) > 1 then begin + for i = 1 to (Array.length v) - 1 do + Format.fprintf fmt " ∨ %a" pp v.(i) + done + end + ) + + (* Complete debug printing *) + let sign a = if a == a.var.pa then "+" else "-" + + let debug_reason fmt = function + | n, _ when n < 0 -> + Format.fprintf fmt "%%" + | n, None -> + Format.fprintf fmt "%d" n + | n, Some Decision -> + Format.fprintf fmt "@@%d" n + | n, Some Bcp c -> + Format.fprintf fmt "->%d/%s" n (name_of_clause c) + | n, Some Semantic -> + Format.fprintf fmt "::%d" n + + let pp_level fmt a = + debug_reason fmt (a.var.v_level, a.var.reason) + + let debug_value fmt a = + if a.is_true then + Format.fprintf fmt "T%a" pp_level a + else if a.neg.is_true then + Format.fprintf fmt "F%a" pp_level a + else + Format.fprintf fmt "" + + let debug out a = + Format.fprintf out "%s%d[%a][atom:@[%a@]]" + (sign a) (a.var.vid+1) debug_value a E.Formula.print a.lit + + let debug_a out vec = + Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec + end (* Elements *) - let[@inline] elt_of_lit l = E_lit l - let[@inline] elt_of_var v = E_var v + module Elt = struct + type t = elt + let[@inline] of_lit l = E_lit l + let[@inline] of_var v = E_var v - let get_elt_id = function - | E_lit l -> l.lid | E_var v -> v.vid - let get_elt_level = function - | E_lit l -> l.l_level | E_var v -> v.v_level - let get_elt_idx = function - | E_lit l -> l.l_idx | E_var v -> v.v_idx - let get_elt_weight = function - | E_lit l -> l.l_weight | E_var v -> v.v_weight + let[@inline] id = function + | E_lit l -> l.lid | E_var v -> v.vid + let[@inline] level = function + | E_lit l -> l.l_level | E_var v -> v.v_level + let[@inline] idx = function + | E_lit l -> l.l_idx | E_var v -> v.v_idx + let[@inline] weight = function + | E_lit l -> l.l_weight | E_var v -> v.v_weight - let set_elt_level e lvl = match e with - | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl - let set_elt_idx e i = match e with - | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i - let set_elt_weight e w = match e with - | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w + let[@inline] set_level e lvl = match e with + | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl + let[@inline] set_idx e i = match e with + | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i + let[@inline] set_weight e w = match e with + | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w + end - (* Name generation *) - let fresh_lname = - let cpt = ref 0 in - fun () -> incr cpt; "L" ^ (string_of_int !cpt) + module Trail_elt = struct + type t = trail_elt + let[@inline] of_lit l = Lit l + let[@inline] of_atom a = Atom a - let fresh_hname = - let cpt = ref 0 in - fun () -> incr cpt; "H" ^ (string_of_int !cpt) + let debug fmt = function + | Lit l -> Lit.debug fmt l + | Atom a -> Atom.debug fmt a + end - let fresh_tname = - let cpt = ref 0 in - fun () -> incr cpt; "T" ^ (string_of_int !cpt) + module Clause = struct + type t = clause + let dummy = dummy_clause - let fresh_name = - let cpt = ref 0 in - fun () -> incr cpt; "C" ^ (string_of_int !cpt) + let make = + let n = ref 0 in + fun ?tag ali premise -> + let atoms = Array.of_list ali in + let name = !n in + incr n; + { name; + tag = tag; + atoms = atoms; + visited = false; + attached = false; + activity = 0.; + cpremise = premise} - (* Pretty printing for atoms and clauses *) - let print_lit fmt v = E.Term.print fmt v.term + let empty = make [] (History []) + let name = name_of_clause + let[@inline] atoms c = c.atoms + let[@inline] tag c = c.tag - let print_atom fmt a = E.Formula.print fmt a.lit + let[@inline] premise c = c.cpremise + let[@inline] set_premise c p = c.cpremise <- p - let print_atoms fmt v = - if Array.length v = 0 then - Format.fprintf fmt "∅" - else begin - print_atom fmt v.(0); - if (Array.length v) > 1 then begin - for i = 1 to (Array.length v) - 1 do - Format.fprintf fmt " ∨ %a" print_atom v.(i) - done - end - end + let[@inline] visited c = c.visited + let[@inline] set_visited c b = c.visited <- b - let print_clause fmt c = - Format.fprintf fmt "%s : %a" c.name print_atoms c.atoms + let[@inline] attached c = c.attached + let[@inline] set_attached c b = c.attached <- b - (* Complete debug printing *) - let sign a = if a == a.var.pa then "+" else "-" + let[@inline] activity c = c.activity + let[@inline] set_activity c w = c.activity <- w - let pp_reason fmt = function - | n, _ when n < 0 -> - Format.fprintf fmt "%%" - | n, None -> - Format.fprintf fmt "%d" n - | n, Some Decision -> - Format.fprintf fmt "@@%d" n - | n, Some Bcp c -> - Format.fprintf fmt "->%d/%s" n c.name - | n, Some Semantic -> - Format.fprintf fmt "::%d" n + let pp fmt c = + Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms - let pp_level fmt a = - pp_reason fmt (a.var.v_level, a.var.reason) + let debug_premise out = function + | Hyp -> Format.fprintf out "hyp" + | Local -> Format.fprintf out "local" + | Lemma _ -> Format.fprintf out "th_lemma" + | History v -> + List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v - let pp_value fmt a = - if a.is_true then - Format.fprintf fmt "T%a" pp_level a - else if a.neg.is_true then - Format.fprintf fmt "F%a" pp_level a - else - Format.fprintf fmt "" + let debug out ({atoms=arr; cpremise=cp;_}as c) = + Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" + (name c) Atom.debug_a arr debug_premise cp - let pp_premise out = function - | Hyp -> Format.fprintf out "hyp" - | Local -> Format.fprintf out "local" - | Lemma _ -> Format.fprintf out "th_lemma" - | History v -> List.iter (fun { name; _ } -> Format.fprintf out "%s,@ " name) v - - let pp_assign fmt v = - match v.assigned with - | None -> - Format.fprintf fmt "" - | Some t -> - Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level E.Term.print t - - let pp_lit out v = - Format.fprintf out "%d[%a][lit:@[%a@]]" - (v.lid+1) pp_assign v E.Term.print v.term - - let pp_atom out a = - Format.fprintf out "%s%d[%a][atom:@[%a@]]" - (sign a) (a.var.vid+1) pp_value a E.Formula.print a.lit - - let pp_atoms_vec out vec = - Array.iter (fun a -> Format.fprintf out "%a@ " pp_atom a) vec - - let pp_clause out {name=name; atoms=arr; cpremise=cp;_} = - Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" - name pp_atoms_vec arr pp_premise cp - - let pp_dimacs fmt {atoms;_} = - let aux fmt a = - Array.iter (fun p -> + let pp_dimacs fmt {atoms;_} = + let aux fmt a = + Array.iter (fun p -> Format.fprintf fmt "%s%d " (if p == p.var.pa then "-" else "") (p.var.vid+1) ) a - in - Format.fprintf fmt "%a0" aux atoms - - let pp fmt = function - | Lit l -> pp_lit fmt l - | Atom a -> pp_atom fmt a - + in + Format.fprintf fmt "%a0" aux atoms + end end diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 6e69c570..08f011cc 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -24,6 +24,8 @@ Copyright 2016 Simon Cruanes module Var_fields = BitField.Make() +type 'a printer = Format.formatter -> 'a -> unit + module type S = sig (** The signatures of clauses used in the Solver. *) @@ -86,7 +88,7 @@ module type S = sig [a.neg] wraps the theory negation of [f]. *) and clause = { - name : string; (** Clause name, mainly for printing, unique. *) + name : int; (** Clause name, mainly for printing, unique. *) tag : int option; (** User-provided tag for clauses. *) atoms : atom array; (** The atoms that constitute the clause.*) mutable cpremise : premise; (** The premise of the clause, i.e. the justification @@ -124,15 +126,11 @@ module type S = sig satisfied by the solver. *) (** {2 Decisions and propagations} *) - type t = + type trail_elt = | Lit of lit | Atom of atom (**) (** Either a lit of an atom *) - val of_lit : lit -> t - val of_atom : atom -> t - (** Constructors and destructors *) - (** {2 Elements} *) type elt = @@ -145,77 +143,136 @@ module type S = sig val iter_elt : (elt -> unit) -> unit (** Read access to the vector of variables created *) - val elt_of_lit : lit -> elt - val elt_of_var : var -> elt - (** Constructors & destructor for elements *) + (** {2 Variables, Literals & Clauses } *) - val get_elt_id : elt -> int - val get_elt_level : elt -> int - val get_elt_idx : elt -> int - val get_elt_weight : elt -> float - val set_elt_level : elt -> int -> unit - val set_elt_idx : elt -> int -> unit - val set_elt_weight : elt -> float -> unit - (** Accessors for elements *) + module Lit : sig + type t = lit + val term : t -> term + val make : term -> t + (** Returns the variable associated with the term *) - (** {2 Variables, Litterals & Clauses } *) + val level : t -> int + val set_level : t -> int -> unit - val dummy_var : var - val dummy_atom : atom - val dummy_clause : clause - (** Dummy values for use in vector dummys *) + val assigned : t -> term option + val set_assigned : t -> term option -> unit + val weight : t -> float + val set_weight : t -> float -> unit - val add_term : term -> lit - (** Returns the variable associated with the term *) - val add_atom : formula -> atom - (** Returns the atom associated with the given formula *) - val make_boolean_var : formula -> var * Formula_intf.negated - (** Returns the variable linked with the given formula, and whether the atom associated with the formula - is [var.pa] or [var.na] *) + val pp : t printer + val debug : t printer + end - val empty_clause : clause - (** The empty clause *) - val make_clause : ?tag:int -> string -> atom list -> premise -> clause - (** [make_clause name atoms size premise] creates a clause with the given attributes. *) + module Var : sig + type t = var + val dummy : t - (** {2 Helpers} *) + val pos : t -> atom + val neg : t -> atom - val mark : atom -> unit - (** Mark the atom as seen, using the 'seen' field in the variable. *) + val level : t -> int + val set_level : t -> int -> unit + val reason : t -> reason option + val set_reason : t -> reason option -> unit + val assignable : t -> lit list option + val set_assignable : t -> lit list option -> unit + val weight : t -> float + val set_weight : t -> float -> unit - val seen : atom -> bool - (** Returns wether the atom has been marked as seen. *) + val make : formula -> t * Formula_intf.negated + (** Returns the variable linked with the given formula, + and whether the atom associated with the formula + is [var.pa] or [var.na] *) - val seen_both : var -> bool - (** both atoms have been seen? *) + val seen_both : t -> bool + (** both atoms have been seen? *) - val clear : var -> unit - (** Clear the 'seen' field of the variable. *) + val clear : t -> unit + (** Clear the 'seen' field of the variable. *) + end + module Atom : sig + type t = atom + val dummy : t + val level : t -> int + val reason : t -> reason option + val lit : t -> formula + val equal : t -> t -> bool + val compare : t -> t -> int + val var : t -> Var.t + val abs : t -> t (** positive atom *) + val neg : t -> t + val id : t -> int + val is_true : t -> bool + val is_false : t -> bool - (** {2 Clause names} *) + val make : formula -> t + (** Returns the atom associated with the given formula *) - val fresh_name : unit -> string - val fresh_lname : unit -> string - val fresh_tname : unit -> string - val fresh_hname : unit -> string - (** Fresh names for clauses *) + val mark : t -> unit + (** Mark the atom as seen, using the 'seen' field in the variable. *) - (** {2 Printing} *) + val seen : t -> bool + (** Returns wether the atom has been marked as seen. *) - val print_lit : Format.formatter -> lit -> unit - val print_atom : Format.formatter -> atom -> unit - val print_clause : Format.formatter -> clause -> unit - (** Pretty printing functions for atoms and clauses *) + val pp : t printer + val pp_a : t array printer + val debug : t printer + val debug_a : t array printer + end - val pp : Format.formatter -> t -> unit - val pp_lit : Format.formatter -> lit -> unit - val pp_atom : Format.formatter -> atom -> unit - val pp_clause : Format.formatter -> clause -> unit - val pp_dimacs : Format.formatter -> clause -> unit - val pp_reason : Format.formatter -> (int * reason option) -> unit - (** Debug function for atoms and clauses (very verbose) *) + module Elt : sig + type t = elt + val of_lit : Lit.t -> t + val of_var : Var.t -> t + + val id : t -> int + val level : t -> int + val idx : t -> int + val weight : t -> float + + val set_level : t -> int -> unit + val set_idx : t -> int -> unit + val set_weight : t -> float -> unit + end + + module Clause : sig + type t = clause + val dummy : t + + val name : t -> string + val atoms : t -> Atom.t array + val tag : t -> int option + val premise : t -> premise + val set_premise : t -> premise -> unit + + val visited : t -> bool + val set_visited : t -> bool -> unit + val attached : t -> bool + val set_attached : t -> bool -> unit + val activity : t -> float + val set_activity : t -> float -> unit + + val empty : t + (** The empty clause *) + + val make : ?tag:int -> Atom.t list -> premise -> clause + (** [make_clause name atoms size premise] creates a clause with the given attributes. *) + + val pp : t printer + val pp_dimacs : t printer + val debug : t printer + end + + module Trail_elt : sig + type t = trail_elt + + val of_lit : Lit.t -> t + val of_atom : Atom.t -> t + (** Constructors and destructors *) + val debug : t printer + end end diff --git a/src/main/main.ml b/src/main/main.ml index f018d8fa..4de98a45 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -41,7 +41,7 @@ module Make let check_clause c = let l = List.map (function a -> Log.debugf 99 - (fun k -> k "Checking value of %a" S.St.pp_atom (S.St.add_atom a)); + (fun k -> k "Checking value of %a" S.St.Atom.debug (S.St.Atom.make a)); state.Msat.eval a) c in List.exists (fun x -> x) l in From ef7333af6d1e57a5d7d6a705cec85fa8b198eb03 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 16:48:26 +0100 Subject: [PATCH 033/182] make state explicit and add `type t` state-wrapper in most modules --- src/backend/Coq.ml | 11 +- src/backend/Dimacs.ml | 16 +- src/backend/Dimacs.mli | 4 +- src/core/Internal.ml | 615 +++++++++++++++++----------------- src/core/Internal.mli | 46 +-- src/core/Res.ml | 5 +- src/core/Res_intf.ml | 2 + src/core/Solver.ml | 64 ++-- src/core/Solver.mli | 3 +- src/core/Solver_intf.ml | 19 +- src/core/Solver_types.ml | 138 ++++---- src/core/Solver_types.mli | 4 +- src/core/Solver_types_intf.ml | 35 +- src/main/main.ml | 32 +- src/mcsat/Minismt_mcsat.ml | 4 +- src/mcsat/Minismt_mcsat.mli | 2 +- src/sat/Minismt_sat.ml | 3 +- src/sat/Minismt_sat.mli | 2 +- src/smt/Minismt_smt.ml | 2 +- src/smt/Minismt_smt.mli | 2 +- src/solver/mcsolver.ml | 6 +- src/solver/mcsolver.mli | 7 +- src/solver/solver.ml | 6 +- src/solver/solver.mli | 5 +- tests/test_api.ml | 17 +- 25 files changed, 558 insertions(+), 492 deletions(-) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index 47fa6e3e..f33f0a2c 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -127,21 +127,18 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause | S.Lemma _ -> A.prove_lemma fmt (name clause) clause | S.Duplicate (p, l) -> - let p' = S.expand p in - let c = p'.S.conclusion in + let c = S.conclusion p in let () = elim_duplicate fmt clause c l in clean t fmt [c] | S.Resolution (p1, p2, a) -> - let c1 = (S.expand p1).S.conclusion in - let c2 = (S.expand p2).S.conclusion in + let c1 = S.conclusion p1 in + let c2 = S.conclusion p2 in if resolution fmt clause c1 c2 a then clean t fmt [c1; c2] let count_uses p = let h = S.H.create 4013 in let aux () node = - List.iter (fun p' -> - incr_use h S.((expand p').conclusion)) - (S.parents node.S.step) + List.iter (fun p' -> incr_use h S.(conclusion p')) (S.parents node.S.step) in let () = S.fold aux () p in h diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index a4e35b0f..07b1ee02 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -7,18 +7,18 @@ Copyright 2014 Simon Cruanes open Msat module type S = sig + type st type clause + (** The type of clauses *) val export : + st -> Format.formatter -> hyps:clause Vec.t -> history:clause Vec.t -> local:clause Vec.t -> unit - (** Export the given clause vectors to the dimacs format. - The arguments should be transmitted directly from the corresponding - function of the {Internal} module. *) val export_icnf : Format.formatter -> @@ -26,13 +26,11 @@ module type S = sig history:clause Vec.t -> local:clause Vec.t -> unit - (** Export the given clause vectors to the dimacs format. - The arguments should be transmitted directly from the corresponding - function of the {Internal} module. *) end -module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct +module Make(St : Solver_types_intf.S) = struct + type st = St.t (* Dimacs & iCNF export *) let export_vec name fmt vec = @@ -76,7 +74,7 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct ) learnt; lemmas - let export fmt ~hyps ~history ~local = + let export st fmt ~hyps ~history ~local = assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); (* Learnt clauses, then filtered to only keep only the theory lemmas; all other learnt clauses should be logical @@ -85,7 +83,7 @@ module Make(St : Solver_types_intf.S)(Dummy: sig end) = struct (* Local assertions *) assert (Vec.for_all (fun c -> St.Local = St.Clause.premise c) local); (* Number of atoms and clauses *) - let n = St.nb_elt () in + let n = St.nb_elt st in let m = Vec.size local + Vec.size hyps + Vec.size lemmas in Format.fprintf fmt "@[p cnf %d %d@,%a%a%a@]@." n m diff --git a/src/backend/Dimacs.mli b/src/backend/Dimacs.mli index 0ad94e63..e829bebd 100644 --- a/src/backend/Dimacs.mli +++ b/src/backend/Dimacs.mli @@ -13,11 +13,13 @@ Copyright 2014 Simon Cruanes open Msat module type S = sig + type st type clause (** The type of clauses *) val export : + st -> Format.formatter -> hyps:clause Vec.t -> history:clause Vec.t -> @@ -42,6 +44,6 @@ module type S = sig end -module Make(St: Solver_types_intf.S)(Dummy: sig end) : S with type clause := St.clause +module Make(St: Solver_types_intf.S) : S with type clause := St.clause and type st = St.t (** Functor to create a module for exporting probems to the dimacs (& iCNF) formats. *) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index b88f8fdd..1c39d704 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -9,7 +9,6 @@ module Make (Plugin : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - (Dummy: sig end) = struct module Proof = Res.Make(St) @@ -36,7 +35,8 @@ module Make let debug = 50 (* Singleton type containing the current state *) - type env = { + type t = { + st : St.t; (* Clauses are simplified for eficiency purposes. In the following vectors, the comments actually refer to the original non-simplified @@ -134,7 +134,8 @@ module Make } (* Starting environment. *) - let env = { + let create ?(st=St.create()) () : t = { + st; unsat_conflict = None; next_decision = None; @@ -181,17 +182,19 @@ module Make } (* Misc functions *) - let to_float i = float_of_int i - let to_int f = int_of_float f + let to_float = float_of_int + let to_int = int_of_float - let nb_clauses () = Vec.size env.clauses_hyps + let[@inline] st t = t.st + let[@inline] nb_clauses st = Vec.size st.clauses_hyps (* let nb_vars () = St.nb_elt () *) - let decision_level () = Vec.size env.elt_levels - let base_level () = Vec.size env.user_levels + + let[@inline] decision_level st = Vec.size st.elt_levels + let[@inline] base_level st = Vec.size st.user_levels (* Are the assumptions currently unsat ? *) - let is_unsat () = - match env.unsat_conflict with + let[@inline] is_unsat st = + match st.unsat_conflict with | Some _ -> true | None -> false @@ -210,15 +213,15 @@ module Make (* When we have a new literal, we need to first create the list of its subterms. *) - let atom (f:St.formula) : atom = - let res = Atom.make f in + let mk_atom st (f:St.formula) : atom = + let res = Atom.make st.st f in if St.mcsat then ( begin match res.var.v_assignable with | Some _ -> () | None -> let l = ref [] in Plugin.iter_assignable - (fun t -> l := Lit.make t :: !l) + (fun t -> l := Lit.make st.st t :: !l) res.var.pa.lit; res.var.v_assignable <- Some !l; end; @@ -232,79 +235,79 @@ module Make When we add a variable (which wraps a formula), we also need to add all its subterms. *) - let rec insert_var_order (elt:elt) : unit = - H.insert env.order elt; + let rec insert_var_order st (elt:elt) : unit = + H.insert st.order elt; begin match elt with | E_lit _ -> () - | E_var v -> insert_subterms_order v + | E_var v -> insert_subterms_order st v end - and insert_subterms_order (v:St.var) : unit = - iter_sub (fun t -> insert_var_order (Elt.of_lit t)) v + and insert_subterms_order st (v:St.var) : unit = + iter_sub (fun t -> insert_var_order st (Elt.of_lit t)) v (* Add new litterals/atoms on which to decide on, even if there is no clause that constrains it. We could maybe check if they have already has been decided before inserting them into the heap, if it appears that it helps performance. *) - let new_lit t = - let l = Lit.make t in - insert_var_order (E_lit l) + let new_lit st t = + let l = Lit.make st.st t in + insert_var_order st (E_lit l) - let new_atom p = - let a = atom p in - insert_var_order (E_var a.var) + let new_atom st p = + let a = mk_atom st p in + insert_var_order st (E_var a.var) (* Rather than iterate over all the heap when we want to decrease all the variables/literals activity, we instead increase the value by which we increase the activity of 'interesting' var/lits. *) - let var_decay_activity () = - env.var_incr <- env.var_incr *. env.var_decay + let var_decay_activity st = + st.var_incr <- st.var_incr *. st.var_decay - let clause_decay_activity () = - env.clause_incr <- env.clause_incr *. env.clause_decay + let clause_decay_activity st = + st.clause_incr <- st.clause_incr *. st.clause_decay (* increase activity of [v] *) - let var_bump_activity_aux v = - v.v_weight <- v.v_weight +. env.var_incr; + let var_bump_activity_aux st v = + v.v_weight <- v.v_weight +. st.var_incr; if v.v_weight > 1e100 then ( - for i = 0 to (St.nb_elt ()) - 1 do - Elt.set_weight (St.get_elt i) ((Elt.weight (St.get_elt i)) *. 1e-100) + for i = 0 to St.nb_elt st.st - 1 do + Elt.set_weight (St.get_elt st.st i) ((Elt.weight (St.get_elt st.st i)) *. 1e-100) done; - env.var_incr <- env.var_incr *. 1e-100; + st.var_incr <- st.var_incr *. 1e-100; ); let elt = Elt.of_var v in if H.in_heap elt then ( - H.decrease env.order elt + H.decrease st.order elt ) (* increase activity of literal [l] *) - let lit_bump_activity_aux (l:lit): unit = - l.l_weight <- l.l_weight +. env.var_incr; + let lit_bump_activity_aux st (l:lit): unit = + l.l_weight <- l.l_weight +. st.var_incr; if l.l_weight > 1e100 then ( - for i = 0 to (St.nb_elt ()) - 1 do - Elt.set_weight (St.get_elt i) ((Elt.weight (St.get_elt i)) *. 1e-100) + for i = 0 to St.nb_elt st.st - 1 do + Elt.set_weight (St.get_elt st.st i) ((Elt.weight (St.get_elt st.st i)) *. 1e-100) done; - env.var_incr <- env.var_incr *. 1e-100; + st.var_incr <- st.var_incr *. 1e-100; ); let elt = Elt.of_lit l in if H.in_heap elt then ( - H.decrease env.order elt + H.decrease st.order elt ) (* increase activity of var [v] *) - let var_bump_activity (v:var): unit = - var_bump_activity_aux v; - iter_sub lit_bump_activity_aux v + let var_bump_activity st (v:var): unit = + var_bump_activity_aux st v; + iter_sub (lit_bump_activity_aux st) v (* increase activity of clause [c] *) - let clause_bump_activity (c:clause) : unit = - c.activity <- c.activity +. env.clause_incr; + let clause_bump_activity st (c:clause) : unit = + c.activity <- c.activity +. st.clause_incr; if c.activity > 1e20 then ( - for i = 0 to (Vec.size env.clauses_learnt) - 1 do - (Vec.get env.clauses_learnt i).activity <- - (Vec.get env.clauses_learnt i).activity *. 1e-20; + for i = 0 to Vec.size st.clauses_learnt - 1 do + (Vec.get st.clauses_learnt i).activity <- + (Vec.get st.clauses_learnt i).activity *. 1e-20; done; - env.clause_incr <- env.clause_incr *. 1e-20 + st.clause_incr <- st.clause_incr *. 1e-20 ) (* Simplification of clauses. @@ -326,7 +329,7 @@ module Make else Array.to_list (Array.sub arr i (Array.length arr - i)) (* Eliminates atom doublons in clauses *) - let eliminate_doublons clause : clause = + let eliminate_duplicates clause : clause = let trivial = ref false in let duplicates = ref [] in let res = ref [] in @@ -404,11 +407,11 @@ module Make stack of literals i.e we have indeed reached a propagation fixpoint before making a new decision *) - let new_decision_level() = - assert (env.th_head = Vec.size env.trail); - assert (env.elt_head = Vec.size env.trail); - Vec.push env.elt_levels (Vec.size env.trail); - Vec.push env.th_levels (Plugin.current_level ()); (* save the current theory state *) + let new_decision_level st = + assert (st.th_head = Vec.size st.trail); + assert (st.elt_head = Vec.size st.trail); + Vec.push st.elt_levels (Vec.size st.trail); + Vec.push st.th_levels (Plugin.current_level ()); (* save the current theory state *) () (* Attach/Detach a clause. @@ -429,32 +432,32 @@ module Make Used to backtrack, i.e cancel down to [lvl] excluded, i.e we want to go back to the state the solver was in when decision level [lvl] was created. *) - let cancel_until lvl = - assert (lvl >= base_level ()); + let cancel_until st lvl = + assert (lvl >= base_level st); (* Nothing to do if we try to backtrack to a non-existent level. *) - if decision_level () <= lvl then ( + if decision_level st <= lvl then ( Log.debugf debug (fun k -> k "Already at level <= %d" lvl) ) else ( Log.debugf info (fun k -> k "Backtracking to lvl %d" lvl); (* We set the head of the solver and theory queue to what it was. *) - let head = ref (Vec.get env.elt_levels lvl) in - env.elt_head <- !head; - env.th_head <- !head; + let head = ref (Vec.get st.elt_levels lvl) in + st.elt_head <- !head; + st.th_head <- !head; (* Now we need to cleanup the vars that are not valid anymore (i.e to the right of elt_head in the queue. *) - for c = env.elt_head to Vec.size env.trail - 1 do - match (Vec.get env.trail c) with + for c = st.elt_head to Vec.size st.trail - 1 do + match (Vec.get st.trail c) with (* A literal is unassigned, we nedd to add it back to the heap of potentially assignable literals, unless it has a level lower than [lvl], in which case we just move it back. *) | Lit l -> if l.l_level <= lvl then ( - Vec.set env.trail !head (Trail_elt.of_lit l); + Vec.set st.trail !head (Trail_elt.of_lit l); head := !head + 1 ) else ( l.assigned <- None; l.l_level <- -1; - insert_var_order (Elt.of_lit l) + insert_var_order st (Elt.of_lit l) ) (* A variable is not true/false anymore, one of two things can happen: *) | Atom a -> @@ -462,7 +465,7 @@ module Make (* It is a late propagation, which has a level lower than where we backtrack, so we just move it to the head of the queue, to be propagated again. *) - Vec.set env.trail !head (Trail_elt.of_atom a); + Vec.set st.trail !head (Trail_elt.of_atom a); head := !head + 1 ) else ( (* it is a result of bolean propagation, or a semantic propagation @@ -472,24 +475,24 @@ module Make a.neg.is_true <- false; a.var.v_level <- -1; a.var.reason <- None; - insert_var_order (Elt.of_var a.var) + insert_var_order st (Elt.of_var a.var) ) done; (* Recover the right theory state. *) - Plugin.backtrack (Vec.get env.th_levels lvl); + Plugin.backtrack (Vec.get st.th_levels lvl); (* Resize the vectors according to their new size. *) - Vec.shrink env.trail !head; - Vec.shrink env.elt_levels lvl; - Vec.shrink env.th_levels lvl; + Vec.shrink st.trail !head; + Vec.shrink st.elt_levels lvl; + Vec.shrink st.th_levels lvl; ); - assert (Vec.size env.elt_levels = Vec.size env.th_levels); + assert (Vec.size st.elt_levels = Vec.size st.th_levels); () (* Unsatisfiability is signaled through an exception, since it can happen in multiple places (adding new clauses, or solving for instance). *) - let report_unsat confl : _ = + let report_unsat st confl : _ = Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" Clause.debug confl); - env.unsat_conflict <- Some confl; + st.unsat_conflict <- Some confl; raise Unsat (* Simplification of boolean propagation reasons. @@ -530,7 +533,7 @@ module Make (* Boolean propagation. Wrapper function for adding a new propagated formula. *) - let enqueue_bool a ~level:lvl reason : unit = + let enqueue_bool st a ~level:lvl reason : unit = if a.neg.is_true then ( Log.debugf error (fun k->k "Trying to enqueue a false literal: %a" Atom.debug a); assert false @@ -544,22 +547,22 @@ module Make a.is_true <- true; a.var.v_level <- lvl; a.var.reason <- Some reason; - Vec.push env.trail (Trail_elt.of_atom a); + Vec.push st.trail (Trail_elt.of_atom a); Log.debugf debug - (fun k->k "Enqueue (%d): %a" (Vec.size env.trail) Atom.debug a); + (fun k->k "Enqueue (%d): %a" (Vec.size st.trail) Atom.debug a); () - let enqueue_semantic a terms = + let enqueue_semantic st a terms = if not a.is_true then ( - let l = List.map Lit.make terms in + let l = List.map (Lit.make st.st) terms in let lvl = List.fold_left (fun acc {l_level; _} -> assert (l_level > 0); max acc l_level) 0 l in - H.grow_to_at_least env.order (St.nb_elt ()); - enqueue_bool a ~level:lvl Semantic + H.grow_to_at_least st.order (St.nb_elt st.st); + enqueue_bool st a ~level:lvl Semantic ) (* MCsat semantic assignment *) - let enqueue_assign l value lvl = + let enqueue_assign st l value lvl = match l.assigned with | Some _ -> Log.debugf error @@ -569,9 +572,9 @@ module Make assert (l.l_level < 0); l.assigned <- Some value; l.l_level <- lvl; - Vec.push env.trail (Trail_elt.of_lit l); + Vec.push st.trail (Trail_elt.of_lit l); Log.debugf debug - (fun k -> k "Enqueue (%d): %a" (Vec.size env.trail) Lit.debug l); + (fun k -> k "Enqueue (%d): %a" (Vec.size st.trail) Lit.debug l); () (* swap elements of array *) @@ -610,7 +613,7 @@ module Make (* evaluate an atom for MCsat, if it's not assigned by boolean propagation/decision *) - let th_eval a : bool option = + let th_eval st a : bool option = if a.is_true || a.neg.is_true then None else match Plugin.eval a.lit with | Plugin_intf.Unknown -> None @@ -620,25 +623,25 @@ module Make Format.asprintf "msat:core/internal.ml: %s" "semantic propagation at level 0 are currently forbidden")); let atom = if b then a else a.neg in - enqueue_semantic atom l; + enqueue_semantic st atom l; Some b (* find which level to backtrack to, given a conflict clause and a boolean stating whether it is a UIP ("Unique Implication Point") precond: the atom list is sorted by decreasing decision level *) - let backtrack_lvl : atom list -> int * bool = function + let backtrack_lvl st : atom list -> int * bool = function | [] | [_] -> 0, true | a :: b :: _ -> - assert(a.var.v_level > base_level ()); + assert(a.var.v_level > base_level st); if a.var.v_level > b.var.v_level then ( (* backtrack below [a], so we can propagate [not a] *) b.var.v_level, true ) else ( assert (a.var.v_level = b.var.v_level); - assert (a.var.v_level >= base_level ()); - max (a.var.v_level - 1) (base_level ()), false + assert (a.var.v_level >= base_level st); + max (a.var.v_level - 1) (base_level st), false ) (* result of conflict analysis, containing the learnt clause and some @@ -654,24 +657,24 @@ module Make cr_is_uip: bool; (* conflict is UIP? *) } - let get_atom i = - match Vec.get env.trail i with + let get_atom st i = + match Vec.get st.trail i with | Lit _ -> assert false | Atom x -> x (* conflict analysis for SAT Same idea as the mcsat analyze function (without semantic propagations), except we look the the Last UIP (TODO: check ?), and do it in an imperative and efficient manner. *) - let analyze_sat c_clause : conflict_res = + let analyze_sat st c_clause : conflict_res = let pathC = ref 0 in let learnt = ref [] in let cond = ref true in let blevel = ref 0 in let seen = ref [] in let c = ref (Some c_clause) in - let tr_ind = ref (Vec.size env.trail - 1) in + let tr_ind = ref (Vec.size st.trail - 1) in let history = ref [] in - assert (decision_level () > 0); + assert (decision_level st > 0); let conflict_level = Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in @@ -684,7 +687,7 @@ module Make | Some clause -> Log.debugf debug (fun k->k" Resolving clause: %a" Clause.debug clause); begin match clause.cpremise with - | History _ -> clause_bump_activity clause + | History _ -> clause_bump_activity st clause | Hyp | Local | Lemma _ -> () end; history := clause :: !history; @@ -703,7 +706,7 @@ module Make Atom.mark q.neg; seen := q :: !seen; if q.var.v_level > 0 then ( - var_bump_activity q.var; + var_bump_activity st q.var; if q.var.v_level >= conflict_level then ( incr pathC; ) else ( @@ -717,7 +720,7 @@ module Make (* look for the next node to expand *) while - let a = Vec.get env.trail !tr_ind in + let a = Vec.get st.trail !tr_ind in Log.debugf debug (fun k -> k " looking at: %a" Trail_elt.debug a); match a with | Atom q -> @@ -727,7 +730,7 @@ module Make do decr tr_ind; done; - let p = get_atom !tr_ind in + let p = get_atom st !tr_ind in decr pathC; decr tr_ind; match !pathC, p.var.reason with @@ -746,15 +749,15 @@ module Make done; List.iter (fun q -> Var.clear q.var) !seen; let l = List.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) !learnt in - let level, is_uip = backtrack_lvl l in + let level, is_uip = backtrack_lvl st l in { cr_backtrack_lvl = level; cr_learnt = l; cr_history = List.rev !history; cr_is_uip = is_uip; } - let analyze c_clause : conflict_res = - analyze_sat c_clause + let[@inline] analyze st c_clause : conflict_res = + analyze_sat st c_clause (* if St.mcsat then analyze_mcsat c_clause @@ -762,67 +765,69 @@ module Make *) (* add the learnt clause to the clause database, propagate, etc. *) - let record_learnt_clause (confl:clause) (cr:conflict_res): unit = + let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = begin match cr.cr_learnt with | [] -> assert false | [fuip] -> assert (cr.cr_backtrack_lvl = 0); if fuip.neg.is_true then ( - report_unsat confl + report_unsat st confl ) else ( let uclause = Clause.make cr.cr_learnt (History cr.cr_history) in - Vec.push env.clauses_learnt uclause; + Vec.push st.clauses_learnt uclause; (* no need to attach [uclause], it is true at level 0 *) - enqueue_bool fuip ~level:0 (Bcp uclause) + enqueue_bool st fuip ~level:0 (Bcp uclause) ) | fuip :: _ -> let lclause = Clause.make cr.cr_learnt (History cr.cr_history) in - Vec.push env.clauses_learnt lclause; + Vec.push st.clauses_learnt lclause; attach_clause lclause; - clause_bump_activity lclause; + clause_bump_activity st lclause; if cr.cr_is_uip then ( - enqueue_bool fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) + enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) ) else ( - env.next_decision <- Some fuip.neg + st.next_decision <- Some fuip.neg ) end; - var_decay_activity (); - clause_decay_activity () + var_decay_activity st; + clause_decay_activity st (* process a conflict: - learn clause - backtrack - report unsat if conflict at level 0 *) - let add_boolean_conflict (confl:clause): unit = + let add_boolean_conflict st (confl:clause): unit = Log.debugf info (fun k -> k "Boolean conflict: %a" Clause.debug confl); - env.next_decision <- None; - env.conflicts <- env.conflicts + 1; - assert (decision_level() >= base_level ()); - if decision_level() = base_level () - || Array.for_all (fun a -> a.var.v_level <= base_level ()) confl.atoms then - report_unsat confl; (* Top-level conflict *) - let cr = analyze confl in - cancel_until (max cr.cr_backtrack_lvl (base_level ())); - record_learnt_clause confl cr + st.next_decision <- None; + st.conflicts <- st.conflicts + 1; + assert (decision_level st >= base_level st); + if decision_level st = base_level st || + Array.for_all (fun a -> a.var.v_level <= base_level st) confl.atoms then ( + (* Top-level conflict *) + report_unsat st confl; + ); + let cr = analyze st confl in + cancel_until st (max cr.cr_backtrack_lvl (base_level st)); + record_learnt_clause st confl cr (* Get the correct vector to insert a clause in. *) - let clause_vector c = + let clause_vector st c = match c.cpremise with - | Hyp -> env.clauses_hyps - | Local -> env.clauses_temp - | Lemma _ | History _ -> env.clauses_learnt + | Hyp -> st.clauses_hyps + | Local -> st.clauses_temp + | Lemma _ | History _ -> st.clauses_learnt (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) - let add_clause (init:clause) : unit = + let add_clause st (init:clause) : unit = Log.debugf debug (fun k -> k "Adding clause: @[%a@]" Clause.debug init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) - Array.iter (fun x -> insert_var_order (Elt.of_var x.var)) init.atoms; - let vec = clause_vector init in + Array.iter (fun x -> insert_var_order st (Elt.of_var x.var)) init.atoms; + let vec = clause_vector st init in try - let c = eliminate_doublons init in + let c = eliminate_duplicates init in Log.debugf debug (fun k -> k "Doublons eliminated: %a" Clause.debug c); let atoms, history = partition c.atoms in let clause = @@ -840,30 +845,30 @@ module Make (* Report_unsat will raise, and the current clause will be lost if we do not store it somewhere. Since the proof search will end, any of env.clauses_to_add or env.clauses_root is adequate. *) - Stack.push clause env.clauses_root; - report_unsat clause + Stack.push clause st.clauses_root; + report_unsat st clause | [a] -> - cancel_until (base_level ()); + cancel_until st (base_level st); if a.neg.is_true then ( (* Since we cannot propagate the atom [a], in order to not lose the information that [a] must be true, we add clause to the list of clauses to add, so that it will be e-examined later. *) Log.debug debug "Unit clause, adding to clauses to add"; - Stack.push clause env.clauses_to_add; - report_unsat clause + Stack.push clause st.clauses_to_add; + report_unsat st clause ) else if a.is_true then ( (* If the atom is already true, then it should be because of a local hyp. However it means we can't propagate it at level 0. In order to not lose that information, we store the clause in a stack of clauses that we will add to the solver at the next pop. *) Log.debug debug "Unit clause, adding to root clauses"; - assert (0 < a.var.v_level && a.var.v_level <= base_level ()); - Stack.push clause env.clauses_root; + assert (0 < a.var.v_level && a.var.v_level <= base_level st); + Stack.push clause st.clauses_root; () ) else ( Log.debugf debug (fun k->k "Unit clause, propagating: %a" Atom.debug a); Vec.push vec clause; - enqueue_bool a ~level:0 (Bcp clause) + enqueue_bool st a ~level:0 (Bcp clause) ) | a::b::_ -> Vec.push vec clause; @@ -872,30 +877,30 @@ module Make or we might watch the wrong literals. *) put_high_level_atoms_first clause.atoms; attach_clause clause; - add_boolean_conflict clause + add_boolean_conflict st clause ) else ( attach_clause clause; if b.neg.is_true && not a.is_true && not a.neg.is_true then ( let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in - cancel_until (max lvl (base_level ())); - enqueue_bool a ~level:lvl (Bcp clause) + cancel_until st (max lvl (base_level st)); + enqueue_bool st a ~level:lvl (Bcp clause) ) ) with Trivial -> Vec.push vec init; Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) - let flush_clauses () = - if not (Stack.is_empty env.clauses_to_add) then begin - let nbv = St.nb_elt () in - let nbc = env.nb_init_clauses + Stack.length env.clauses_to_add in - H.grow_to_at_least env.order nbv; - Vec.grow_to_at_least env.clauses_hyps nbc; - Vec.grow_to_at_least env.clauses_learnt nbc; - env.nb_init_clauses <- nbc; - while not (Stack.is_empty env.clauses_to_add) do - let c = Stack.pop env.clauses_to_add in - add_clause c + let flush_clauses st = + if not (Stack.is_empty st.clauses_to_add) then begin + let nbv = St.nb_elt st.st in + let nbc = st.nb_init_clauses + Stack.length st.clauses_to_add in + H.grow_to_at_least st.order nbv; + Vec.grow_to_at_least st.clauses_hyps nbc; + Vec.grow_to_at_least st.clauses_learnt nbc; + st.nb_init_clauses <- nbc; + while not (Stack.is_empty st.clauses_to_add) do + let c = Stack.pop st.clauses_to_add in + add_clause st c done end @@ -908,7 +913,7 @@ module Make [i] is the index of [c] in [a.watched] @return whether [c] was removed from [a.watched] *) - let propagate_in_clause (a:atom) (c:clause) (i:int): watch_res = + let propagate_in_clause st (a:atom) (c:clause) (i:int): watch_res = let atoms = c.atoms in let first = atoms.(0) in if first == a.neg then ( @@ -937,15 +942,15 @@ module Make (* no watch lit found *) if first.neg.is_true then ( (* clause is false *) - env.elt_head <- Vec.size env.trail; + st.elt_head <- Vec.size st.trail; raise (Conflict c) ) else ( - match th_eval first with + match th_eval st first with | None -> (* clause is unit, keep the same watches, but propagate *) - enqueue_bool first ~level:(decision_level ()) (Bcp c) + enqueue_bool st first ~level:(decision_level st) (Bcp c) | Some true -> () | Some false -> - env.elt_head <- Vec.size env.trail; + st.elt_head <- Vec.size st.trail; raise (Conflict c) ); Watch_kept @@ -957,7 +962,7 @@ module Make clause watching [a] to see if the clause is false, unit, or has other possible watches @param res the optional conflict clause that the propagation might trigger *) - let propagate_atom a (res:clause option ref) : unit = + let propagate_atom st a (res:clause option ref) : unit = let watched = a.watched in begin try @@ -966,7 +971,7 @@ module Make else ( let c = Vec.get watched i in assert (Clause.attached c); - let j = match propagate_in_clause a c i with + let j = match propagate_in_clause st a c i with | Watch_kept -> i+1 | Watch_removed -> i (* clause at this index changed *) in @@ -981,88 +986,87 @@ module Make () (* Propagation (boolean and theory) *) - let create_atom f = - let a = atom f in - ignore (th_eval a); + let create_atom st f = + let a = mk_atom st f in + ignore (th_eval st a); a - let slice_get i = - match Vec.get env.trail i with + let slice_get st i = + match Vec.get st.trail i with | Atom a -> Plugin_intf.Lit a.lit | Lit {term; assigned = Some v; _} -> Plugin_intf.Assign (term, v) | Lit _ -> assert false - let slice_push (l:formula list) (lemma:proof): unit = - let atoms = List.rev_map create_atom l in + let slice_push st (l:formula list) (lemma:proof): unit = + let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); - Stack.push c env.clauses_to_add + Stack.push c st.clauses_to_add - let slice_propagate f = function + let slice_propagate (st:t) f = function | Plugin_intf.Eval l -> - let a = atom f in - enqueue_semantic a l + let a = mk_atom st f in + enqueue_semantic st a l | Plugin_intf.Consequence (causes, proof) -> - let l = List.rev_map atom causes in + let l = List.rev_map (mk_atom st) causes in if List.for_all (fun a -> a.is_true) l then ( - let p = atom f in + let p = mk_atom st f in let c = Clause.make (p :: List.map Atom.neg l) (Lemma proof) in if p.is_true then () else if p.neg.is_true then ( - Stack.push c env.clauses_to_add + Stack.push c st.clauses_to_add ) else ( - H.grow_to_at_least env.order (St.nb_elt ()); - insert_subterms_order p.var; - let lvl = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in - enqueue_bool p ~level:lvl (Bcp c) + H.grow_to_at_least st.order (St.nb_elt st.st); + insert_subterms_order st p.var; + let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in + enqueue_bool st p ~level (Bcp c) ) ) else ( invalid_arg "Msat.Internal.slice_propagate" ) - let current_slice (): (_,_,_) Plugin_intf.slice = { - Plugin_intf.start = env.th_head; - length = (Vec.size env.trail) - env.th_head; - get = slice_get; - push = slice_push; - propagate = slice_propagate; + let current_slice st : (_,_,_) Plugin_intf.slice = { + Plugin_intf.start = st.th_head; + length = (Vec.size st.trail) - st.th_head; + get = slice_get st; + push = slice_push st; + propagate = slice_propagate st; } (* full slice, for [if_sat] final check *) - let full_slice () : (_,_,_) Plugin_intf.slice = { + let full_slice st : (_,_,_) Plugin_intf.slice = { Plugin_intf.start = 0; - length = Vec.size env.trail; - get = slice_get; - push = slice_push; + length = Vec.size st.trail; + get = slice_get st; + push = slice_push st; propagate = (fun _ -> assert false); } (* some boolean literals were decided/propagated within Msat. Now we need to inform the theory of those assumptions, so it can do its job. @return the conflict clause, if the theory detects unsatisfiability *) - let rec theory_propagate (): clause option = - assert (env.elt_head = Vec.size env.trail); - assert (env.th_head <= env.elt_head); - if env.th_head = env.elt_head then ( + let rec theory_propagate st : clause option = + assert (st.elt_head = Vec.size st.trail); + assert (st.th_head <= st.elt_head); + if st.th_head = st.elt_head then ( None (* fixpoint/no propagation *) ) else ( - let slice = current_slice () in - env.th_head <- env.elt_head; (* catch up *) + let slice = current_slice st in + st.th_head <- st.elt_head; (* catch up *) match Plugin.assume slice with | Plugin_intf.Sat -> - propagate () + propagate st | Plugin_intf.Unsat (l, p) -> (* conflict *) - let l = List.rev_map create_atom l in + let l = List.rev_map (create_atom st) l in (* Assert that the conflcit is indeeed a conflict *) if not @@ List.for_all (fun a -> a.neg.is_true) l then ( raise (Invalid_argument "msat:core/internal: invalid conflict"); ); - (* Insert elements for decision (and ensure the heap is big enough) *) - H.grow_to_at_least env.order (St.nb_elt ()); - List.iter (fun a -> insert_var_order (Elt.of_var a.var)) l; + H.grow_to_at_least st.order (St.nb_elt st.st); + List.iter (fun a -> insert_var_order st (Elt.of_var a.var)) l; (* Create the clause and return it. *) let c = St.Clause.make l (Lemma p) in Some c @@ -1070,31 +1074,31 @@ module Make (* fixpoint between boolean propagation and theory propagation @return a conflict clause, if any *) - and propagate (): clause option = + and propagate (st:t) : clause option = (* First, treat the stack of lemmas added by the theory, if any *) - flush_clauses (); + flush_clauses st; (* Now, check that the situation is sane *) - assert (env.elt_head <= Vec.size env.trail); - if env.elt_head = Vec.size env.trail then - theory_propagate () - else begin + assert (st.elt_head <= Vec.size st.trail); + if st.elt_head = Vec.size st.trail then + theory_propagate st + else ( let num_props = ref 0 in let res = ref None in - while env.elt_head < Vec.size env.trail do - begin match Vec.get env.trail env.elt_head with + while st.elt_head < Vec.size st.trail do + begin match Vec.get st.trail st.elt_head with | Lit _ -> () | Atom a -> incr num_props; - propagate_atom a res + propagate_atom st a res end; - env.elt_head <- env.elt_head + 1; + st.elt_head <- st.elt_head + 1; done; - env.propagations <- env.propagations + !num_props; - env.simpDB_props <- env.simpDB_props - !num_props; + st.propagations <- st.propagations + !num_props; + st.simpDB_props <- st.simpDB_props - !num_props; match !res with - | None -> theory_propagate () + | None -> theory_propagate st | _ -> !res - end + ) (* remove some learnt clauses NOTE: so far we do not forget learnt clauses. We could, as long as @@ -1102,50 +1106,50 @@ module Make let reduce_db () = () (* Decide on a new literal, and enqueue it into the trail *) - let rec pick_branch_aux atom: unit = + let rec pick_branch_aux st atom : unit = let v = atom.var in if v.v_level >= 0 then ( assert (v.pa.is_true || v.na.is_true); - pick_branch_lit () + pick_branch_lit st ) else match Plugin.eval atom.lit with | Plugin_intf.Unknown -> - env.decisions <- env.decisions + 1; - new_decision_level(); - let current_level = decision_level () in - enqueue_bool atom ~level:current_level Decision + st.decisions <- st.decisions + 1; + new_decision_level st; + let current_level = decision_level st in + enqueue_bool st atom ~level:current_level Decision | Plugin_intf.Valued (b, l) -> let a = if b then atom else atom.neg in - enqueue_semantic a l + enqueue_semantic st a l - and pick_branch_lit () = - match env.next_decision with + and pick_branch_lit st = + match st.next_decision with | Some atom -> - env.next_decision <- None; - pick_branch_aux atom + st.next_decision <- None; + pick_branch_aux st atom | None -> - begin match H.remove_min env.order with + begin match H.remove_min st.order with | E_lit l -> if Lit.level l >= 0 then - pick_branch_lit () + pick_branch_lit st else ( let value = Plugin.assign l.term in - env.decisions <- env.decisions + 1; - new_decision_level(); - let current_level = decision_level () in - enqueue_assign l value current_level + st.decisions <- st.decisions + 1; + new_decision_level st; + let current_level = decision_level st in + enqueue_assign st l value current_level ) | E_var v -> - pick_branch_aux v.pa + pick_branch_aux st v.pa | exception Not_found -> raise Sat end (* do some amount of search, until the number of conflicts or clause learnt reaches the given parameters *) - let search n_of_conflicts n_of_learnts: unit = + let search (st:t) n_of_conflicts n_of_learnts : unit = let conflictC = ref 0 in - env.starts <- env.starts + 1; + st.starts <- st.starts + 1; while true do - match propagate () with + match propagate st with | Some confl -> (* Conflict *) incr conflictC; (* When the theory has raised Unsat, add_boolean_conflict @@ -1153,31 +1157,31 @@ module Make analyzed backtrack clause. So in those case, we use add_clause to make sure the initial conflict clause is also added. *) if Clause.attached confl then - add_boolean_conflict confl + add_boolean_conflict st confl else - add_clause confl + add_clause st confl | None -> (* No Conflict *) - assert (env.elt_head = Vec.size env.trail); - assert (env.elt_head = env.th_head); - if Vec.size env.trail = St.nb_elt () + assert (st.elt_head = Vec.size st.trail); + assert (st.elt_head = st.th_head); + if Vec.size st.trail = St.nb_elt st.st then raise Sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( Log.debug info "Restarting..."; - cancel_until (base_level ()); + cancel_until st (base_level st); raise Restart ); (* if decision_level() = 0 then simplify (); *) if n_of_learnts >= 0 && - Vec.size env.clauses_learnt - Vec.size env.trail >= n_of_learnts + Vec.size st.clauses_learnt - Vec.size st.trail >= n_of_learnts then reduce_db(); - pick_branch_lit () + pick_branch_lit st done - let eval_level lit = - let var, negated = Var.make lit in + let eval_level (st:t) lit = + let var, negated = Var.make st.st lit in if not var.pa.is_true && not var.na.is_true then raise UndecidedLit else assert (var.v_level >= 0); @@ -1188,128 +1192,128 @@ module Make in value, var.v_level - let eval lit = fst (eval_level lit) + let eval st lit = fst (eval_level st lit) - let unsat_conflict () = env.unsat_conflict + let[@inline] unsat_conflict st = st.unsat_conflict - let model () : (term * term) list = + let model (st:t) : (term * term) list = let opt = function Some a -> a | None -> assert false in Vec.fold (fun acc e -> match e with | Lit v -> (v.term, opt v.assigned) :: acc | Atom _ -> acc) - [] env.trail + [] st.trail (* fixpoint of propagation and decisions until a model is found, or a conflict is reached *) - let solve (): unit = + let solve (st:t) : unit = Log.debug 5 "solve"; - if is_unsat () then raise Unsat; - let n_of_conflicts = ref (to_float env.restart_first) in - let n_of_learnts = ref ((to_float (nb_clauses())) *. env.learntsize_factor) in + if is_unsat st then raise Unsat; + let n_of_conflicts = ref (to_float st.restart_first) in + let n_of_learnts = ref ((to_float (nb_clauses st)) *. st.learntsize_factor) in try while true do begin try - search (to_int !n_of_conflicts) (to_int !n_of_learnts) + search st (to_int !n_of_conflicts) (to_int !n_of_learnts) with | Restart -> - n_of_conflicts := !n_of_conflicts *. env.restart_inc; - n_of_learnts := !n_of_learnts *. env.learntsize_inc + n_of_conflicts := !n_of_conflicts *. st.restart_inc; + n_of_learnts := !n_of_learnts *. st.learntsize_inc | Sat -> - assert (env.elt_head = Vec.size env.trail); - begin match Plugin.if_sat (full_slice ()) with + assert (st.elt_head = Vec.size st.trail); + begin match Plugin.if_sat (full_slice st) with | Plugin_intf.Sat -> () | Plugin_intf.Unsat (l, p) -> - let atoms = List.rev_map create_atom l in + let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma p) in Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); - Stack.push c env.clauses_to_add + Stack.push c st.clauses_to_add end; - if Stack.is_empty env.clauses_to_add then raise Sat + if Stack.is_empty st.clauses_to_add then raise Sat end done with Sat -> () - let assume ?tag cnf = + let assume st ?tag cnf = List.iter (fun l -> - let atoms = List.rev_map atom l in + let atoms = List.rev_map (mk_atom st) l in let c = Clause.make ?tag atoms Hyp in Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" Clause.debug c); - Stack.push c env.clauses_to_add) + Stack.push c st.clauses_to_add) cnf (* create a factice decision level for local assumptions *) - let push (): unit = + let push st : unit = Log.debug debug "Pushing a new user level"; - match env.unsat_conflict with + match st.unsat_conflict with | Some _ -> raise Unsat | None -> - cancel_until (base_level ()); + cancel_until st (base_level st); Log.debugf debug (fun k -> k "@[Status:@,@[trail: %d - %d@,%a@]" - env.elt_head env.th_head (Vec.print ~sep:"" Trail_elt.debug) env.trail); - begin match propagate () with + st.elt_head st.th_head (Vec.print ~sep:"" Trail_elt.debug) st.trail); + begin match propagate st with | Some confl -> - report_unsat confl + report_unsat st confl | None -> Log.debugf debug (fun k -> k "@[Current trail:@,@[%a@]@]" - (Vec.print ~sep:"" Trail_elt.debug) env.trail); + (Vec.print ~sep:"" Trail_elt.debug) st.trail); Log.debug info "Creating new user level"; - new_decision_level (); - Vec.push env.user_levels (Vec.size env.clauses_temp); - assert (decision_level () = base_level ()) + new_decision_level st; + Vec.push st.user_levels (Vec.size st.clauses_temp); + assert (decision_level st = base_level st) end (* pop the last factice decision level *) - let pop (): unit = - if base_level () = 0 then + let pop st : unit = + if base_level st = 0 then Log.debug warn "Cannot pop (already at level 0)" else ( Log.debug info "Popping user level"; - assert (base_level () > 0); - env.unsat_conflict <- None; - let n = Vec.last env.user_levels in - Vec.pop env.user_levels; (* before the [cancel_until]! *) + assert (base_level st > 0); + st.unsat_conflict <- None; + let n = Vec.last st.user_levels in + Vec.pop st.user_levels; (* before the [cancel_until]! *) (* Add the root clauses to the clauses to add *) - Stack.iter (fun c -> Stack.push c env.clauses_to_add) env.clauses_root; - Stack.clear env.clauses_root; + Stack.iter (fun c -> Stack.push c st.clauses_to_add) st.clauses_root; + Stack.clear st.clauses_root; (* remove from env.clauses_temp the now invalid caluses. *) - Vec.shrink env.clauses_temp n; - assert (Vec.for_all (fun c -> Array.length c.atoms = 1) env.clauses_temp); - assert (Vec.for_all (fun c -> c.atoms.(0).var.v_level <= base_level ()) env.clauses_temp); - cancel_until (base_level ()) + Vec.shrink st.clauses_temp n; + assert (Vec.for_all (fun c -> Array.length c.atoms = 1) st.clauses_temp); + assert (Vec.for_all (fun c -> c.atoms.(0).var.v_level <= base_level st) st.clauses_temp); + cancel_until st (base_level st) ) (* Add local hyps to the current decision level *) - let local l = + let local (st:t) (l:_ list) : unit = let aux lit = - let a = atom lit in + let a = mk_atom st lit in Log.debugf info (fun k-> k "Local assumption: @[%a@]" Atom.debug a); - assert (decision_level () = base_level ()); + assert (decision_level st = base_level st); if not a.is_true then ( let c = Clause.make [a] Local in Log.debugf debug (fun k -> k "Temp clause: @[%a@]" Clause.debug c); - Vec.push env.clauses_temp c; + Vec.push st.clauses_temp c; if a.neg.is_true then ( (* conflict between assumptions: UNSAT *) - report_unsat c; + report_unsat st c; ) else ( (* Grow the heap, because when the lit is backtracked, it will be added to the heap. *) - H.grow_to_at_least env.order (St.nb_elt ()); + H.grow_to_at_least st.order (St.nb_elt st.st); (* make a decision, propagate *) - let level = decision_level() in - enqueue_bool a ~level (Bcp c); + let level = decision_level st in + enqueue_bool st a ~level (Bcp c); ) ) in - assert (base_level () > 0); - match env.unsat_conflict with + assert (base_level st > 0); + match st.unsat_conflict with | None -> Log.debug info "Adding local assumption"; - cancel_until (base_level ()); + cancel_until st (base_level st); List.iter aux l | Some _ -> Log.debug warn "Cannot add local assumption (already unsat)" @@ -1338,22 +1342,23 @@ module Make with Exit -> false - let check () = - Stack.is_empty env.clauses_to_add && - check_stack env.clauses_root && - check_vec env.clauses_hyps && - check_vec env.clauses_learnt && - check_vec env.clauses_temp + let check st : bool = + Stack.is_empty st.clauses_to_add && + check_stack st.clauses_root && + check_vec st.clauses_hyps && + check_vec st.clauses_learnt && + check_vec st.clauses_temp (* Unsafe access to internal data *) - let hyps () = env.clauses_hyps + let hyps env = env.clauses_hyps - let history () = env.clauses_learnt + let history env = env.clauses_learnt - let temp () = env.clauses_temp + let temp env = env.clauses_temp - let trail () = env.trail + let trail env = env.trail end +[@@inline] diff --git a/src/core/Internal.mli b/src/core/Internal.mli index bdbbb29e..e25a675c 100644 --- a/src/core/Internal.mli +++ b/src/core/Internal.mli @@ -14,8 +14,8 @@ Copyright 2014 Simon Cruanes module Make (St : Solver_types.S) - (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - (Dummy: sig end) + (Th : Plugin_intf.S with type term = St.term + and type formula = St.formula and type proof = St.proof) : sig (** Functor to create a solver parametrised by the atomic formulas and a theory. *) @@ -24,55 +24,63 @@ module Make exception Unsat exception UndecidedLit - val solve : unit -> unit + type t + (** Solver *) + + val create : ?st:St.t -> unit -> t + + val st : t -> St.t + (** Underlying state *) + + val solve : t -> unit (** Try and solves the current set of assumptions. @return () if the current set of clauses is satisfiable @raise Unsat if a toplevel conflict is found *) - val assume : ?tag:int -> St.formula list list -> unit + val assume : t -> ?tag:int -> St.formula list list -> unit (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) - val new_lit : St.term -> unit + val new_lit : t -> St.term -> unit (** Add a new litteral (i.e term) to the solver. This term will be decided on at some point during solving, wether it appears in clauses or not. *) - val new_atom : St.formula -> unit + val new_atom : t -> St.formula -> unit (** Add a new atom (i.e propositional formula) to the solver. This formula will be decided on at some point during solving, wether it appears in clauses or not. *) - val push : unit -> unit + val push : t -> unit (** Create a decision level for local assumptions. @raise Unsat if a conflict is detected in the current state. *) - val pop : unit -> unit + val pop : t -> unit (** Pop a decision level for local assumptions. *) - val local : St.formula list -> unit + val local : t -> St.formula list -> unit (** Add local assumptions @param assumptions list of additional local assumptions to make, removed after the callback returns a value *) (** {2 Propositional models} *) - val eval : St.formula -> bool + val eval : t -> St.formula -> bool (** Returns the valuation of a formula in the current state of the sat solver. @raise UndecidedLit if the literal is not decided *) - val eval_level : St.formula -> bool * int + val eval_level : t -> St.formula -> bool * int (** Return the current assignement of the literals, as well as its decision level. If the level is 0, then it is necessary for the atom to have this value; otherwise it is due to choices that can potentially be backtracked. @raise UndecidedLit if the literal is not decided *) - val model : unit -> (St.term * St.term) list + val model : t -> (St.term * St.term) list (** Returns the model found if the formula is satisfiable. *) - val check : unit -> bool + val check : t -> bool (** Check the satisfiability of the current model. Only has meaning if the solver finished proof search and has returned [Sat]. *) @@ -80,11 +88,11 @@ module Make module Proof : Res.S with module St = St - val unsat_conflict : unit -> St.clause option + val unsat_conflict : t -> St.clause option (** Returns the unsat clause found at the toplevel, if it exists (i.e if [solve] has raised [Unsat]) *) - val full_slice : unit -> (St.term, St.formula, St.proof) Plugin_intf.slice + val full_slice : t -> (St.term, St.formula, St.proof) Plugin_intf.slice (** View the current state of the trail as a slice. Mainly useful when the solver has reached a SAT conclusion. *) @@ -92,21 +100,21 @@ module Make These functions expose some internal data stored by the solver, as such great care should be taken to ensure not to mess with the values returned. *) - val trail : unit -> St.trail_elt Vec.t + val trail : t -> St.trail_elt Vec.t (** Returns the current trail. *DO NOT MUTATE* *) - val hyps : unit -> St.clause Vec.t + val hyps : t -> St.clause Vec.t (** Returns the vector of assumptions used by the solver. May be slightly different from the clauses assumed because of top-level simplification of clauses. *DO NOT MUTATE* *) - val temp : unit -> St.clause Vec.t + val temp : t -> St.clause Vec.t (** Returns the clauses coreesponding to the local assumptions. All clauses in this vec are assured to be unit clauses. *DO NOT MUTATE* *) - val history : unit -> St.clause Vec.t + val history : t -> St.clause Vec.t (** Returns the history of learnt clauses, with no guarantees on order. *DO NOT MUTATE* *) diff --git a/src/core/Res.ml b/src/core/Res.ml index f164b940..4fbde74c 100644 --- a/src/core/Res.ml +++ b/src/core/Res.ml @@ -178,12 +178,15 @@ module Make(St : Solver_types.S) = struct end | _ -> Log.debugf error - (fun k -> k "While resolving clauses:@[%a@\n%a@]" St.Clause.debug c St.Clause.debug d); + (fun k -> k "While resolving clauses:@[%a@\n%a@]" + St.Clause.debug c St.Clause.debug d); raise (Resolution_error "Clause mismatch") end | _ -> raise (Resolution_error "Bad history") + let[@inline] conclusion (p:proof) : clause = p + let expand conclusion = Log.debugf debug (fun k -> k "Expanding : @[%a@]" St.Clause.debug conclusion); match conclusion.St.cpremise with diff --git a/src/core/Res_intf.ml b/src/core/Res_intf.ml index 5b443bd0..ce5f1479 100644 --- a/src/core/Res_intf.ml +++ b/src/core/Res_intf.ml @@ -95,6 +95,8 @@ module type S = sig val expand : proof -> proof_node (** Return the proof step at the root of a given proof. *) + val conclusion : proof -> clause + val fold : ('a -> proof_node -> 'a) -> 'a -> proof -> 'a (** [fold f acc p], fold [f] over the proof [p] and all its node. It is guaranteed that [f] is executed exactly once on each proof node in the tree, and that the execution of diff --git a/src/core/Solver.ml b/src/core/Solver.ml index 3dab855e..1a633694 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -13,12 +13,11 @@ module Make (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - () = struct module St = St - module S = Internal.Make(St)(Th)(struct end) + module S = Internal.Make(St)(Th) module Proof = S.Proof @@ -26,25 +25,30 @@ module Make type atom = St.formula + type t = S.t + + let create = S.create + (* Result type *) type res = | Sat of (St.term,St.formula) sat_state | Unsat of (St.clause,Proof.proof) unsat_state - let pp_all lvl status = + let pp_all st lvl status = Log.debugf lvl (fun k -> k - "@[%s - Full resume:@,@[Trail:@\n%a@]@,@[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." + "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ + @[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." status - (Vec.print ~sep:"" St.Trail_elt.debug) (S.trail ()) - (Vec.print ~sep:"" St.Clause.debug) (S.temp ()) - (Vec.print ~sep:"" St.Clause.debug) (S.hyps ()) - (Vec.print ~sep:"" St.Clause.debug) (S.history ()) + (Vec.print ~sep:"" St.Trail_elt.debug) (S.trail st) + (Vec.print ~sep:"" St.Clause.debug) (S.temp st) + (Vec.print ~sep:"" St.Clause.debug) (S.hyps st) + (Vec.print ~sep:"" St.Clause.debug) (S.history st) ) - let mk_sat () : (_,_) sat_state = - pp_all 99 "SAT"; - let t = S.trail () in + let mk_sat (st:S.t) : (_,_) sat_state = + pp_all st 99 "SAT"; + let t = S.trail st in let iter f f' = Vec.iter (function | St.Atom a -> f a.St.lit @@ -52,16 +56,16 @@ module Make t in { - eval = S.eval; - eval_level = S.eval_level; + eval = S.eval st; + eval_level = S.eval_level st; iter_trail = iter; - model = S.model; + model = (fun () -> S.model st); } - let mk_unsat () : (_,_) unsat_state = - pp_all 99 "UNSAT"; + let mk_unsat (st:S.t) : (_,_) unsat_state = + pp_all st 99 "UNSAT"; let unsat_conflict () = - match S.unsat_conflict () with + match S.unsat_conflict st with | None -> assert false | Some c -> c in @@ -74,21 +78,21 @@ module Make (* Wrappers around internal functions*) let assume = S.assume - let solve ?(assumptions=[]) () = + let solve (st:t) ?(assumptions=[]) () = try - S.pop (); (* FIXME: what?! *) - S.push (); - S.local assumptions; - S.solve (); - Sat (mk_sat()) + S.pop st; (* FIXME: what?! *) + S.push st; + S.local st assumptions; + S.solve st; + Sat (mk_sat st) with S.Unsat -> - Unsat (mk_unsat()) + Unsat (mk_unsat st) let unsat_core = S.Proof.unsat_core - let true_at_level0 a = + let true_at_level0 st a = try - let b, lev = S.eval_level a in + let b, lev = S.eval_level st a in b && lev = 0 with S.UndecidedLit -> false @@ -97,9 +101,9 @@ module Make let new_lit = S.new_lit let new_atom = S.new_atom - let export () : St.clause export = - let hyps = S.hyps () in - let history = S.history () in - let local = S.temp () in + let export (st:t) : St.clause export = + let hyps = S.hyps st in + let history = S.history st in + let local = S.temp st in {hyps; history; local} end diff --git a/src/core/Solver.mli b/src/core/Solver.mli index 38c2ca9e..c46dc61c 100644 --- a/src/core/Solver.mli +++ b/src/core/Solver.mli @@ -18,8 +18,7 @@ module Make (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - () : - S with module St = St + : S with module St = St (** Functor to make a safe external interface. *) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 39e47ef9..4fafcf18 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -61,6 +61,13 @@ module type S = sig module Proof : Res.S with module St = St (** A module to manipulate proofs. *) + type t + (** Main solver type, containing all state *) + + val create : ?st:St.t -> unit -> t + (** Create new solver *) + (* TODO: add size hint, callbacks, etc. *) + (** {2 Types} *) type atom = St.formula @@ -77,19 +84,19 @@ module type S = sig (** {2 Base operations} *) - val assume : ?tag:int -> atom list list -> unit + val assume : t -> ?tag:int -> atom list list -> unit (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) - val solve : ?assumptions:atom list -> unit -> res + val solve : t -> ?assumptions:atom list -> unit -> res (** Try and solves the current set of assumptions. *) - val new_lit : St.term -> unit + val new_lit : t -> St.term -> unit (** Add a new litteral (i.e term) to the solver. This term will be decided on at some point during solving, wether it appears in clauses or not. *) - val new_atom : atom -> unit + val new_atom : t -> atom -> unit (** Add a new atom (i.e propositional formula) to the solver. This formula will be decided on at some point during solving, wether it appears in clauses or not. *) @@ -97,13 +104,13 @@ module type S = sig val unsat_core : Proof.proof -> St.clause list (** Returns the unsat core of a given proof. *) - val true_at_level0 : atom -> bool + val true_at_level0 : t -> atom -> bool (** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e. it must hold in all models *) val get_tag : St.clause -> int option (** Recover tag from a clause, if any *) - val export : unit -> St.clause export + val export : t -> St.clause export end diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 9f46d043..c22d2df2 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -27,7 +27,7 @@ let () = Var_fields.freeze() (* Solver types for McSat Solving *) (* ************************************************************************ *) -module McMake (E : Expr_intf.S)() = struct +module McMake (E : Expr_intf.S) = struct (* Flag for Mcsat v.s Pure Sat *) let mcsat = true @@ -36,6 +36,8 @@ module McMake (E : Expr_intf.S)() = struct type formula = E.Formula.t type proof = E.proof + let pp_form = E.Formula.dummy + type seen = | Nope | Both @@ -136,16 +138,29 @@ module McMake (E : Expr_intf.S)() = struct module MF = Hashtbl.Make(E.Formula) module MT = Hashtbl.Make(E.Term) + type t = { + t_map: lit MT.t; + f_map: var MF.t; + vars: elt Vec.t; + mutable cpt_mk_var: int; + mutable cpt_mk_clause: int; + } + + type state = t + + let create() : t = { + f_map = MF.create 4096; + t_map = MT.create 4096; + vars = Vec.make 107 (E_var dummy_var); + cpt_mk_var = 0; + cpt_mk_clause = 0; + } + (* TODO: embed a state `t` with these inside *) - let f_map = MF.create 4096 - let t_map = MT.create 4096 - let vars = Vec.make 107 (E_var dummy_var) - let nb_elt () = Vec.size vars - let get_elt i = Vec.get vars i - let iter_elt f = Vec.iter f vars - - let cpt_mk_var = ref 0 + let nb_elt st = Vec.size st.vars + let get_elt st i = Vec.get st.vars i + let iter_elt st f = Vec.iter f st.vars let name_of_clause c = match c.cpremise with | Hyp -> "H" ^ string_of_int c.name @@ -165,20 +180,20 @@ module McMake (E : Expr_intf.S)() = struct let[@inline] weight l = l.l_weight let[@inline] set_weight l w = l.l_weight <- w - let make t = - try MT.find t_map t + let make (st:state) (t:term) : t = + try MT.find st.t_map t with Not_found -> let res = { - lid = !cpt_mk_var; + lid = st.cpt_mk_var; term = t; l_weight = 1.; l_idx= -1; l_level = -1; assigned = None; } in - incr cpt_mk_var; - MT.add t_map t res; - Vec.push vars (E_lit res); + st.cpt_mk_var <- st.cpt_mk_var + 1; + MT.add st.t_map t res; + Vec.push st.vars (E_lit res); res let debug_assign fmt v = @@ -208,42 +223,41 @@ module McMake (E : Expr_intf.S)() = struct let[@inline] weight v = v.v_weight let[@inline] set_weight v w = v.v_weight <- w - let make : formula -> var * Expr_intf.negated = - fun t -> - let lit, negated = E.Formula.norm t in - try - MF.find f_map lit, negated - with Not_found -> - let cpt_fois_2 = !cpt_mk_var lsl 1 in - let rec var = - { vid = !cpt_mk_var; - pa = pa; - na = na; - v_fields = Var_fields.empty; - v_level = -1; - v_idx= -1; - v_weight = 0.; - v_assignable = None; - reason = None; - } - and pa = - { var = var; - lit = lit; - watched = Vec.make 10 dummy_clause; - neg = na; - is_true = false; - aid = cpt_fois_2 (* aid = vid*2 *) } - and na = - { var = var; - lit = E.Formula.neg lit; - watched = Vec.make 10 dummy_clause; - neg = pa; - is_true = false; - aid = cpt_fois_2 + 1 (* aid = vid*2+1 *) } in - MF.add f_map lit var; - incr cpt_mk_var; - Vec.push vars (E_var var); - var, negated + let make (st:state) (t:formula) : var * Expr_intf.negated = + let lit, negated = E.Formula.norm t in + try + MF.find st.f_map lit, negated + with Not_found -> + let cpt_double = st.cpt_mk_var lsl 1 in + let rec var = + { vid = st.cpt_mk_var; + pa = pa; + na = na; + v_fields = Var_fields.empty; + v_level = -1; + v_idx= -1; + v_weight = 0.; + v_assignable = None; + reason = None; + } + and pa = + { var = var; + lit = lit; + watched = Vec.make 10 dummy_clause; + neg = na; + is_true = false; + aid = cpt_double (* aid = vid*2 *) } + and na = + { var = var; + lit = E.Formula.neg lit; + watched = Vec.make 10 dummy_clause; + neg = pa; + is_true = false; + aid = cpt_double + 1 (* aid = vid*2+1 *) } in + MF.add st.f_map lit var; + st.cpt_mk_var <- st.cpt_mk_var + 1; + Vec.push st.vars (E_var var); + var, negated (* Marking helpers *) let[@inline] clear v = @@ -281,8 +295,8 @@ module McMake (E : Expr_intf.S)() = struct then a.var.v_fields <- Var_fields.set v_field_seen_pos true a.var.v_fields else a.var.v_fields <- Var_fields.set v_field_seen_neg true a.var.v_fields - let[@inline] make lit = - let var, negated = Var.make lit in + let[@inline] make st lit = + let var, negated = Var.make st lit in match negated with | Formula_intf.Negated -> var.na | Formula_intf.Same_sign -> var.pa @@ -427,19 +441,29 @@ module McMake (E : Expr_intf.S)() = struct in Format.fprintf fmt "%a0" aux atoms end -end + + module Term = struct + include E.Term + let pp = print + end + + module Formula = struct + include E.Formula + let pp = print + end +end[@@inline] (* Solver types for pure SAT Solving *) (* ************************************************************************ *) -module SatMake (E : Formula_intf.S)() = struct +module SatMake (E : Formula_intf.S) = struct include McMake(struct include E module Term = E module Formula = E - end)(struct end) + end) let mcsat = false -end +end[@@inline] diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli index 62f1baaa..4d12d93d 100644 --- a/src/core/Solver_types.mli +++ b/src/core/Solver_types.mli @@ -30,11 +30,11 @@ module type S = Solver_types_intf.S module Var_fields = Solver_types_intf.Var_fields -module McMake (E : Expr_intf.S)(): +module McMake (E : Expr_intf.S): S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) -module SatMake (E : Formula_intf.S)(): +module SatMake (E : Formula_intf.S): S with type term = E.t and type formula = E.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 08f011cc..b311f4ea 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -32,6 +32,12 @@ module type S = sig val mcsat : bool (** TODO:deprecate. *) + type t + (** State for creating new terms, literals, clauses *) + + (* TODO: add size hint *) + val create: unit -> t + (** {2 Type definitions} *) type term @@ -138,17 +144,19 @@ module type S = sig | E_var of var (**) (** Either a lit of a var *) - val nb_elt : unit -> int - val get_elt : int -> elt - val iter_elt : (elt -> unit) -> unit + val nb_elt : t -> int + val get_elt : t -> int -> elt + val iter_elt : t -> (elt -> unit) -> unit (** Read access to the vector of variables created *) (** {2 Variables, Literals & Clauses } *) + type state = t + module Lit : sig type t = lit val term : t -> term - val make : term -> t + val make : state -> term -> t (** Returns the variable associated with the term *) val level : t -> int @@ -167,7 +175,6 @@ module type S = sig type t = var val dummy : t - val pos : t -> atom val neg : t -> atom @@ -180,7 +187,7 @@ module type S = sig val weight : t -> float val set_weight : t -> float -> unit - val make : formula -> t * Formula_intf.negated + val make : state -> formula -> t * Formula_intf.negated (** Returns the variable linked with the given formula, and whether the atom associated with the formula is [var.pa] or [var.na] *) @@ -207,7 +214,7 @@ module type S = sig val is_true : t -> bool val is_false : t -> bool - val make : formula -> t + val make : state -> formula -> t (** Returns the atom associated with the given formula *) val mark : t -> unit @@ -274,5 +281,19 @@ module type S = sig (** Constructors and destructors *) val debug : t printer end + + module Term : sig + type t = term + val equal : t -> t -> bool + val hash : t -> int + val pp : t printer + end + + module Formula : sig + type t = formula + val equal : t -> t -> bool + val hash : t -> int + val pp : t printer + end end diff --git a/src/main/main.ml b/src/main/main.ml index 4de98a45..0d3d0301 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -37,19 +37,21 @@ module Make let hyps = ref [] - let check_model state = + let st = S.create() + + let check_model sat = let check_clause c = let l = List.map (function a -> Log.debugf 99 - (fun k -> k "Checking value of %a" S.St.Atom.debug (S.St.Atom.make a)); - state.Msat.eval a) c in + (fun k -> k "Checking value of %a" S.St.Formula.pp a); + sat.Msat.eval a) c in List.exists (fun x -> x) l in let l = List.map check_clause !hyps in List.for_all (fun x -> x) l - let prove ~assumptions = - let res = S.solve ~assumptions () in + let prove ~assumptions () = + let res = S.solve st ~assumptions () in let t = Sys.time () in begin match res with | S.Sat state -> @@ -78,26 +80,26 @@ module Make | Dolmen.Statement.Clause l -> let cnf = T.antecedent (Dolmen.Term.or_ l) in hyps := cnf @ !hyps; - S.assume cnf + S.assume st cnf | Dolmen.Statement.Consequent t -> let cnf = T.consequent t in hyps := cnf @ !hyps; - S.assume cnf + S.assume st cnf | Dolmen.Statement.Antecedent t -> let cnf = T.antecedent t in hyps := cnf @ !hyps; - S.assume cnf + S.assume st cnf | Dolmen.Statement.Pack [ { Dolmen.Statement.descr = Dolmen.Statement.Push 1;_ }; { Dolmen.Statement.descr = Dolmen.Statement.Antecedent f;_ }; - { Dolmen.Statement.descr = Dolmen.Statement.Prove []; }; + { Dolmen.Statement.descr = Dolmen.Statement.Prove [];_ }; { Dolmen.Statement.descr = Dolmen.Statement.Pop 1;_ }; ] -> let assumptions = T.assumptions f in - prove ~assumptions + prove ~assumptions () | Dolmen.Statement.Prove l -> - let assumptions = List.map T.assumptions l in - prove ~assumptions + let assumptions = List.map T.assumptions l |> List.flatten in + prove ~assumptions () | Dolmen.Statement.Set_info _ | Dolmen.Statement.Set_logic _ -> () | Dolmen.Statement.Exit -> exit 0 @@ -106,9 +108,9 @@ module Make Dolmen.Statement.print s end -module Sat = Make(Minismt_sat.Make(struct end))(Minismt_sat.Type) -module Smt = Make(Minismt_smt.Make(struct end))(Minismt_smt.Type) -module Mcsat = Make(Minismt_mcsat.Make(struct end))(Minismt_smt.Type) +module Sat = Make(Minismt_sat)(Minismt_sat.Type) +module Smt = Make(Minismt_smt)(Minismt_smt.Type) +module Mcsat = Make(Minismt_mcsat)(Minismt_smt.Type) let solver = ref (module Sat : S) let solver_list = [ diff --git a/src/mcsat/Minismt_mcsat.ml b/src/mcsat/Minismt_mcsat.ml index fcd928e3..a7f01e92 100644 --- a/src/mcsat/Minismt_mcsat.ml +++ b/src/mcsat/Minismt_mcsat.ml @@ -4,10 +4,10 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make() = +include Minismt.Mcsolver.Make(struct type proof = unit module Term = Minismt_smt.Expr.Term module Formula = Minismt_smt.Expr.Atom - end)(Plugin_mcsat)() + end)(Plugin_mcsat) diff --git a/src/mcsat/Minismt_mcsat.mli b/src/mcsat/Minismt_mcsat.mli index 254699a6..1499f361 100644 --- a/src/mcsat/Minismt_mcsat.mli +++ b/src/mcsat/Minismt_mcsat.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -module Make() : Minismt.Solver.S with type St.formula = Minismt_smt.Expr.atom +include Minismt.Solver.S with type St.formula = Minismt_smt.Expr.atom diff --git a/src/sat/Minismt_sat.ml b/src/sat/Minismt_sat.ml index d1e8b933..cbb2b082 100644 --- a/src/sat/Minismt_sat.ml +++ b/src/sat/Minismt_sat.ml @@ -6,6 +6,5 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat module Type = Type_sat -module Make() = - Minismt.Solver.Make(Expr)(Minismt.Solver.DummyTheory(Expr))() +include Minismt.Solver.Make(Expr)(Minismt.Solver.DummyTheory(Expr)) diff --git a/src/sat/Minismt_sat.mli b/src/sat/Minismt_sat.mli index 8c34b184..ffc5034f 100644 --- a/src/sat/Minismt_sat.mli +++ b/src/sat/Minismt_sat.mli @@ -12,6 +12,6 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat module Type = Type_sat -module Make() : Minismt.Solver.S with type St.formula = Expr.t +include Minismt.Solver.S with type St.formula = Expr.t (** A functor that can generate as many solvers as needed. *) diff --git a/src/smt/Minismt_smt.ml b/src/smt/Minismt_smt.ml index 6d25bf9b..473b12de 100644 --- a/src/smt/Minismt_smt.ml +++ b/src/smt/Minismt_smt.ml @@ -9,5 +9,5 @@ module Type = Type_smt module Th = Minismt.Solver.DummyTheory(Expr.Atom) -module Make() = Minismt.Solver.Make(Expr.Atom)(Th)() +include Minismt.Solver.Make(Expr.Atom)(Th) diff --git a/src/smt/Minismt_smt.mli b/src/smt/Minismt_smt.mli index d2e99f39..4850929d 100644 --- a/src/smt/Minismt_smt.mli +++ b/src/smt/Minismt_smt.mli @@ -7,5 +7,5 @@ Copyright 2014 Simon Cruanes module Expr = Expr_smt module Type = Type_smt -module Make() : Minismt.Solver.S with type St.formula = Expr_smt.atom +include Minismt.Solver.S with type St.formula = Expr_smt.atom diff --git a/src/solver/mcsolver.ml b/src/solver/mcsolver.ml index a81bf7ae..f6eb0c68 100644 --- a/src/solver/mcsolver.ml +++ b/src/solver/mcsolver.ml @@ -10,10 +10,6 @@ module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) - () = - Msat.Make - (Make_mcsat_expr(E)()) - (Th) - () + = Msat.Make (Make_mcsat_expr(E)) (Th) diff --git a/src/solver/mcsolver.mli b/src/solver/mcsolver.mli index 7ae92fad..04727d35 100644 --- a/src/solver/mcsolver.mli +++ b/src/solver/mcsolver.mli @@ -16,9 +16,8 @@ module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) - () : - S with type St.term = E.Term.t - and type St.formula = E.Formula.t - and type St.proof = E.proof + : S with type St.term = E.Term.t + and type St.formula = E.Formula.t + and type St.proof = E.proof (** Functor to create a solver parametrised by the atomic formulas and a theory. *) diff --git a/src/solver/solver.ml b/src/solver/solver.ml index 8ad787c5..d06de2e8 100644 --- a/src/solver/solver.ml +++ b/src/solver/solver.ml @@ -76,10 +76,6 @@ end module Make (E : Formula_intf.S) (Th : Theory_intf.S with type formula = E.t and type proof = E.proof) - () = - Msat.Make - (Make_smt_expr(E)(struct end)) - (Plugin(E)(Th)) - () + = Msat.Make (Make_smt_expr(E)) (Plugin(E)(Th)) diff --git a/src/solver/solver.mli b/src/solver/solver.mli index 37840eb7..fef0160e 100644 --- a/src/solver/solver.mli +++ b/src/solver/solver.mli @@ -23,9 +23,8 @@ module DummyTheory(F : Formula_intf.S) : module Make (F : Formula_intf.S) (Th : Theory_intf.S with type formula = F.t and type proof = F.proof) - () : - S with type St.formula = F.t - and type St.proof = F.proof + : S with type St.formula = F.t + and type St.proof = F.proof (** Functor to create a SMT Solver parametrised by the atomic formulas and a theory. *) diff --git a/tests/test_api.ml b/tests/test_api.ml index 69c1e526..838ceed0 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -40,14 +40,18 @@ type solver_res = exception Incorrect_model module type BASIC_SOLVER = sig - val solve : ?assumptions:F.t list -> unit -> solver_res - val assume : ?tag:int -> F.t list list -> unit + type t + val create : unit -> t + val solve : t -> ?assumptions:F.t list -> unit -> solver_res + val assume : t -> ?tag:int -> F.t list list -> unit end let mk_solver (): (module BASIC_SOLVER) = let module S = struct - include Minismt_sat.Make(struct end) - let solve ?assumptions ()= match solve ?assumptions() with + include Minismt_sat + let create() = create() + let solve st ?assumptions () = + match solve st ?assumptions() with | Sat _ -> R_sat | Unsat us -> @@ -86,13 +90,14 @@ module Test = struct let run (t:t): result = (* Interesting stuff happening *) let (module S: BASIC_SOLVER) = mk_solver () in + let st = S.create() in try List.iter (function | A_assume cs -> - S.assume cs + S.assume st cs | A_solve (assumptions, expect) -> - match S.solve ~assumptions (), expect with + match S.solve st ~assumptions (), expect with | R_sat, `Expect_sat | R_unsat, `Expect_unsat -> () | R_unsat, `Expect_sat -> From a34c191ddc63670c9515c63aee8e95386d5054a1 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 17:24:09 +0100 Subject: [PATCH 034/182] add optional size argument to `create` functions --- src/core/Internal.ml | 18 +++++++++++++----- src/core/Internal.mli | 2 +- src/core/Solver_intf.ml | 2 +- src/core/Solver_types.ml | 16 +++++++++++----- src/core/Solver_types_intf.ml | 3 +-- src/main/main.ml | 2 +- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 1c39d704..3ed0aefd 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -134,7 +134,7 @@ module Make } (* Starting environment. *) - let create ?(st=St.create()) () : t = { + let create_ ~st ~size_trail ~size_lvl () : t = { st; unsat_conflict = None; next_decision = None; @@ -149,10 +149,10 @@ module Make th_head = 0; elt_head = 0; - trail = Vec.make 601 (Trail_elt.of_atom Atom.dummy); - elt_levels = Vec.make 601 (-1); - th_levels = Vec.make 100 Plugin.dummy; - user_levels = Vec.make 10 (-1); + trail = Vec.make size_trail (Trail_elt.of_atom Atom.dummy); + elt_levels = Vec.make size_lvl (-1); + th_levels = Vec.make size_lvl Plugin.dummy; + user_levels = Vec.make 0 (-1); order = H.create(); @@ -181,6 +181,14 @@ module Make nb_init_clauses = 0; } + let create ?(size=`Big) ?st () : t = + let st = match st with Some s -> s | None -> St.create ~size () in + let size_trail, size_lvl = match size with + | `Tiny -> 0, 0 + | `Small -> 32, 16 + | `Big -> 600, 50 + in create_ ~st ~size_trail ~size_lvl () + (* Misc functions *) let to_float = float_of_int let to_int = int_of_float diff --git a/src/core/Internal.mli b/src/core/Internal.mli index e25a675c..6082470e 100644 --- a/src/core/Internal.mli +++ b/src/core/Internal.mli @@ -27,7 +27,7 @@ module Make type t (** Solver *) - val create : ?st:St.t -> unit -> t + val create : ?size:[`Tiny|`Small|`Big] -> ?st:St.t -> unit -> t val st : t -> St.t (** Underlying state *) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 4fafcf18..21e2612e 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -64,7 +64,7 @@ module type S = sig type t (** Main solver type, containing all state *) - val create : ?st:St.t -> unit -> t + val create : ?size:[`Tiny|`Small|`Big] -> ?st:St.t -> unit -> t (** Create new solver *) (* TODO: add size hint, callbacks, etc. *) diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index c22d2df2..40f62928 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -148,15 +148,21 @@ module McMake (E : Expr_intf.S) = struct type state = t - let create() : t = { - f_map = MF.create 4096; - t_map = MT.create 4096; - vars = Vec.make 107 (E_var dummy_var); + let create_ size_map size_vars () : t = { + f_map = MF.create size_map; + t_map = MT.create size_map; + vars = Vec.make size_vars (E_var dummy_var); cpt_mk_var = 0; cpt_mk_clause = 0; } - (* TODO: embed a state `t` with these inside *) + let create ?(size=`Big) () : t = + let size_map, size_vars = match size with + | `Tiny -> 8, 0 + | `Small -> 16, 10 + | `Big -> 4096, 128 + in + create_ size_map size_vars () let nb_elt st = Vec.size st.vars let get_elt st i = Vec.get st.vars i diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index b311f4ea..485316df 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -35,8 +35,7 @@ module type S = sig type t (** State for creating new terms, literals, clauses *) - (* TODO: add size hint *) - val create: unit -> t + val create: ?size:[`Tiny|`Small|`Big] -> unit -> t (** {2 Type definitions} *) diff --git a/src/main/main.ml b/src/main/main.ml index 0d3d0301..1d56b09d 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -37,7 +37,7 @@ module Make let hyps = ref [] - let st = S.create() + let st = S.create ~size:`Big () let check_model sat = let check_clause c = From a612a1cda2666be38c83f719bf9e1b42033759b9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 17:29:24 +0100 Subject: [PATCH 035/182] make `Solver.t` more lightweight by removing some useless fields --- src/core/Internal.ml | 85 +++++++++++--------------------------------- 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 3ed0aefd..252662e3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -34,6 +34,18 @@ module Make let info = 5 let debug = 50 + let var_decay : float = 1. /. 0.95 + (* inverse of the activity factor for variables. Default 1/0.999 *) + + let clause_decay : float = 1. /. 0.999 + (* inverse of the activity factor for clauses. Default 1/0.95 *) + + let restart_inc : float = 1.5 + (* multiplicative factor for restart limit, default 1.5 *) + + let learntsize_inc : float = 1.1 + (* multiplicative factor for [learntsize_factor] at each restart, default 1.1 *) + (* Singleton type containing the current state *) type t = { st : St.t; @@ -88,49 +100,21 @@ module Make th_head = elt_head = length trail *) - - mutable simpDB_props : int; - (* remaining number of propagations before the next call to [simplify ()] *) - mutable simpDB_assigns : int; - (* number of toplevel assignments since last call to [simplify ()] *) - - order : H.t; (* Heap ordered by variable activity *) - var_decay : float; - (* inverse of the activity factor for variables. Default 1/0.999 *) - clause_decay : float; - (* inverse of the activity factor for clauses. Default 1/0.95 *) - mutable var_incr : float; (* increment for variables' activity *) + mutable clause_incr : float; (* increment for clauses' activity *) - remove_satisfied : bool; - (* Wether to remove satisfied learnt clauses when simplifying *) - - - restart_inc : float; - (* multiplicative factor for restart limit, default 1.5 *) mutable restart_first : int; (* intial restart limit, default 100 *) - - learntsize_inc : float; - (* multiplicative factor for [learntsize_factor] at each restart, default 1.1 *) mutable learntsize_factor : float; (* initial limit for the number of learnt clauses, 1/3 of initial number of clauses by default *) - - mutable starts : int; - mutable decisions : int; - mutable propagations : int; - mutable conflicts : int; - mutable clauses_literals : int; - mutable learnts_literals : int; - mutable nb_init_clauses : int; } (* Starting environment. *) @@ -158,27 +142,10 @@ module Make var_incr = 1.; clause_incr = 1.; - var_decay = 1. /. 0.95; - clause_decay = 1. /. 0.999; - simpDB_assigns = -1; - simpDB_props = 0; - - remove_satisfied = false; - - restart_inc = 1.5; restart_first = 100; learntsize_factor = 1. /. 3. ; - learntsize_inc = 1.1; - - starts = 0; - decisions = 0; - propagations = 0; - conflicts = 0; - clauses_literals = 0; - learnts_literals = 0; - nb_init_clauses = 0; } let create ?(size=`Big) ?st () : t = @@ -268,11 +235,11 @@ module Make (* Rather than iterate over all the heap when we want to decrease all the variables/literals activity, we instead increase the value by which we increase the activity of 'interesting' var/lits. *) - let var_decay_activity st = - st.var_incr <- st.var_incr *. st.var_decay + let[@inline] var_decay_activity st = + st.var_incr <- st.var_incr *. var_decay - let clause_decay_activity st = - st.clause_incr <- st.clause_incr *. st.clause_decay + let[@inline] clause_decay_activity st = + st.clause_incr <- st.clause_incr *. clause_decay (* increase activity of [v] *) let var_bump_activity_aux st v = @@ -808,7 +775,6 @@ module Make let add_boolean_conflict st (confl:clause): unit = Log.debugf info (fun k -> k "Boolean conflict: %a" Clause.debug confl); st.next_decision <- None; - st.conflicts <- st.conflicts + 1; assert (decision_level st >= base_level st); if decision_level st = base_level st || Array.for_all (fun a -> a.var.v_level <= base_level st) confl.atoms then ( @@ -899,18 +865,14 @@ module Make Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) let flush_clauses st = - if not (Stack.is_empty st.clauses_to_add) then begin + if not (Stack.is_empty st.clauses_to_add) then ( let nbv = St.nb_elt st.st in - let nbc = st.nb_init_clauses + Stack.length st.clauses_to_add in H.grow_to_at_least st.order nbv; - Vec.grow_to_at_least st.clauses_hyps nbc; - Vec.grow_to_at_least st.clauses_learnt nbc; - st.nb_init_clauses <- nbc; while not (Stack.is_empty st.clauses_to_add) do let c = Stack.pop st.clauses_to_add in add_clause st c done - end + ) type watch_res = | Watch_kept @@ -1101,8 +1063,6 @@ module Make end; st.elt_head <- st.elt_head + 1; done; - st.propagations <- st.propagations + !num_props; - st.simpDB_props <- st.simpDB_props - !num_props; match !res with | None -> theory_propagate st | _ -> !res @@ -1121,7 +1081,6 @@ module Make pick_branch_lit st ) else match Plugin.eval atom.lit with | Plugin_intf.Unknown -> - st.decisions <- st.decisions + 1; new_decision_level st; let current_level = decision_level st in enqueue_bool st atom ~level:current_level Decision @@ -1141,7 +1100,6 @@ module Make pick_branch_lit st else ( let value = Plugin.assign l.term in - st.decisions <- st.decisions + 1; new_decision_level st; let current_level = decision_level st in enqueue_assign st l value current_level @@ -1155,7 +1113,6 @@ module Make reaches the given parameters *) let search (st:t) n_of_conflicts n_of_learnts : unit = let conflictC = ref 0 in - st.starts <- st.starts + 1; while true do match propagate st with | Some confl -> (* Conflict *) @@ -1225,8 +1182,8 @@ module Make search st (to_int !n_of_conflicts) (to_int !n_of_learnts) with | Restart -> - n_of_conflicts := !n_of_conflicts *. st.restart_inc; - n_of_learnts := !n_of_learnts *. st.learntsize_inc + n_of_conflicts := !n_of_conflicts *. restart_inc; + n_of_learnts := !n_of_learnts *. learntsize_inc | Sat -> assert (st.elt_head = Vec.size st.trail); begin match Plugin.if_sat (full_slice st) with From b2e646343ae26040ef40b7c3c32d2ea7dd07ef9a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 18:29:56 +0100 Subject: [PATCH 036/182] do not expose `St` in solver, but only expose a restricted API. --- src/backend/Coq.ml | 48 +++++++------ src/backend/Coq.mli | 4 +- src/backend/Dedukti.ml | 12 ++-- src/backend/Dedukti.mli | 2 +- src/backend/Dot.ml | 22 +++--- src/backend/Dot.mli | 6 +- src/core/Internal.ml | 4 +- src/core/Internal.mli | 122 ---------------------------------- src/core/Res.ml | 22 +++--- src/core/Res.mli | 4 +- src/core/Res_intf.ml | 60 +++++++++++------ src/core/Solver.ml | 21 +++++- src/core/Solver.mli | 5 +- src/core/Solver_intf.ml | 60 ++++++++++++----- src/core/Solver_types.ml | 9 +++ src/core/Solver_types_intf.ml | 5 ++ src/main/main.ml | 2 +- src/mcsat/Minismt_mcsat.mli | 2 +- src/sat/Minismt_sat.mli | 2 +- src/smt/Minismt_smt.mli | 2 +- src/solver/mcsolver.mli | 6 +- src/solver/solver.mli | 4 +- 22 files changed, 196 insertions(+), 228 deletions(-) delete mode 100644 src/core/Internal.mli diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index f33f0a2c..adde1c05 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -22,11 +22,12 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct - module Atom = S.St.Atom - module Clause = S.St.Clause - module M = Map.Make(S.St.Atom) + module Atom = S.Atom + module Clause = S.Clause + module M = Map.Make(S.Atom) + module C_tbl = S.Clause.Tbl - let name = S.St.Clause.name + let name = Clause.name let clause_map c = let rec aux acc a i = @@ -36,11 +37,11 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause aux (M.add a.(i) name acc) a (i + 1) end in - aux M.empty c.S.St.atoms 0 + aux M.empty (Clause.atoms c) 0 let clause_iter m format fmt clause = let aux atom = Format.fprintf fmt format (M.find atom m) in - Array.iter aux clause.S.St.atoms + Array.iter aux (Clause.atoms clause) let elim_duplicate fmt goal hyp _ = (** Printing info comment in coq *) @@ -59,27 +60,30 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause if b == a then begin Format.fprintf fmt "@ (fun p =>@ %s%a)" (name h2) (fun fmt -> (Array.iter (fun c -> - if c == a.S.St.neg then + if Atom.equal c (Atom.neg a) then Format.fprintf fmt "@ (fun np => np p)" else Format.fprintf fmt "@ %s" (M.find c m))) - ) h2.S.St.atoms + ) (Clause.atoms h2) end else Format.fprintf fmt "@ %s" (M.find b m) - )) h1.S.St.atoms + )) (Clause.atoms h1) let resolution fmt goal hyp1 hyp2 atom = let a = Atom.abs atom in let h1, h2 = - if Array.exists (Atom.equal a) hyp1.S.St.atoms then hyp1, hyp2 - else (assert (Array.exists (Atom.equal a) hyp2.S.St.atoms); hyp2, hyp1) + if Array.exists (Atom.equal a) (Clause.atoms hyp1) then hyp1, hyp2 + else ( + assert (Array.exists (Atom.equal a) (Clause.atoms hyp2)); + hyp2, hyp1 + ) in (** Print some debug info *) Format.fprintf fmt "(* Clausal resolution. Goal : %s ; Hyps : %s, %s *)@\n" (name goal) (name h1) (name h2); (** Prove the goal: intro the axioms, then perform resolution *) - if Array.length goal.S.St.atoms = 0 then ( + if Array.length (Clause.atoms goal) = 0 then ( let m = M.empty in Format.fprintf fmt "exact @[(%a)@].@\n" (resolution_aux m a h1 h2) (); false @@ -92,13 +96,13 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause (* Count uses of hypotheses *) let incr_use h c = - let i = try S.H.find h c with Not_found -> 0 in - S.H.add h c (i + 1) + let i = try C_tbl.find h c with Not_found -> 0 in + C_tbl.add h c (i + 1) let decr_use h c = - let i = S.H.find h c - 1 in + let i = C_tbl.find h c - 1 in assert (i >= 0); - let () = S.H.add h c i in + let () = C_tbl.add h c i in i <= 0 let clear fmt c = @@ -136,7 +140,7 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause if resolution fmt clause c1 c2 a then clean t fmt [c1; c2] let count_uses p = - let h = S.H.create 4013 in + let h = C_tbl.create 128 in let aux () node = List.iter (fun p' -> incr_use h S.(conclusion p')) (S.parents node.S.step) in @@ -157,13 +161,13 @@ end module Simple(S : Res.S) - (A : Arg with type hyp = S.St.formula list + (A : Arg with type hyp = S.formula list and type lemma := S.lemma - and type assumption := S.St.formula) = + and type assumption := S.formula) = Make(S)(struct (* Some helpers *) - let lit a = a.S.St.lit + let lit = S.Atom.lit let get_assumption c = match S.to_list c with @@ -171,8 +175,8 @@ module Simple(S : Res.S) | _ -> assert false let get_lemma c = - match c.S.St.cpremise with - | S.St.Lemma p -> p + match S.expand (S.prove c) with + | {S.step=S.Lemma p; _} -> p | _ -> assert false let prove_hyp fmt name c = diff --git a/src/backend/Coq.mli b/src/backend/Coq.mli index 205319b6..ec743ff2 100644 --- a/src/backend/Coq.mli +++ b/src/backend/Coq.mli @@ -40,8 +40,8 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause (** Base functor to output Coq proofs *) -module Simple(S : Res.S)(A : Arg with type hyp = S.St.formula list +module Simple(S : Res.S)(A : Arg with type hyp = S.formula list and type lemma := S.lemma - and type assumption := S.St.formula) : S with type t := S.proof + and type assumption := S.formula) : S with type t := S.proof (** Simple functo to output Coq proofs *) diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml index a4a842d1..1a13cb08 100644 --- a/src/backend/Dedukti.ml +++ b/src/backend/Dedukti.ml @@ -18,22 +18,24 @@ module type Arg = sig val context : Format.formatter -> proof -> unit end -module Make(S : Res.S)(A : Arg with type formula := S.St.formula and type lemma := S.lemma and type proof := S.proof) = struct +module Make(S : Res.S)(A : Arg with type formula := S.formula + and type lemma := S.lemma + and type proof := S.proof) = struct let pp_nl fmt = Format.fprintf fmt "@\n" let fprintf fmt format = Format.kfprintf pp_nl fmt format - let _clause_name c = S.St.(c.name) + let _clause_name = S.Clause.name let _pp_clause fmt c = let rec aux fmt = function | [] -> () | a :: r -> let f, pos = - if S.St.(a.var.pa == a) then - S.St.(a.lit), true + if S.Atom.is_pos a then + S.Atom.lit a, true else - S.St.(a.neg.lit), false + S.Atom.lit (S.Atom.neg a), false in fprintf fmt "%s _b %a ->@ %a" (if pos then "_pos" else "_neg") A.print f aux r diff --git a/src/backend/Dedukti.mli b/src/backend/Dedukti.mli index e24deb9a..826c8031 100644 --- a/src/backend/Dedukti.mli +++ b/src/backend/Dedukti.mli @@ -27,7 +27,7 @@ end module Make : functor(S : Res.S) -> functor(A : Arg - with type formula := S.St.formula + with type formula := S.formula and type lemma := S.lemma and type proof := S.proof) -> S with type t := S.proof diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 2fde8287..067046e7 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -31,8 +31,8 @@ module type Arg = sig end module Default(S : Res.S) = struct - module Atom = S.St.Atom - module Clause = S.St.Clause + module Atom = S.Atom + module Clause = S.Clause let print_atom = Atom.pp @@ -55,8 +55,8 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct - module Atom = S.St.Atom - module Clause = S.St.Clause + module Atom = S.Atom + module Clause = S.Clause let node_id n = Clause.name n.S.conclusion @@ -148,13 +148,13 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom end module Simple(S : Res.S) - (A : Arg with type atom := S.St.formula - and type hyp = S.St.formula list + (A : Arg with type atom := S.formula + and type hyp = S.formula list and type lemma := S.lemma - and type assumption = S.St.formula) = + and type assumption = S.formula) = Make(S)(struct - module Atom = S.St.Atom - module Clause = S.St.Clause + module Atom = S.Atom + module Clause = S.Clause (* Some helpers *) let lit = Atom.lit @@ -165,8 +165,8 @@ module Simple(S : Res.S) | _ -> assert false let get_lemma c = - match Clause.premise c with - | S.St.Lemma p -> p + match S.expand (S.prove c) with + | {S.step=S.Lemma p;_} -> p | _ -> assert false (* Actual functions *) diff --git a/src/backend/Dot.mli b/src/backend/Dot.mli index 84cf048b..a3c9c787 100644 --- a/src/backend/Dot.mli +++ b/src/backend/Dot.mli @@ -61,10 +61,10 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom and type assumption := S.clause) : S with type t := S.proof (** Functor for making a module to export proofs to the DOT format. *) -module Simple(S : Res.S)(A : Arg with type atom := S.St.formula - and type hyp = S.St.formula list +module Simple(S : Res.S)(A : Arg with type atom := S.formula + and type hyp = S.formula list and type lemma := S.lemma - and type assumption = S.St.formula) : S with type t := S.proof + and type assumption = S.formula) : S with type t := S.proof (** Functor for making a module to export proofs to the DOT format. The substitution of the hyp type is non-destructive due to a restriction of destructive substitutions on earlier versions of ocaml. *) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 252662e3..7a8d7c88 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -228,7 +228,7 @@ module Make let l = Lit.make st.st t in insert_var_order st (E_lit l) - let new_atom st p = + let new_atom st (p:formula) : unit = let a = mk_atom st p in insert_var_order st (E_var a.var) @@ -294,7 +294,7 @@ module Make literals (which are the first two lits of the clause) are appropriate. Indeed, it is better to watch true literals, and then unassigned literals. Watching false literals should be a last resort, and come with constraints - (see add_clause). + (see {!add_clause}). *) exception Trivial diff --git a/src/core/Internal.mli b/src/core/Internal.mli deleted file mode 100644 index 6082470e..00000000 --- a/src/core/Internal.mli +++ /dev/null @@ -1,122 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** mSAT core - - This is the core of msat, containing the code doing the actual solving. - This module is based on mini-sat, and as such the solver heavily uses mutation, - which makes using it direclty kinda tricky (some exceptions can be raised - at surprising times, mutating is dangerous for maintaining invariants, etc...). -*) - -module Make - (St : Solver_types.S) - (Th : Plugin_intf.S with type term = St.term - and type formula = St.formula and type proof = St.proof) -: sig - (** Functor to create a solver parametrised by the atomic formulas and a theory. *) - - (** {2 Solving facilities} *) - - exception Unsat - exception UndecidedLit - - type t - (** Solver *) - - val create : ?size:[`Tiny|`Small|`Big] -> ?st:St.t -> unit -> t - - val st : t -> St.t - (** Underlying state *) - - val solve : t -> unit - (** Try and solves the current set of assumptions. - @return () if the current set of clauses is satisfiable - @raise Unsat if a toplevel conflict is found *) - - val assume : t -> ?tag:int -> St.formula list list -> unit - (** Add the list of clauses to the current set of assumptions. - Modifies the sat solver state in place. *) - - val new_lit : t -> St.term -> unit - (** Add a new litteral (i.e term) to the solver. This term will - be decided on at some point during solving, wether it appears - in clauses or not. *) - - val new_atom : t -> St.formula -> unit - (** Add a new atom (i.e propositional formula) to the solver. - This formula will be decided on at some point during solving, - wether it appears in clauses or not. *) - - val push : t -> unit - (** Create a decision level for local assumptions. - @raise Unsat if a conflict is detected in the current state. *) - - val pop : t -> unit - (** Pop a decision level for local assumptions. *) - - val local : t -> St.formula list -> unit - (** Add local assumptions - @param assumptions list of additional local assumptions to make, - removed after the callback returns a value *) - - (** {2 Propositional models} *) - - val eval : t -> St.formula -> bool - (** Returns the valuation of a formula in the current state - of the sat solver. - @raise UndecidedLit if the literal is not decided *) - - val eval_level : t -> St.formula -> bool * int - (** Return the current assignement of the literals, as well as its - decision level. If the level is 0, then it is necessary for - the atom to have this value; otherwise it is due to choices - that can potentially be backtracked. - @raise UndecidedLit if the literal is not decided *) - - val model : t -> (St.term * St.term) list - (** Returns the model found if the formula is satisfiable. *) - - val check : t -> bool - (** Check the satisfiability of the current model. Only has meaning - if the solver finished proof search and has returned [Sat]. *) - - (** {2 Proofs and Models} *) - - module Proof : Res.S with module St = St - - val unsat_conflict : t -> St.clause option - (** Returns the unsat clause found at the toplevel, if it exists (i.e if - [solve] has raised [Unsat]) *) - - val full_slice : t -> (St.term, St.formula, St.proof) Plugin_intf.slice - (** View the current state of the trail as a slice. Mainly useful when the - solver has reached a SAT conclusion. *) - - (** {2 Internal data} - These functions expose some internal data stored by the solver, as such - great care should be taken to ensure not to mess with the values returned. *) - - val trail : t -> St.trail_elt Vec.t - (** Returns the current trail. - *DO NOT MUTATE* *) - - val hyps : t -> St.clause Vec.t - (** Returns the vector of assumptions used by the solver. May be slightly different - from the clauses assumed because of top-level simplification of clauses. - *DO NOT MUTATE* *) - - val temp : t -> St.clause Vec.t - (** Returns the clauses coreesponding to the local assumptions. - All clauses in this vec are assured to be unit clauses. - *DO NOT MUTATE* *) - - val history : t -> St.clause Vec.t - (** Returns the history of learnt clauses, with no guarantees on order. - *DO NOT MUTATE* *) - -end - diff --git a/src/core/Res.ml b/src/core/Res.ml index 4fbde74c..0114dbb7 100644 --- a/src/core/Res.ml +++ b/src/core/Res.ml @@ -5,12 +5,13 @@ Copyright 2014 Simon Cruanes *) module type S = Res_intf.S +module type FULL = Res_intf.FULL module Make(St : Solver_types.S) = struct module St = St - (* Type definitions *) + type formula = St.formula type lemma = St.proof type clause = St.clause type atom = St.atom @@ -27,7 +28,8 @@ module Make(St : Solver_types.S) = struct let equal_atoms a b = St.(a.aid) = St.(b.aid) let compare_atoms a b = Pervasives.compare St.(a.aid) St.(b.aid) - let print_clause = St.Clause.pp + module Clause = St.Clause + module Atom = St.Atom let merge = List.merge compare_atoms @@ -105,7 +107,7 @@ module Make(St : Solver_types.S) = struct in aux (c, d) - let prove conclusion = + let[@inline] prove conclusion = assert St.(conclusion.cpremise <> History []); conclusion @@ -259,13 +261,7 @@ module Make(St : Solver_types.S) = struct List.iter (fun c -> c.St.visited <- false) tmp; res - (* Iter on proofs *) - module H = Hashtbl.Make(struct - type t = clause - let hash cl = - Array.fold_left (fun i a -> Hashtbl.hash St.(a.aid, i)) 0 cl.St.atoms - let equal = (==) - end) + module Tbl = Clause.Tbl type task = | Enter of proof @@ -277,10 +273,10 @@ module Make(St : Solver_types.S) = struct match spop s with | None -> acc | Some (Leaving c) -> - H.add h c true; + Tbl.add h c true; fold_aux s h f (f acc (expand c)) | Some (Enter c) -> - if not (H.mem h c) then begin + if not (Tbl.mem h c) then begin Stack.push (Leaving c) s; let node = expand c in begin match node.step with @@ -295,7 +291,7 @@ module Make(St : Solver_types.S) = struct fold_aux s h f acc let fold f acc p = - let h = H.create 42 in + let h = Tbl.create 42 in let s = Stack.create () in Stack.push (Enter p) s; fold_aux s h f acc diff --git a/src/core/Res.mli b/src/core/Res.mli index a2c8bdc9..06b9c647 100644 --- a/src/core/Res.mli +++ b/src/core/Res.mli @@ -12,6 +12,8 @@ Copyright 2014 Simon Cruanes module type S = Res_intf.S (** Interface for a module manipulating resolution proofs. *) -module Make : functor (St : Solver_types.S) -> S with module St = St +module type FULL = Res_intf.FULL + +module Make : functor (St : Solver_types.S) -> FULL with module St = St (** Functor to create a module building proofs from a sat-solver unsat trace. *) diff --git a/src/core/Res_intf.ml b/src/core/Res_intf.ml index ce5f1479..15ea549e 100644 --- a/src/core/Res_intf.ml +++ b/src/core/Res_intf.ml @@ -6,25 +6,26 @@ Copyright 2014 Simon Cruanes (** Interface for proofs *) +type 'a printer = Format.formatter -> 'a -> unit + module type S = sig (** Signature for a module handling proof by resolution from sat solving traces *) - module St : Solver_types.S - (** Module defining atom and clauses *) - (** {3 Type declarations} *) exception Insuficient_hyps (** Raised when a complete resolution derivation cannot be found using the current hypotheses. *) - type atom = St.atom - type lemma = St.proof - type clause = St.clause + type formula + type atom + type lemma + type clause (** Abstract types for atoms, clauses and theory-specific lemmas *) type proof (** Lazy type for proof trees. Proofs are persistent objects, and can be extended to proof nodes using functions defined later. *) + and proof_node = { conclusion : clause; (** The conclusion of the proof *) step : step; (** The reasoning step used to prove the conclusion *) @@ -45,7 +46,6 @@ module type S = sig of the two given proofs. The atom on which to perform the resolution is also given. *) (** The type of reasoning steps allowed in a proof. *) - (** {3 Resolution helpers} *) val to_list : clause -> atom list @@ -73,7 +73,6 @@ module type S = sig val prove_atom : atom -> proof option (** Given an atom [a], returns a proof of the clause [\[a\]] if [a] is true at level 0 *) - (** {3 Proof Nodes} *) val parents : step -> proof list @@ -103,25 +102,46 @@ module type S = sig [f] on a proof node happens after the execution on the parents of the nodes. *) val unsat_core : proof -> clause list - (** Returns the unsat_core of the given proof, i.e the lists of conclusions of all leafs of the proof. - More efficient than using the [fold] function since it has access to the internal representation of proofs *) - + (** Returns the unsat_core of the given proof, i.e the lists of conclusions + of all leafs of the proof. + More efficient than using the [fold] function since it has + access to the internal representation of proofs *) (** {3 Misc} *) val check : proof -> unit (** Check the contents of a proof. Mainly for internal use *) - val print_clause : Format.formatter -> clause -> unit - (** A nice looking printer for clauses, which sort the atoms before printing. *) + module Clause : sig + type t = clause + val name : t -> string + val atoms : t -> atom array + val pp : t printer + (** A nice looking printer for clauses, which sort the atoms before printing. *) + module Tbl : Hashtbl.S with type key = t + end - (** {3 Unsafe} *) - - module H : Hashtbl.S with type key = clause - (** Hashtable over proofs. Uses the details of the internal representation - to achieve the best performances, however hashtables from this module - become invalid when solving is restarted, so they should only be live - during inspection of a single proof. *) + module Atom : sig + type t = atom + val is_pos : t -> bool + val neg : t -> t + val abs : t -> t + val compare : t -> t -> int + val equal : t -> t -> bool + val lit : t -> formula + val pp : t printer + end + module Tbl : Hashtbl.S with type key = proof +end + +module type FULL = sig + module St : Solver_types.S + (** Module defining atom and clauses *) + + include S with type atom = St.atom + and type lemma = St.proof + and type clause = St.clause + and type formula = St.formula end diff --git a/src/core/Solver.ml b/src/core/Solver.ml index 1a633694..cc64cfaf 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -23,11 +23,15 @@ module Make exception UndecidedLit = S.UndecidedLit + type formula = St.formula + type term = St.term type atom = St.formula + type clause = St.clause type t = S.t + type solver = t - let create = S.create + let[@inline] create ?size () = S.create ?size () (* Result type *) type res = @@ -78,6 +82,8 @@ module Make (* Wrappers around internal functions*) let assume = S.assume + let add_clause = S.add_clause + let solve (st:t) ?(assumptions=[]) () = try S.pop st; (* FIXME: what?! *) @@ -106,4 +112,17 @@ module Make let history = S.history st in let local = S.temp st in {hyps; history; local} + + module Clause = struct + include St.Clause + + let atoms c = St.Clause.atoms c |> Array.map (fun a -> a.St.lit) + + let make st ?tag l = + let l = List.map (S.mk_atom st) l in + St.Clause.make ?tag l St.Hyp + end + + module Formula = St.Formula + module Term = St.Term end diff --git a/src/core/Solver.mli b/src/core/Solver.mli index c46dc61c..3272a62b 100644 --- a/src/core/Solver.mli +++ b/src/core/Solver.mli @@ -18,7 +18,10 @@ module Make (Th : Plugin_intf.S with type term = St.term and type formula = St.formula and type proof = St.proof) - : S with module St = St + : S with type term = St.term + and type formula = St.formula + and type clause = St.clause + and type Proof.lemma = St.proof (** Functor to make a safe external interface. *) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 21e2612e..ae98b8d0 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -45,6 +45,8 @@ type 'clause export = { } (** Export internal state *) +type 'a printer = Format.formatter -> 'a -> unit + (** The external interface implemented by safe solvers, such as the one created by the {!Solver.Make} and {!Mcsolver.Make} functors. *) module type S = sig @@ -52,30 +54,29 @@ module type S = sig These are the internal modules used, you should probably not use them if you're not familiar with the internals of mSAT. *) - (* TODO: replace {!St} with explicit modules (Expr, Var, Lit, Elt,...) - with carefully picked interfaces *) - module St : Solver_types.S - (** WARNING: Very dangerous module that expose the internal representation used - by the solver. *) + type term (** user terms *) - module Proof : Res.S with module St = St + type formula (** user formulas *) + + type clause + + module Proof : Res.S with type clause = clause (** A module to manipulate proofs. *) type t (** Main solver type, containing all state *) - val create : ?size:[`Tiny|`Small|`Big] -> ?st:St.t -> unit -> t + val create : ?size:[`Tiny|`Small|`Big] -> unit -> t (** Create new solver *) - (* TODO: add size hint, callbacks, etc. *) (** {2 Types} *) - type atom = St.formula + type atom = formula (** The type of atoms given by the module argument for formulas *) type res = - | Sat of (St.term,St.formula) sat_state (** Returned when the solver reaches SAT *) - | Unsat of (St.clause,Proof.proof) unsat_state (** Returned when the solver reaches UNSAT *) + | Sat of (term,formula) sat_state (** Returned when the solver reaches SAT *) + | Unsat of (clause,Proof.proof) unsat_state (** Returned when the solver reaches UNSAT *) (** Result type for the solver *) exception UndecidedLit @@ -88,10 +89,13 @@ module type S = sig (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) + val add_clause : t -> clause -> unit + (** Lower level addition of clauses *) + val solve : t -> ?assumptions:atom list -> unit -> res (** Try and solves the current set of assumptions. *) - val new_lit : t -> St.term -> unit + val new_lit : t -> term -> unit (** Add a new litteral (i.e term) to the solver. This term will be decided on at some point during solving, wether it appears in clauses or not. *) @@ -101,16 +105,42 @@ module type S = sig This formula will be decided on at some point during solving, wether it appears in clauses or not. *) - val unsat_core : Proof.proof -> St.clause list + val unsat_core : Proof.proof -> clause list (** Returns the unsat core of a given proof. *) val true_at_level0 : t -> atom -> bool (** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e. it must hold in all models *) - val get_tag : St.clause -> int option + val get_tag : clause -> int option (** Recover tag from a clause, if any *) - val export : t -> St.clause export + val export : t -> clause export + + (** {2 Re-export some functions} *) + + type solver = t + + module Clause : sig + type t = clause + + val atoms : t -> atom array + val tag : t -> int option + val equal : t -> t -> bool + + val make : solver -> ?tag:int -> atom list -> t + + val pp : t printer + end + + module Formula : sig + type t = formula + val pp : t printer + end + + module Term : sig + type t = term + val pp : t printer + end end diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 40f62928..a5663bcb 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -283,6 +283,7 @@ module McMake (E : Expr_intf.S) = struct let[@inline] abs a = a.var.pa let[@inline] lit a = a.lit let[@inline] equal a b = a == b + let[@inline] is_pos a = a == abs a let[@inline] compare a b = Pervasives.compare a.aid b.aid let[@inline] reason a = Var.reason a.var let[@inline] id a = a.aid @@ -408,8 +409,10 @@ module McMake (E : Expr_intf.S) = struct let empty = make [] (History []) let name = name_of_clause + let[@inline] equal c1 c2 = c1==c2 let[@inline] atoms c = c.atoms let[@inline] tag c = c.tag + let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms let[@inline] premise c = c.cpremise let[@inline] set_premise c p = c.cpremise <- p @@ -423,6 +426,12 @@ module McMake (E : Expr_intf.S) = struct let[@inline] activity c = c.activity let[@inline] set_activity c w = c.activity <- w + module Tbl = Hashtbl.Make(struct + type t = clause + let hash = hash + let equal = equal + end) + let pp fmt c = Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 485316df..643bc06d 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -210,6 +210,7 @@ module type S = sig val abs : t -> t (** positive atom *) val neg : t -> t val id : t -> int + val is_pos : t -> bool (* positive atom? *) val is_true : t -> bool val is_false : t -> bool @@ -249,6 +250,8 @@ module type S = sig val dummy : t val name : t -> string + val equal : t -> t -> bool + val hash : t -> int val atoms : t -> Atom.t array val tag : t -> int option val premise : t -> premise @@ -270,6 +273,8 @@ module type S = sig val pp : t printer val pp_dimacs : t printer val debug : t printer + + module Tbl : Hashtbl.S with type key = t end module Trail_elt : sig diff --git a/src/main/main.ml b/src/main/main.ml index 1d56b09d..8197e30b 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -43,7 +43,7 @@ module Make let check_clause c = let l = List.map (function a -> Log.debugf 99 - (fun k -> k "Checking value of %a" S.St.Formula.pp a); + (fun k -> k "Checking value of %a" S.Formula.pp a); sat.Msat.eval a) c in List.exists (fun x -> x) l in diff --git a/src/mcsat/Minismt_mcsat.mli b/src/mcsat/Minismt_mcsat.mli index 1499f361..a6de15ae 100644 --- a/src/mcsat/Minismt_mcsat.mli +++ b/src/mcsat/Minismt_mcsat.mli @@ -4,5 +4,5 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -include Minismt.Solver.S with type St.formula = Minismt_smt.Expr.atom +include Minismt.Solver.S with type formula = Minismt_smt.Expr.atom diff --git a/src/sat/Minismt_sat.mli b/src/sat/Minismt_sat.mli index ffc5034f..9dbd63f1 100644 --- a/src/sat/Minismt_sat.mli +++ b/src/sat/Minismt_sat.mli @@ -12,6 +12,6 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat module Type = Type_sat -include Minismt.Solver.S with type St.formula = Expr.t +include Minismt.Solver.S with type formula = Expr.t (** A functor that can generate as many solvers as needed. *) diff --git a/src/smt/Minismt_smt.mli b/src/smt/Minismt_smt.mli index 4850929d..f81b5277 100644 --- a/src/smt/Minismt_smt.mli +++ b/src/smt/Minismt_smt.mli @@ -7,5 +7,5 @@ Copyright 2014 Simon Cruanes module Expr = Expr_smt module Type = Type_smt -include Minismt.Solver.S with type St.formula = Expr_smt.atom +include Minismt.Solver.S with type formula = Expr_smt.atom diff --git a/src/solver/mcsolver.mli b/src/solver/mcsolver.mli index 04727d35..51f05b8a 100644 --- a/src/solver/mcsolver.mli +++ b/src/solver/mcsolver.mli @@ -16,8 +16,8 @@ module Make (E : Expr_intf.S) (Th : Plugin_intf.S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof) - : S with type St.term = E.Term.t - and type St.formula = E.Formula.t - and type St.proof = E.proof + : S with type term = E.Term.t + and type formula = E.Formula.t + and type Proof.lemma = E.proof (** Functor to create a solver parametrised by the atomic formulas and a theory. *) diff --git a/src/solver/solver.mli b/src/solver/solver.mli index fef0160e..77efd675 100644 --- a/src/solver/solver.mli +++ b/src/solver/solver.mli @@ -23,8 +23,8 @@ module DummyTheory(F : Formula_intf.S) : module Make (F : Formula_intf.S) (Th : Theory_intf.S with type formula = F.t and type proof = F.proof) - : S with type St.formula = F.t - and type St.proof = F.proof + : S with type formula = F.t + and type Proof.lemma = F.proof (** Functor to create a SMT Solver parametrised by the atomic formulas and a theory. *) From 585bf6bd500646357380411c47b0da13e9519f5c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 18:35:27 +0100 Subject: [PATCH 037/182] detail --- src/main/main.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/main.ml b/src/main/main.ml index 8197e30b..008592f2 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -198,10 +198,10 @@ let check () = let main () = (* Administrative duties *) Arg.parse argspec input_file usage; - if !file = "" then begin + if !file = "" then ( Arg.usage argspec usage; exit 2 - end; + ); let al = Gc.create_alarm check in (* Interesting stuff happening *) From 52794564191a82f28a0b8e3bb924764862012c26 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 18:53:26 +0100 Subject: [PATCH 038/182] reset some record accesses, for perf --- src/core/Internal.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 7a8d7c88..6bf75876 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -396,11 +396,11 @@ module Make *) let attach_clause c = - assert (not @@ Clause.attached c); + assert (not c.attached); Log.debugf debug (fun k -> k "Attaching %a" Clause.debug c); Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; - Clause.set_attached c true; + c.attached <- true; () (* Backtracking. @@ -892,7 +892,7 @@ module Make atoms.(1) <- first ) else assert (a.neg == atoms.(1)); let first = atoms.(0) in - if Atom.is_true first + if first.is_true then Watch_kept (* true clause, keep it in watched *) else ( try (* look for another watch lit *) @@ -940,7 +940,7 @@ module Make if i >= Vec.size watched then () else ( let c = Vec.get watched i in - assert (Clause.attached c); + assert c.attached; let j = match propagate_in_clause st a c i with | Watch_kept -> i+1 | Watch_removed -> i (* clause at this index changed *) @@ -999,7 +999,7 @@ module Make let current_slice st : (_,_,_) Plugin_intf.slice = { Plugin_intf.start = st.th_head; - length = (Vec.size st.trail) - st.th_head; + length = Vec.size st.trail - st.th_head; get = slice_get st; push = slice_push st; propagate = slice_propagate st; @@ -1121,7 +1121,7 @@ module Make might 'forget' the initial conflict clause, and only add the analyzed backtrack clause. So in those case, we use add_clause to make sure the initial conflict clause is also added. *) - if Clause.attached confl then + if confl.attached then add_boolean_conflict st confl else add_clause st confl From 87fc9aef26cf049568936d43c8d35d706097eb8c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 19:00:32 +0100 Subject: [PATCH 039/182] reinstate better way of picking watch literals --- src/core/Internal.ml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 6bf75876..570e06d1 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -560,14 +560,8 @@ module Make a.(j) <- tmp; ) - let[@inline] put_high_level_atoms_first (arr:atom array) : unit = - Array.sort - (fun a b -> compare b.var.v_level a.var.v_level) - arr - - (* FIXME (* move atoms assigned at high levels first *) - let[@inline] put_high_level_atoms_first (arr:atom array) : unit = + let put_high_level_atoms_first (arr:atom array) : unit = Array.iteri (fun i a -> if i>0 && Atom.level a > Atom.level arr.(0) then ( @@ -584,7 +578,6 @@ module Make swap_arr arr 1 i; )) arr - *) (* evaluate an atom for MCsat, if it's not assigned by boolean propagation/decision *) From 241e2fa4d75c6884e39ca0896f1349dad8c1826c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 19:12:17 +0100 Subject: [PATCH 040/182] remove useless functions --- src/core/Solver_types.ml | 9 --------- src/core/Solver_types_intf.ml | 16 ---------------- 2 files changed, 25 deletions(-) diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index a5663bcb..8b399a31 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -178,13 +178,8 @@ module McMake (E : Expr_intf.S) = struct type t = lit let[@inline] term l = l.term let[@inline] level l = l.l_level - let[@inline] set_level l lvl = l.l_level <- lvl - let[@inline] assigned l = l.assigned - let[@inline] set_assigned l t = l.assigned <- t - let[@inline] weight l = l.l_weight - let[@inline] set_weight l w = l.l_weight <- w let make (st:state) (t:term) : t = try MT.find st.t_map t @@ -219,15 +214,11 @@ module McMake (E : Expr_intf.S) = struct type t = var let dummy = dummy_var let[@inline] level v = v.v_level - let[@inline] set_level v lvl = v.v_level <- lvl let[@inline] pos v = v.pa let[@inline] neg v = v.na let[@inline] reason v = v.reason - let[@inline] set_reason v r = v.reason <- r let[@inline] assignable v = v.v_assignable - let[@inline] set_assignable v x = v.v_assignable <- x let[@inline] weight v = v.v_weight - let[@inline] set_weight v w = v.v_weight <- w let make (st:state) (t:formula) : var * Expr_intf.negated = let lit, negated = E.Formula.norm t in diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 643bc06d..f073456c 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -159,12 +159,8 @@ module type S = sig (** Returns the variable associated with the term *) val level : t -> int - val set_level : t -> int -> unit - val assigned : t -> term option - val set_assigned : t -> term option -> unit val weight : t -> float - val set_weight : t -> float -> unit val pp : t printer val debug : t printer @@ -178,13 +174,9 @@ module type S = sig val neg : t -> atom val level : t -> int - val set_level : t -> int -> unit val reason : t -> reason option - val set_reason : t -> reason option -> unit val assignable : t -> lit list option - val set_assignable : t -> lit list option -> unit val weight : t -> float - val set_weight : t -> float -> unit val make : state -> formula -> t * Formula_intf.negated (** Returns the variable linked with the given formula, @@ -255,14 +247,6 @@ module type S = sig val atoms : t -> Atom.t array val tag : t -> int option val premise : t -> premise - val set_premise : t -> premise -> unit - - val visited : t -> bool - val set_visited : t -> bool -> unit - val attached : t -> bool - val set_attached : t -> bool -> unit - val activity : t -> float - val set_activity : t -> float -> unit val empty : t (** The empty clause *) From 6762985d182b830255a1f1db887b8e3f1803a9b8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 21:29:43 +0100 Subject: [PATCH 041/182] expose `{push,pop}` in main solver --- src/core/Internal.ml | 5 +++++ src/core/Solver.ml | 37 +++++++++++++++++++++++++++++++------ src/core/Solver_intf.ml | 8 ++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 570e06d1..a613820a 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -115,6 +115,10 @@ module Make mutable learntsize_factor : float; (* initial limit for the number of learnt clauses, 1/3 of initial number of clauses by default *) + + mutable dirty: bool; + (* is there a [pop()] on top of the stack for examining + current model/proof? *) } (* Starting environment. *) @@ -146,6 +150,7 @@ module Make restart_first = 100; learntsize_factor = 1. /. 3. ; + dirty=false; } let create ?(size=`Big) ?st () : t = diff --git a/src/core/Solver.ml b/src/core/Solver.ml index cc64cfaf..a8099c38 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -79,21 +79,41 @@ module Make in { unsat_conflict; get_proof; } - (* Wrappers around internal functions*) - let assume = S.assume + (* clean local state *) + let[@inline] cleanup_ (st:t) : unit = + if st.S.dirty then ( + S.pop st; (* reset *) + st.S.dirty <- false; + ) - let add_clause = S.add_clause + (* Wrappers around internal functions*) + let[@inline] assume st ?tag cls : unit = + cleanup_ st; + S.assume st ?tag cls + + let[@inline] add_clause st c : unit = + cleanup_ st; + S.add_clause st c let solve (st:t) ?(assumptions=[]) () = + cleanup_ st; try - S.pop st; (* FIXME: what?! *) S.push st; + st.S.dirty <- true; (* to call [pop] before any other action *) S.local st assumptions; S.solve st; Sat (mk_sat st) with S.Unsat -> Unsat (mk_unsat st) + let[@inline] push st = + cleanup_ st; + S.push st + + let[@inline] pop st = + cleanup_ st; + S.pop st + let unsat_core = S.Proof.unsat_core let true_at_level0 st a = @@ -104,8 +124,13 @@ module Make let get_tag cl = St.(cl.tag) - let new_lit = S.new_lit - let new_atom = S.new_atom + let[@inline] new_lit st t = + cleanup_ st; + S.new_lit st t + + let[@inline] new_atom st a = + cleanup_ st; + S.new_atom st a let export (st:t) : St.clause export = let hyps = S.hyps st in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index ae98b8d0..7bb371ff 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -115,6 +115,14 @@ module type S = sig val get_tag : clause -> int option (** Recover tag from a clause, if any *) + val push : t -> unit + (** Push a new save point *) + + val pop : t -> unit + (** Return to last save point *) + + val pop : t -> unit + val export : t -> clause export (** {2 Re-export some functions} *) From f5066a2ff39c9bb64e709813ce793be3ba1e42c2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2017 22:10:01 +0100 Subject: [PATCH 042/182] typo --- src/core/Solver_intf.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 7bb371ff..e49ba33f 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -121,8 +121,6 @@ module type S = sig val pop : t -> unit (** Return to last save point *) - val pop : t -> unit - val export : t -> clause export (** {2 Re-export some functions} *) From 83d30486480db00f338df28d010f096fac0b2e3d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 3 Jan 2018 22:01:12 +0100 Subject: [PATCH 043/182] a bit of doc --- src/core/Solver_intf.ml | 31 +++++++++++++++++++++---------- src/core/msat.mld | 4 ++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index e49ba33f..7c56168f 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -64,20 +64,24 @@ module type S = sig (** A module to manipulate proofs. *) type t - (** Main solver type, containing all state *) + (** Main solver type, containing all state for solving. *) val create : ?size:[`Tiny|`Small|`Big] -> unit -> t - (** Create new solver *) + (** Create new solver + @param size the initial size of internal data structures. The bigger, + the faster, but also the more RAM it uses. *) (** {2 Types} *) type atom = formula - (** The type of atoms given by the module argument for formulas *) + (** The type of atoms given by the module argument for formulas. + An atom is a user-defined atomic formula whose truth value is + picked by Msat. *) - type res = - | Sat of (term,formula) sat_state (** Returned when the solver reaches SAT *) - | Unsat of (clause,Proof.proof) unsat_state (** Returned when the solver reaches UNSAT *) (** Result type for the solver *) + type res = + | Sat of (term,formula) sat_state (** Returned when the solver reaches SAT, with a model *) + | Unsat of (clause,Proof.proof) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit (** Exception raised by the evaluating functions when a literal @@ -93,7 +97,10 @@ module type S = sig (** Lower level addition of clauses *) val solve : t -> ?assumptions:atom list -> unit -> res - (** Try and solves the current set of assumptions. *) + (** Try and solves the current set of clauses. + @param assumptions additional atomic assumptions to be temporarily added. + The assumptions are just used for this call to [solve], they are + not saved in the solver's state. *) val new_lit : t -> term -> unit (** Add a new litteral (i.e term) to the solver. This term will @@ -106,7 +113,8 @@ module type S = sig wether it appears in clauses or not. *) val unsat_core : Proof.proof -> clause list - (** Returns the unsat core of a given proof. *) + (** Returns the unsat core of a given proof, ie a subset of all the added + clauses that is sufficient to establish unsatisfiability. *) val true_at_level0 : t -> atom -> bool (** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e. @@ -116,10 +124,13 @@ module type S = sig (** Recover tag from a clause, if any *) val push : t -> unit - (** Push a new save point *) + (** Push a new save point. Clauses added after this call to [push] will + be added as normal, but the corresponding call to [pop] will + remove these clauses. *) val pop : t -> unit - (** Return to last save point *) + (** Return to last save point, discarding clauses added since last + call to [push] *) val export : t -> clause export diff --git a/src/core/msat.mld b/src/core/msat.mld index 622fc9c6..33a6b1c3 100644 --- a/src/core/msat.mld +++ b/src/core/msat.mld @@ -59,8 +59,8 @@ leafs of the tree are either hypotheses, or tautologies (i.e. conflicts returned the theory). {!modules: -Res -Res_intf +Msat__Res +Msat__Res_intf } Backends for exporting proofs to different formats: From 27cbb981e736497b04deea81f9380607e75486f8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 3 Jan 2018 22:07:40 +0100 Subject: [PATCH 044/182] more controled API for Res --- src/backend/Coq.ml | 4 ++-- src/backend/Dedukti.ml | 2 +- src/backend/Dot.ml | 4 ++-- src/core/Res_intf.ml | 21 +++++---------------- src/core/Solver.ml | 1 + src/core/Solver_intf.ml | 1 + src/core/Solver_types.ml | 1 + src/core/Solver_types_intf.ml | 1 + 8 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index adde1c05..bba56ac7 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -170,7 +170,7 @@ module Simple(S : Res.S) let lit = S.Atom.lit let get_assumption c = - match S.to_list c with + match S.Clause.atoms_l c with | [ x ] -> x | _ -> assert false @@ -180,7 +180,7 @@ module Simple(S : Res.S) | _ -> assert false let prove_hyp fmt name c = - A.prove_hyp fmt name (List.map lit (S.to_list c)) + A.prove_hyp fmt name (List.map lit (S.Clause.atoms_l c)) let prove_lemma fmt name c = A.prove_lemma fmt name (get_lemma c) diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml index 1a13cb08..76c9db7f 100644 --- a/src/backend/Dedukti.ml +++ b/src/backend/Dedukti.ml @@ -40,7 +40,7 @@ module Make(S : Res.S)(A : Arg with type formula := S.formula fprintf fmt "%s _b %a ->@ %a" (if pos then "_pos" else "_neg") A.print f aux r in - fprintf fmt "_b : Prop ->@ %a ->@ _proof _b" aux (S.to_list c) + fprintf fmt "_b : Prop ->@ %a ->@ _proof _b" aux (S.Clause.atoms_l c) let context fmt p = fprintf fmt "(; Embedding ;)"; diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 067046e7..d25f1a09 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -160,7 +160,7 @@ module Simple(S : Res.S) let lit = Atom.lit let get_assumption c = - match S.to_list c with + match S.Clause.atoms_l c with | [ x ] -> x | _ -> assert false @@ -174,7 +174,7 @@ module Simple(S : Res.S) A.print_atom fmt (Atom.lit a) let hyp_info c = - A.hyp_info (List.map lit (S.to_list c)) + A.hyp_info (List.map lit (S.Clause.atoms_l c)) let lemma_info c = A.lemma_info (get_lemma c) diff --git a/src/core/Res_intf.ml b/src/core/Res_intf.ml index 15ea549e..2106a3bc 100644 --- a/src/core/Res_intf.ml +++ b/src/core/Res_intf.ml @@ -31,6 +31,8 @@ module type S = sig step : step; (** The reasoning step used to prove the conclusion *) } (** A proof can be expanded into a proof node, which show the first step of the proof. *) + + (** The type of reasoning steps allowed in a proof. *) and step = | Hypothesis (** The conclusion is a user-provided hypothesis *) @@ -44,21 +46,6 @@ module type S = sig | Resolution of proof * proof * atom (** The conclusion can be deduced by performing a resolution between the conclusions of the two given proofs. The atom on which to perform the resolution is also given. *) - (** The type of reasoning steps allowed in a proof. *) - - (** {3 Resolution helpers} *) - - val to_list : clause -> atom list - (** Returns the sorted list of atoms of a clause. *) - - val merge : atom list -> atom list -> atom list - (** Merge two sorted atom list using a suitable comparison function. *) - - val resolve : atom list -> atom list * atom list - (** Performs a "resolution step" on a sorted list of atoms. - [resolve (List.merge l1 l2)] where [l1] and [l2] are sorted atom lists should return the pair - [\[a\], l'], where [l'] is the result of the resolution of [l1] and [l2] over [a]. *) - (** {3 Proof building functions} *) @@ -71,7 +58,7 @@ module type S = sig @raise Insuficient_hyps if it does not succeed. *) val prove_atom : atom -> proof option - (** Given an atom [a], returns a proof of the clause [\[a\]] if [a] is true at level 0 *) + (** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *) (** {3 Proof Nodes} *) @@ -95,6 +82,7 @@ module type S = sig (** Return the proof step at the root of a given proof. *) val conclusion : proof -> clause + (** What is proved at the root of the clause *) val fold : ('a -> proof_node -> 'a) -> 'a -> proof -> 'a (** [fold f acc p], fold [f] over the proof [p] and all its node. It is guaranteed that @@ -116,6 +104,7 @@ module type S = sig type t = clause val name : t -> string val atoms : t -> atom array + val atoms_l : t -> atom list val pp : t printer (** A nice looking printer for clauses, which sort the atoms before printing. *) diff --git a/src/core/Solver.ml b/src/core/Solver.ml index a8099c38..883636f0 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -142,6 +142,7 @@ module Make include St.Clause let atoms c = St.Clause.atoms c |> Array.map (fun a -> a.St.lit) + let atoms_l c = St.Clause.atoms_l c |> List.map (fun a -> a.St.lit) let make st ?tag l = let l = List.map (S.mk_atom st) l in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 7c56168f..aebfe2b2 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -142,6 +142,7 @@ module type S = sig type t = clause val atoms : t -> atom array + val atoms_l : t -> atom list val tag : t -> int option val equal : t -> t -> bool diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 8b399a31..5e2686ab 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -402,6 +402,7 @@ module McMake (E : Expr_intf.S) = struct let name = name_of_clause let[@inline] equal c1 c2 = c1==c2 let[@inline] atoms c = c.atoms + let[@inline] atoms_l c = Array.to_list c.atoms let[@inline] tag c = c.tag let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index f073456c..f5076d30 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -245,6 +245,7 @@ module type S = sig val equal : t -> t -> bool val hash : t -> int val atoms : t -> Atom.t array + val atoms_l : t -> Atom.t list val tag : t -> int option val premise : t -> premise From 6eeb649cdc998b7716922315cbcc1a7df214a965 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 3 Jan 2018 22:08:55 +0100 Subject: [PATCH 045/182] doc --- src/core/msat.mld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/msat.mld b/src/core/msat.mld index 33a6b1c3..50973961 100644 --- a/src/core/msat.mld +++ b/src/core/msat.mld @@ -21,7 +21,7 @@ The following modules allow to easily create a SAT or SMT solver (remark: a SAT simply an SMT solver with an empty theory). {!modules: -Msat_solver +Msat } The following modules allow the creation of a McSat solver (Model Constructing solver): From cba9f9592dca2e20cd4e877d7abd4ce6a059c9e1 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 30 May 2018 20:02:37 -0500 Subject: [PATCH 046/182] fix: update .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d56fd66e..c6315169 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,6 @@ before_install: - ${HOME}/opam init --compiler=${OCAML_VERSION} - eval `${HOME}/opam config env` - export OPAMVERBOSE=1 - - opam init - opam switch ${OCAML_VERSION} - eval `opam config env` - opam install ocamlfind jbuilder From 2fb51d808206348365417e2af5ec76c3343658ef Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 18:01:09 -0600 Subject: [PATCH 047/182] chore: move to dune --- Makefile | 21 +++++++++------------ dune-project | 1 + src/backend/dune | 10 ++++++++++ src/backend/jbuild | 13 ------------- src/core/dune | 9 +++++++++ src/core/jbuild | 11 ----------- src/main/dune | 11 +++++++++++ src/main/jbuild | 13 ------------- src/mcsat/dune | 12 ++++++++++++ src/mcsat/jbuild | 13 ------------- src/sat/dune | 12 ++++++++++++ src/sat/jbuild | 13 ------------- src/smt/dune | 12 ++++++++++++ src/smt/jbuild | 13 ------------- src/solver/dune | 11 +++++++++++ src/solver/jbuild | 12 ------------ src/tseitin/dune | 10 ++++++++++ src/tseitin/jbuild | 12 ------------ 18 files changed, 97 insertions(+), 112 deletions(-) create mode 100644 dune-project create mode 100644 src/backend/dune delete mode 100644 src/backend/jbuild create mode 100644 src/core/dune delete mode 100644 src/core/jbuild create mode 100644 src/main/dune delete mode 100644 src/main/jbuild create mode 100644 src/mcsat/dune delete mode 100644 src/mcsat/jbuild create mode 100644 src/sat/dune delete mode 100644 src/sat/jbuild create mode 100644 src/smt/dune delete mode 100644 src/smt/jbuild create mode 100644 src/solver/dune delete mode 100644 src/solver/jbuild create mode 100644 src/tseitin/dune delete mode 100644 src/tseitin/jbuild diff --git a/Makefile b/Makefile index f789c987..e5d6c0a7 100644 --- a/Makefile +++ b/Makefile @@ -12,17 +12,17 @@ OPTS= -j $(J) LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) -all: build-dev test +dev: build-dev test build: - jbuilder build $(OPTS) @install + @dune build $(OPTS) @install --profile=release build-dev: - jbuilder build $(OPTS) @install --dev + @dune build $(OPTS) @install test: build @echo "run API tests…" - jbuilder runtest + @dune runtest @echo "run benchmarks…" # @/usr/bin/time -f "%e" ./tests/run smt @/usr/bin/time -f "%e" ./tests/run mcsat @@ -34,16 +34,16 @@ disable_log: cd src/core; ln -sf log_dummy.ml log.ml clean: - jbuilder clean + @dune clean install: build-install - jbuilder install + @dune install uninstall: - jbuilder uninstall + @dune uninstall doc: - jbuilder build $(OPTS) @doc + @dune build $(OPTS) @doc reinstall: | uninstall install @@ -60,9 +60,6 @@ reindent: ocp-indent WATCH=all watch: - while find src/ tests/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ - echo "============ at `date` ==========" ; \ - make $(WATCH); \ - done + @dune build @all -w .PHONY: clean doc all bench install uninstall remove reinstall enable_log disable_log bin test diff --git a/dune-project b/dune-project new file mode 100644 index 00000000..7655de07 --- /dev/null +++ b/dune-project @@ -0,0 +1 @@ +(lang dune 1.1) diff --git a/src/backend/dune b/src/backend/dune new file mode 100644 index 00000000..d9a6c6de --- /dev/null +++ b/src/backend/dune @@ -0,0 +1,10 @@ +(library + (name msat_backend) + (public_name msat.backend) + (synopsis "proof backends for msat") + (libraries msat) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + diff --git a/src/backend/jbuild b/src/backend/jbuild deleted file mode 100644 index aa2609ff..00000000 --- a/src/backend/jbuild +++ /dev/null @@ -1,13 +0,0 @@ -; vim:ft=lisp: - -; main binary -(library - ((name msat_backend) - (public_name msat.backend) - (synopsis "proof backends for msat") - (libraries (msat)) - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - diff --git a/src/core/dune b/src/core/dune new file mode 100644 index 00000000..c72f5475 --- /dev/null +++ b/src/core/dune @@ -0,0 +1,9 @@ + +(library + (name msat) + (public_name msat) + (synopsis "core data structures and algorithms for msat") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) diff --git a/src/core/jbuild b/src/core/jbuild deleted file mode 100644 index 22ef52fb..00000000 --- a/src/core/jbuild +++ /dev/null @@ -1,11 +0,0 @@ -; vim:ft=lisp: - -(library - ((name msat) - (public_name msat) - (synopsis "core data structures and algorithms for msat") - (libraries ()) - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) diff --git a/src/main/dune b/src/main/dune new file mode 100644 index 00000000..2c6fd9e9 --- /dev/null +++ b/src/main/dune @@ -0,0 +1,11 @@ + +; main binary +(executable + (name main) + (public_name msat_solver) + (package minismt) + (libraries msat msat.backend minismt.sat minismt.smt minismt.mcsat dolmen) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -color always + -unbox-closures -unbox-closures-factor 20) + ) diff --git a/src/main/jbuild b/src/main/jbuild deleted file mode 100644 index 8de51a2e..00000000 --- a/src/main/jbuild +++ /dev/null @@ -1,13 +0,0 @@ - -; vim:ft=lisp: - -; main binary -(executable - ((name main) - (public_name msat_solver) - (package minismt) - (libraries (msat msat.backend minismt.sat minismt.smt minismt.mcsat dolmen)) - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) - (ocamlopt_flags (:standard -O3 -color always - -unbox-closures -unbox-closures-factor 20)) - )) diff --git a/src/mcsat/dune b/src/mcsat/dune new file mode 100644 index 00000000..b3556847 --- /dev/null +++ b/src/mcsat/dune @@ -0,0 +1,12 @@ + +(library + (name minismt_mcsat) + (public_name minismt.mcsat) + (libraries msat minismt minismt.smt) + (synopsis "mcsat interface") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + + diff --git a/src/mcsat/jbuild b/src/mcsat/jbuild deleted file mode 100644 index 692db5f8..00000000 --- a/src/mcsat/jbuild +++ /dev/null @@ -1,13 +0,0 @@ -; vim:ft=lisp: - -(library - ((name minismt_mcsat) - (public_name minismt.mcsat) - (libraries (msat minismt minismt.smt)) - (synopsis "mcsat interface") - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - - diff --git a/src/sat/dune b/src/sat/dune new file mode 100644 index 00000000..650ba4c3 --- /dev/null +++ b/src/sat/dune @@ -0,0 +1,12 @@ + +(library + (name minismt_sat) + (public_name minismt.sat) + (libraries msat msat.tseitin minismt dolmen) + (synopsis "sat interface") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + + diff --git a/src/sat/jbuild b/src/sat/jbuild deleted file mode 100644 index 82068bfb..00000000 --- a/src/sat/jbuild +++ /dev/null @@ -1,13 +0,0 @@ -; vim:ft=lisp: - -(library - ((name minismt_sat) - (public_name minismt.sat) - (libraries (msat msat.tseitin minismt dolmen)) - (synopsis "sat interface") - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - - diff --git a/src/smt/dune b/src/smt/dune new file mode 100644 index 00000000..c2bd637f --- /dev/null +++ b/src/smt/dune @@ -0,0 +1,12 @@ + +(library + (name minismt_smt) + (public_name minismt.smt) + (libraries msat minismt msat.tseitin dolmen) + (synopsis "smt interface") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + + diff --git a/src/smt/jbuild b/src/smt/jbuild deleted file mode 100644 index 9999f849..00000000 --- a/src/smt/jbuild +++ /dev/null @@ -1,13 +0,0 @@ -; vim:ft=lisp: - -(library - ((name minismt_smt) - (public_name minismt.smt) - (libraries (msat minismt msat.tseitin dolmen)) - (synopsis "smt interface") - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - - diff --git a/src/solver/dune b/src/solver/dune new file mode 100644 index 00000000..31c93320 --- /dev/null +++ b/src/solver/dune @@ -0,0 +1,11 @@ + +(library + (name minismt) + (public_name minismt) + (libraries msat dolmen) + (synopsis "minismt") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + diff --git a/src/solver/jbuild b/src/solver/jbuild deleted file mode 100644 index f3176323..00000000 --- a/src/solver/jbuild +++ /dev/null @@ -1,12 +0,0 @@ -; vim:ft=lisp: - -(library - ((name minismt) - (public_name minismt) - (libraries (msat dolmen)) - (synopsis "minismt") - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - diff --git a/src/tseitin/dune b/src/tseitin/dune new file mode 100644 index 00000000..a9057a53 --- /dev/null +++ b/src/tseitin/dune @@ -0,0 +1,10 @@ + +(library + (name msat_tseitin) + (public_name msat.tseitin) + (synopsis "Tseitin transformation for msat") + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) + diff --git a/src/tseitin/jbuild b/src/tseitin/jbuild deleted file mode 100644 index e70bd0e7..00000000 --- a/src/tseitin/jbuild +++ /dev/null @@ -1,12 +0,0 @@ -; vim:ft=lisp: - -(library - ((name msat_tseitin) - (public_name msat.tseitin) - (synopsis "Tseitin transformation for msat") - (libraries ()) - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) - (ocamlopt_flags (:standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20)) - )) - From 9d729b2136d60c3d579340a62105cac020bcdeb9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 18:05:01 -0600 Subject: [PATCH 048/182] chore: move to opam 2 --- minismt.opam | 22 ++++++++++------------ msat.opam | 25 ++++++++++++------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/minismt.opam b/minismt.opam index a38b471a..0f2f75a4 100644 --- a/minismt.opam +++ b/minismt.opam @@ -1,24 +1,22 @@ -opam-version: "1.2" +opam-version: "2.0" name: "minismt" +synopsis: "Test library for msat" license: "Apache" version: "dev" -author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] +author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: ["jbuilder" "build" "@install" "-p" name] -build-doc: ["jbuilder" "build" "@doc" "-p" name] -install: ["jbuilder" "install" name] -remove: ["jbuilder" "uninstall" name] +build: [ + ["dune" "build" "@install" "-p" name "-j" jobs] + ["dune" "build" "@doc" "-p" name] {with-doc} + ["dune" "runtest" "-p" name] {with-test} +] depends: [ - "ocamlfind" {build} - "jbuilder" {build} + "dune" {build} "dolmen" "msat" ] -available: [ - ocaml-version >= "4.03.0" -] tags: [ "sat" "smt" ] homepage: "https://github.com/Gbury/mSAT" -dev-repo: "https://github.com/Gbury/mSAT.git" +dev-repo: "git+https://github.com/Gbury/mSAT.git" bug-reports: "https://github.com/Gbury/mSAT/issues/" diff --git a/msat.opam b/msat.opam index fb16c28a..b52afa20 100644 --- a/msat.opam +++ b/msat.opam @@ -1,23 +1,22 @@ -opam-version: "1.2" +opam-version: "2.0" name: "msat" +synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" version: "dev" -author: ["Sylvain Conchon" "Alain Mebsout" "Stephane Lecuyer" "Simon Cruanes" "Guillaume Bury"] +author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: ["jbuilder" "build" "@install" "-p" name] -build-doc: ["jbuilder" "build" "@doc" "-p" name] -install: ["jbuilder" "install" name] -remove: ["jbuilder" "uninstall" name] -depends: [ - "ocamlfind" {build} - "jbuilder" {build} - "dolmen" {test & = "dev" } +build: [ + ["dune" "build" "@install" "-p" name "-j" jobs] + ["dune" "build" "@doc" "-p" name] {with-doc} + ["dune" "runtest" "-p" name] {with-test} ] -available: [ - ocaml-version >= "4.03.0" +depends: [ + "ocaml" { >= "4.03" } + "dune" {build} + "dolmen" {with-test & = "dev" } ] tags: [ "sat" "smt" ] homepage: "https://github.com/Gbury/mSAT" -dev-repo: "https://github.com/Gbury/mSAT.git" +dev-repo: "git+https://github.com/Gbury/mSAT.git" bug-reports: "https://github.com/Gbury/mSAT/issues/" From 5846ae7e178a230b6f7f757daf2377ceff6726ec Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 18:18:36 -0600 Subject: [PATCH 049/182] chore: finish moving to dune --- tests/dune | 15 +++++++++++++++ tests/jbuild | 16 ---------------- 2 files changed, 15 insertions(+), 16 deletions(-) create mode 100644 tests/dune delete mode 100644 tests/jbuild diff --git a/tests/dune b/tests/dune new file mode 100644 index 00000000..34c62686 --- /dev/null +++ b/tests/dune @@ -0,0 +1,15 @@ + +(executable + (name test_api) + (libraries msat msat.tseitin msat.backend minismt.sat minismt.smt minismt.mcsat dolmen) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -color always + -unbox-closures -unbox-closures-factor 20) + ) + +(alias + (name runtest) + (deps test_api.exe) + (action (run %{deps}))) + + diff --git a/tests/jbuild b/tests/jbuild deleted file mode 100644 index deab296d..00000000 --- a/tests/jbuild +++ /dev/null @@ -1,16 +0,0 @@ -; vim:ft=lisp: - -(executable - ((name test_api) - (libraries (msat msat.tseitin msat.backend minismt.sat minismt.smt minismt.mcsat dolmen)) - (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)) - (ocamlopt_flags (:standard -O3 -color always - -unbox-closures -unbox-closures-factor 20)) - )) - -(alias - ((name runtest) - (deps (test_api.exe)) - (action (run ${<})))) - - From 05e25063626086ba3bc09aa0ccf73b03169e0d8f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 19:39:12 -0600 Subject: [PATCH 050/182] refactor: remove `minismt` things, make simple `msat.sh` --- Makefile | 5 +- dune-project | 1 + minismt.opam | 22 - msat.exe | 1 - msat.opam | 2 +- msat.sh | 3 + src/core/Msat.ml | 2 + src/core/Plugin_intf.ml | 19 + src/core/Theory_intf.ml | 12 + src/main/Dimacs_lex.mll | 20 + src/main/Dimacs_parse.mly | 41 ++ src/main/dune | 10 +- src/main/main.ml | 104 +--- src/mcsat/Minismt_mcsat.ml | 13 - src/mcsat/Minismt_mcsat.mli | 8 - src/mcsat/README.md | 68 --- src/mcsat/backtrack.ml | 99 ---- src/mcsat/backtrack.mli | 77 --- src/mcsat/dune | 12 - src/mcsat/eclosure.ml | 232 -------- src/mcsat/eclosure.mli | 60 --- src/mcsat/plugin_mcsat.ml | 200 ------- src/sat/{Minismt_sat.ml => Msat_sat.ml} | 4 +- src/sat/{Minismt_sat.mli => Msat_sat.mli} | 3 +- src/sat/dune | 10 +- src/sat/type_sat.ml | 90 ---- src/sat/type_sat.mli | 12 - src/smt/Minismt_smt.ml | 13 - src/smt/Minismt_smt.mli | 11 - src/smt/dune | 12 - src/smt/expr_smt.ml | 525 ------------------ src/smt/expr_smt.mli | 326 ----------- src/smt/type_smt.ml | 628 ---------------------- src/smt/type_smt.mli | 7 - src/smt/unionfind.ml | 90 ---- src/smt/unionfind.mli | 20 - src/solver/dune | 11 - src/solver/mcsolver.ml | 15 - src/solver/mcsolver.mli | 23 - src/solver/solver.ml | 81 --- src/solver/solver.mli | 30 -- src/solver/type.ml | 28 - tests/dune | 4 +- tests/run | 6 +- tests/test_api.ml | 7 +- 45 files changed, 137 insertions(+), 2830 deletions(-) delete mode 100644 minismt.opam delete mode 120000 msat.exe create mode 100755 msat.sh create mode 100644 src/main/Dimacs_lex.mll create mode 100644 src/main/Dimacs_parse.mly delete mode 100644 src/mcsat/Minismt_mcsat.ml delete mode 100644 src/mcsat/Minismt_mcsat.mli delete mode 100644 src/mcsat/README.md delete mode 100644 src/mcsat/backtrack.ml delete mode 100644 src/mcsat/backtrack.mli delete mode 100644 src/mcsat/dune delete mode 100644 src/mcsat/eclosure.ml delete mode 100644 src/mcsat/eclosure.mli delete mode 100644 src/mcsat/plugin_mcsat.ml rename src/sat/{Minismt_sat.ml => Msat_sat.ml} (58%) rename src/sat/{Minismt_sat.mli => Msat_sat.mli} (80%) delete mode 100644 src/sat/type_sat.ml delete mode 100644 src/sat/type_sat.mli delete mode 100644 src/smt/Minismt_smt.ml delete mode 100644 src/smt/Minismt_smt.mli delete mode 100644 src/smt/dune delete mode 100644 src/smt/expr_smt.ml delete mode 100644 src/smt/expr_smt.mli delete mode 100644 src/smt/type_smt.ml delete mode 100644 src/smt/type_smt.mli delete mode 100644 src/smt/unionfind.ml delete mode 100644 src/smt/unionfind.mli delete mode 100644 src/solver/dune delete mode 100644 src/solver/mcsolver.ml delete mode 100644 src/solver/mcsolver.mli delete mode 100644 src/solver/solver.ml delete mode 100644 src/solver/solver.mli delete mode 100644 src/solver/type.ml diff --git a/Makefile b/Makefile index e5d6c0a7..3391828d 100644 --- a/Makefile +++ b/Makefile @@ -20,12 +20,11 @@ build: build-dev: @dune build $(OPTS) @install -test: build +test: @echo "run API tests…" @dune runtest @echo "run benchmarks…" - # @/usr/bin/time -f "%e" ./tests/run smt - @/usr/bin/time -f "%e" ./tests/run mcsat + @/usr/bin/time -f "%e" ./tests/run sat enable_log: cd src/core; ln -sf log_real.ml log.ml diff --git a/dune-project b/dune-project index 7655de07..977e7d75 100644 --- a/dune-project +++ b/dune-project @@ -1 +1,2 @@ (lang dune 1.1) +(using menhir 1.0) diff --git a/minismt.opam b/minismt.opam deleted file mode 100644 index 0f2f75a4..00000000 --- a/minismt.opam +++ /dev/null @@ -1,22 +0,0 @@ -opam-version: "2.0" -name: "minismt" -synopsis: "Test library for msat" -license: "Apache" -version: "dev" -author: ["Simon Cruanes" "Guillaume Bury"] -maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: [ - ["dune" "build" "@install" "-p" name "-j" jobs] - ["dune" "build" "@doc" "-p" name] {with-doc} - ["dune" "runtest" "-p" name] {with-test} -] -depends: [ - "dune" {build} - "dolmen" - "msat" -] -tags: [ "sat" "smt" ] -homepage: "https://github.com/Gbury/mSAT" -dev-repo: "git+https://github.com/Gbury/mSAT.git" -bug-reports: "https://github.com/Gbury/mSAT/issues/" - diff --git a/msat.exe b/msat.exe deleted file mode 120000 index cceb9ac3..00000000 --- a/msat.exe +++ /dev/null @@ -1 +0,0 @@ -_build/default/src/main/main.exe \ No newline at end of file diff --git a/msat.opam b/msat.opam index b52afa20..d5c45b44 100644 --- a/msat.opam +++ b/msat.opam @@ -13,7 +13,7 @@ build: [ depends: [ "ocaml" { >= "4.03" } "dune" {build} - "dolmen" {with-test & = "dev" } + "containers" {with-test} ] tags: [ "sat" "smt" ] homepage: "https://github.com/Gbury/mSAT" diff --git a/msat.sh b/msat.sh new file mode 100755 index 00000000..d111c6b0 --- /dev/null +++ b/msat.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec dune exec src/main/main.exe -- $@ diff --git a/src/core/Msat.ml b/src/core/Msat.ml index daa470c2..6b15cf2f 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -33,6 +33,8 @@ module Make_mcsat_expr(E : Expr_intf.S) = Solver_types.McMake(E) module Make = Solver.Make +module Make_dummy = Plugin_intf.Dummy + (**/**) module Vec = Vec module Log = Log diff --git a/src/core/Plugin_intf.ml b/src/core/Plugin_intf.ml index 92d1b2e4..2584eb52 100644 --- a/src/core/Plugin_intf.ml +++ b/src/core/Plugin_intf.ml @@ -119,3 +119,22 @@ module type S = sig end +module Dummy(F: Solver_types.S) + : S with type formula = F.formula + and type term = F.term + and type proof = F.proof += struct + type formula = F.formula + type term = F.term + type proof = F.proof + type level = unit + let dummy = () + let current_level () = () + let assume _ = Sat + let if_sat _ = Sat + let backtrack _ = () + let eval _ = Unknown + let assign t = t + let mcsat = false + let iter_assignable _ _ = () +end diff --git a/src/core/Theory_intf.ml b/src/core/Theory_intf.ml index b465f4c9..f0183342 100644 --- a/src/core/Theory_intf.ml +++ b/src/core/Theory_intf.ml @@ -83,3 +83,15 @@ module type S = sig end +module Dummy(F: Formula_intf.S) + : S with type formula = F.t += struct + type formula = F.t + type proof = unit + type level = unit + let dummy = () + let current_level () = () + let assume _ = Sat + let if_sat _ = Sat + let backtrack _ = () +end diff --git a/src/main/Dimacs_lex.mll b/src/main/Dimacs_lex.mll new file mode 100644 index 00000000..67850c1f --- /dev/null +++ b/src/main/Dimacs_lex.mll @@ -0,0 +1,20 @@ +{ + open Dimacs_parse +} + +let number = ['1' - '9'] ['0' - '9']* + +rule token = parse + | eof { EOF } + | "c" { comment lexbuf } + | [' ' '\t' '\r'] { token lexbuf } + | 'p' { P } + | "cnf" { CNF } + | '\n' { Lexing.new_line lexbuf; token lexbuf } + | '0' { ZERO } + | '-'? number { LIT (int_of_string (Lexing.lexeme lexbuf)) } + | _ { failwith @@ Printf.sprintf "dimacs.lexer: unexpected char `%s`" (Lexing.lexeme lexbuf) } + +and comment = parse + | '\n' { Lexing.new_line lexbuf; token lexbuf } + | [^'\n'] { comment lexbuf } diff --git a/src/main/Dimacs_parse.mly b/src/main/Dimacs_parse.mly new file mode 100644 index 00000000..3ad9341b --- /dev/null +++ b/src/main/Dimacs_parse.mly @@ -0,0 +1,41 @@ +/* Copyright 2005 INRIA */ + +%{ + let lnum pos = pos.Lexing.pos_lnum + let cnum pos = pos.Lexing.pos_cnum - pos.Lexing.pos_bol + let pp_pos out (start,stop) = + Format.fprintf out "(at %d:%d - %d:%d)" + (lnum start) (cnum start) (lnum stop) (cnum stop) +%} + +%token LIT +%token ZERO +%token P CNF EOF + +%start file +%type file + +%% + +/* DIMACS syntax */ + +prelude: + | P CNF LIT LIT { () } + | error + { + failwith @@ Format.asprintf "expected prelude %a" pp_pos ($startpos,$endpos) + } + +clauses: + | l=clause* { l } + | error + { + failwith @@ Format.asprintf "expected list of clauses %a" + pp_pos ($startpos,$endpos) + } + +file: + | prelude l=clauses EOF { l } + +clause: + | l=LIT+ ZERO { l } diff --git a/src/main/dune b/src/main/dune index 2c6fd9e9..7b96abd7 100644 --- a/src/main/dune +++ b/src/main/dune @@ -2,10 +2,12 @@ ; main binary (executable (name main) - (public_name msat_solver) - (package minismt) - (libraries msat msat.backend minismt.sat minismt.smt minismt.mcsat dolmen) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + ;(package msat) + (libraries containers msat msat_sat msat.backend) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) ) + +(menhir (modules Dimacs_parse)) +(ocamllex (modules Dimacs_lex)) diff --git a/src/main/main.ml b/src/main/main.ml index 008592f2..28400e72 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -4,8 +4,6 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -open Msat - exception Incorrect_model exception Out_of_time exception Out_of_space @@ -18,21 +16,9 @@ let p_proof_print = ref false let time_limit = ref 300. let size_limit = ref 1000_000_000. -module P = - Dolmen.Logic.Make(Dolmen.ParseLocation) - (Dolmen.Id)(Dolmen.Term)(Dolmen.Statement) - -module type S = sig - val do_task : Dolmen.Statement.t -> unit -end - -module Make - (S : Msat.S) - (T : Minismt.Type.S with type atom := S.atom) - : sig - val do_task : Dolmen.Statement.t -> unit - end = struct +module S = Msat_sat +module Process = struct module D = Msat_backend.Dot.Make(S.Proof)(Msat_backend.Dot.Default(S.Proof)) let hyps = ref [] @@ -73,65 +59,25 @@ module Make Format.printf "Unsat (%f/%f)@." t t' end - let do_task s = - match s.Dolmen.Statement.descr with - | Dolmen.Statement.Def (id, t) -> T.def id t - | Dolmen.Statement.Decl (id, t) -> T.decl id t - | Dolmen.Statement.Clause l -> - let cnf = T.antecedent (Dolmen.Term.or_ l) in - hyps := cnf @ !hyps; - S.assume st cnf - | Dolmen.Statement.Consequent t -> - let cnf = T.consequent t in - hyps := cnf @ !hyps; - S.assume st cnf - | Dolmen.Statement.Antecedent t -> - let cnf = T.antecedent t in - hyps := cnf @ !hyps; - S.assume st cnf - | Dolmen.Statement.Pack [ - { Dolmen.Statement.descr = Dolmen.Statement.Push 1;_ }; - { Dolmen.Statement.descr = Dolmen.Statement.Antecedent f;_ }; - { Dolmen.Statement.descr = Dolmen.Statement.Prove [];_ }; - { Dolmen.Statement.descr = Dolmen.Statement.Pop 1;_ }; - ] -> - let assumptions = T.assumptions f in - prove ~assumptions () - | Dolmen.Statement.Prove l -> - let assumptions = List.map T.assumptions l |> List.flatten in - prove ~assumptions () - | Dolmen.Statement.Set_info _ - | Dolmen.Statement.Set_logic _ -> () - | Dolmen.Statement.Exit -> exit 0 - | _ -> - Format.printf "Command not supported:@\n%a@." - Dolmen.Statement.print s + let conv_c c = List.rev_map S.Expr.make c + + let add_clauses cs = + S.assume st @@ CCList.map conv_c cs end -module Sat = Make(Minismt_sat)(Minismt_sat.Type) -module Smt = Make(Minismt_smt)(Minismt_smt.Type) -module Mcsat = Make(Minismt_mcsat)(Minismt_smt.Type) - -let solver = ref (module Sat : S) -let solver_list = [ - "sat", (module Sat : S); - "smt", (module Smt : S); - "mcsat", (module Mcsat : S); -] +let parse_file f = + let module L = Lexing in + CCIO.with_in f + (fun ic -> + let buf = L.from_channel ic in + buf.L.lex_curr_p <- {buf.L.lex_curr_p with L.pos_fname=f;}; + Dimacs_parse.file Dimacs_lex.token buf) let error_msg opt arg l = Format.fprintf Format.str_formatter "'%s' is not a valid argument for '%s', valid arguments are : %a" arg opt (fun fmt -> List.iter (fun (s, _) -> Format.fprintf fmt "%s, " s)) l; Format.flush_str_formatter () -let set_flag opt arg flag l = - try - flag := List.assoc arg l - with Not_found -> - invalid_arg (error_msg opt arg l) - -let set_solver s = set_flag "Solver" s solver solver_list - (* Arguments parsing *) let int_arg r arg = let l = String.length arg in @@ -174,8 +120,6 @@ let argspec = Arg.align [ " If provided, print the dot proof in the given file"; "-gc", Arg.Unit setup_gc_stat, " Outputs statistics about the GC"; - "-s", Arg.String set_solver, - "{sat,smt,mcsat} Sets the solver to use (default smt)"; "-size", Arg.String (int_arg size_limit), "[kMGT] Sets the size limit for the sat solver"; "-time", Arg.String (int_arg time_limit), @@ -194,7 +138,6 @@ let check () = else if s > !size_limit then raise Out_of_space - let main () = (* Administrative duties *) Arg.parse argspec input_file usage; @@ -205,14 +148,9 @@ let main () = let al = Gc.create_alarm check in (* Interesting stuff happening *) - let lang, input = P.parse_file !file in - let module S = (val !solver : S) in - List.iter S.do_task input; - (* Small hack for dimacs, which do not output a "Prove" statement *) - begin match lang with - | P.Dimacs -> S.do_task @@ Dolmen.Statement.check_sat [] - | _ -> () - end; + let clauses = parse_file !file in + Process.add_clauses clauses; + Process.prove ~assumptions:[] (); Gc.delete_alarm al; () @@ -229,14 +167,4 @@ let () = | Incorrect_model -> Format.printf "Internal error : incorrect *sat* model@."; exit 4 - | Minismt_sat.Type.Typing_error (msg, t) - | Minismt_smt.Type.Typing_error (msg, t) -> - let b = Printexc.get_backtrace () in - let loc = match t.Dolmen.Term.loc with - | Some l -> l | None -> Dolmen.ParseLocation.mk "<>" 0 0 0 0 - in - Format.fprintf Format.std_formatter "While typing:@\n%a@\n%a: typing error\n%s@." - Dolmen.Term.print t Dolmen.ParseLocation.fmt loc msg; - if Printexc.backtrace_status () then - Format.fprintf Format.std_formatter "%s@." b diff --git a/src/mcsat/Minismt_mcsat.ml b/src/mcsat/Minismt_mcsat.ml deleted file mode 100644 index a7f01e92..00000000 --- a/src/mcsat/Minismt_mcsat.ml +++ /dev/null @@ -1,13 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -include - Minismt.Mcsolver.Make(struct - type proof = unit - module Term = Minismt_smt.Expr.Term - module Formula = Minismt_smt.Expr.Atom - end)(Plugin_mcsat) - diff --git a/src/mcsat/Minismt_mcsat.mli b/src/mcsat/Minismt_mcsat.mli deleted file mode 100644 index a6de15ae..00000000 --- a/src/mcsat/Minismt_mcsat.mli +++ /dev/null @@ -1,8 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -include Minismt.Solver.S with type formula = Minismt_smt.Expr.atom - diff --git a/src/mcsat/README.md b/src/mcsat/README.md deleted file mode 100644 index 80ab1a01..00000000 --- a/src/mcsat/README.md +++ /dev/null @@ -1,68 +0,0 @@ - -# Equality in McSat - -## Basics - -McSat theories have different interfaces and requirements than classic SMT theories. -The good point of these additional requirements is that it becomes easier to combine -theories, since the assignments allow theories to exchange information about -the equality of terms. In a context where there are multiple theories, they each have -to handle the following operations: - -- return an assignment value for a given term -- receive a new assignment value for a term (the assignment may, or not, have been - done by another theory) -- receive a new assertion (i.e an atomic formula asserted to be true by the sat solver) - -With assignments, the reason for a theory returning UNSAT now becomes when -some term has no potential assignment value because of past assignments -and assertions, (or in some cases, an assignments decided by a theory A is -incompatible with the possible assignments of the same term according to theory B). - -When returning UNSAT, the theory must, as usual return a conflict clause. -The conflict clause must be a tautology, and such that every atomic proposition -in it must evaluate to false using assignments. - - -## Equality of uninterpreted types - -To handle equality on arbitrary values efficiently, we maintain a simple union-find -of known equalities (*NOT* computing congruence closure, only the reflexive-transitive -closure of the equalities), where each class can be tagged with an optional assignment. - -When receiving a new assertions by the sat, we update the union-find. When the theory is -asked for an assignment value for a term, we lookup its class. If it is tagged, we return -the tagged value. Else, we take an arbitrary representative $x$ of the class and return it. -When a new assignment $t \mapsto v$ is propagated by the sat solver, there are three cases: - -- the class of $t$ if not tagged, we then tag it with $t \mapsto v$ and continue -- the class of $t$ is already tagged with $\_ mapsto v$, we do nothing -- the class of $t$ is tagged with a $t' \mapsto v'$, we raise unsat, - using the explanation of why $t$ and $t'$ are in the same class and the equality - $t' = v'$ - -Additionally, in order to handle disequalities, each class contains the list of classes -it must be distinct from. There are then two possible reasons to raise unsat, when -a disequality $x <> y$ is invalidated by assignemnts or later equalities: - -- when two classes that should be distinct are merged -- when two classes that should be distinct are assigned to the same value - -in both cases, we use the union-find structure to get the explanation of why $x$ and $y$ -must now be equal (since their class have been merged), and use that to create the -conflict clause. - - -## Uninterpreted functions - -The uninterpreted function theory is much simpler, it doesn't return any assignemnt values -(the equality theory does it already), but rather check that the assignemnts so far are -coherent with the semantics of uninterpreted functions. - -So for each function asignment $f(x1,...,xn) \mapsto v$, we wait for all the arguments to -also be assigned to values $x1 \mapsto v1$, etc... $xn \mapsto vn$, and we add the binding -$(f,v1,...,vn) \mapsto (v,x1,...,xn)$ in a map (meaning that in the model $f$ applied to -$v1,...,vn$ is equal to $v$). If a binding $(f,v1,...,vn) \mapsto (v',y1,...,yn)$ already -exists (with $v' <> v$), then we raise UNSAT, with the explanation: -$( x1=y1 /\ ... /\ xn = yn) => f(x1,...,xn) = f(y1,...,yn)$ - diff --git a/src/mcsat/backtrack.ml b/src/mcsat/backtrack.ml deleted file mode 100644 index aff629cc..00000000 --- a/src/mcsat/backtrack.ml +++ /dev/null @@ -1,99 +0,0 @@ - -module Stack = struct - - type op = - (* Stack structure *) - | Nil : op - | Level : op * int -> op - (* Undo operations *) - | Set : 'a ref * 'a * op -> op - | Call1 : ('a -> unit) * 'a * op -> op - | Call2 : ('a -> 'b -> unit) * 'a * 'b * op -> op - | Call3 : ('a -> 'b -> 'c -> unit) * 'a * 'b * 'c * op -> op - | CallUnit : (unit -> unit) * op -> op - - type t = { - mutable stack : op; - mutable last : int; - } - - type level = int - - let dummy_level = -1 - - let create () = { - stack = Nil; - last = dummy_level; - } - - let register_set t ref value = t.stack <- Set(ref, value, t.stack) - let register_undo t f = t.stack <- CallUnit (f, t.stack) - let register1 t f x = t.stack <- Call1 (f, x, t.stack) - let register2 t f x y = t.stack <- Call2 (f, x, y, t.stack) - let register3 t f x y z = t.stack <- Call3 (f, x, y, z, t.stack) - - let curr = ref 0 - - let push t = - let level = !curr in - t.stack <- Level (t.stack, level); - t.last <- level; - incr curr - - let rec level t = - match t.stack with - | Level (_, lvl) -> lvl - | _ -> push t; level t - - let backtrack t lvl = - let rec pop = function - | Nil -> assert false - | Level (op, level) as current -> - if level = lvl then begin - t.stack <- current; - t.last <- level - end else - pop op - | Set (ref, x, op) -> ref := x; pop op - | CallUnit (f, op) -> f (); pop op - | Call1 (f, x, op) -> f x; pop op - | Call2 (f, x, y, op) -> f x y; pop op - | Call3 (f, x, y, z, op) -> f x y z; pop op - in - pop t.stack - - let pop t = backtrack t (t.last) - -end - -module Hashtbl(K : Hashtbl.HashedType) = struct - - module H = Hashtbl.Make(K) - - type key = K.t - type 'a t = { - tbl : 'a H.t; - stack : Stack.t; - } - - let create ?(size=256) stack = {tbl = H.create size; stack; } - - let mem {tbl; _} x = H.mem tbl x - let find {tbl; _} k = H.find tbl k - - let add t k v = - Stack.register2 t.stack H.remove t.tbl k; - H.add t.tbl k v - - let remove t k = - try - let v = find t k in - Stack.register3 t.stack H.add t.tbl k v; - H.remove t.tbl k - with Not_found -> () - - let fold t f acc = H.fold f t.tbl acc - - let iter f t = H.iter f t.tbl -end - diff --git a/src/mcsat/backtrack.mli b/src/mcsat/backtrack.mli deleted file mode 100644 index 6480cf63..00000000 --- a/src/mcsat/backtrack.mli +++ /dev/null @@ -1,77 +0,0 @@ - -(** Provides helpers for backtracking. - This module defines backtracking stacks, i.e stacks of undo actions - to perform when backtracking to a certain point. This allows for - side-effect backtracking, and so to have backtracking automatically - handled by extensions without the need for explicit synchronisation - between the dispatcher and the extensions. -*) - -module Stack : sig - (** A backtracking stack is a stack of undo actions to perform - in order to revert back to a (mutable) state. *) - - type t - (** The type for a stack. *) - - type level - (** The type of backtracking point. *) - - val create : unit -> t - (** Creates an empty stack. *) - - val dummy_level : level - (** A dummy level. *) - - val push : t -> unit - (** Creates a backtracking point at the top of the stack. *) - - val pop : t -> unit - (** Pop all actions in the undo stack until the first backtracking point. *) - - val level : t -> level - (** Insert a named backtracking point at the top of the stack. *) - - val backtrack : t -> level -> unit - (** Backtrack to the given named backtracking point. *) - - val register_undo : t -> (unit -> unit) -> unit - (** Adds a callback at the top of the stack. *) - - val register1 : t -> ('a -> unit) -> 'a -> unit - val register2 : t -> ('a -> 'b -> unit) -> 'a -> 'b -> unit - val register3 : t -> ('a -> 'b -> 'c -> unit) -> 'a -> 'b -> 'c -> unit - (** Register functions to be called on the given arguments at the top of the stack. - Allows to save some space by not creating too much closure as would be the case if - only [unit -> unit] callbacks were stored. *) - - val register_set : t -> 'a ref -> 'a -> unit - (** Registers a ref to be set to the given value upon backtracking. *) - -end - -module Hashtbl : - functor (K : Hashtbl.HashedType) -> - sig - (** Provides wrappers around hastables in order to have - very simple integration with backtraking stacks. - All actions performed on this table register the corresponding - undo operations so that backtracking is automatic. *) - - type key = K.t - (** The type of keys of the Hashtbl. *) - - type 'a t - (** The type of hastable from keys to values of type ['a]. *) - - val create : ?size:int -> Stack.t -> 'a t - (** Creates an empty hashtable, that registers undo operations on the given stack. *) - - val add : 'a t -> key -> 'a -> unit - val mem : 'a t -> key -> bool - val find : 'a t -> key -> 'a - val remove : 'a t -> key -> unit - val iter : (key -> 'a -> unit) -> 'a t -> unit - val fold : 'a t -> (key -> 'a -> 'b -> 'b) -> 'b -> 'b - (** Usual operations on the hashtabl. For more information see the Hashtbl module of the stdlib. *) - end diff --git a/src/mcsat/dune b/src/mcsat/dune deleted file mode 100644 index b3556847..00000000 --- a/src/mcsat/dune +++ /dev/null @@ -1,12 +0,0 @@ - -(library - (name minismt_mcsat) - (public_name minismt.mcsat) - (libraries msat minismt minismt.smt) - (synopsis "mcsat interface") - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) - - diff --git a/src/mcsat/eclosure.ml b/src/mcsat/eclosure.ml deleted file mode 100644 index fb53655d..00000000 --- a/src/mcsat/eclosure.ml +++ /dev/null @@ -1,232 +0,0 @@ - -module type Key = sig - type t - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - val print : Format.formatter -> t -> unit -end - -module type S = sig - type t - type var - - exception Unsat of var * var * var list - - val create : Backtrack.Stack.t -> t - - val find : t -> var -> var - - val add_eq : t -> var -> var -> unit - val add_neq : t -> var -> var -> unit - val add_tag : t -> var -> var -> unit - - val find_tag : t -> var -> var * (var * var) option - -end - -module Make(T : Key) = struct - - module M = Map.Make(T) - module H = Backtrack.Hashtbl(T) - - type var = T.t - - exception Equal of var * var - exception Same_tag of var * var - exception Unsat of var * var * var list - - type repr_info = { - rank : int; - tag : (T.t * T.t) option; - forbidden : (var * var) M.t; - } - - type node = - | Follow of var - | Repr of repr_info - - type t = { - size : int H.t; - expl : var H.t; - repr : node H.t; - } - - let create s = { - size = H.create s; - expl = H.create s; - repr = H.create s; - } - - (* Union-find algorithm with path compression *) - let self_repr = Repr { rank = 0; tag = None; forbidden = M.empty } - - let find_hash m i default = - try H.find m i - with Not_found -> default - - let rec find_aux m i = - match find_hash m i self_repr with - | Repr r -> r, i - | Follow j -> - let r, k = find_aux m j in - H.add m i (Follow k); - r, k - - let get_repr h x = - let r, y = find_aux h.repr x in - y, r - - let tag h x v = - let r, y = find_aux h.repr x in - let new_m = - { r with - tag = match r.tag with - | Some (_, v') when not (T.equal v v') -> raise (Equal (x, y)) - | (Some _) as t -> t - | None -> Some (x, v) } - in - H.add h.repr y (Repr new_m) - - let find h x = fst (get_repr h x) - - let find_tag h x = - let r, y = find_aux h.repr x in - y, r.tag - - let forbid_aux m x = - try - let a, b = M.find x m in - raise (Equal (a, b)) - with Not_found -> () - - let link h x mx y my = - let new_m = { - rank = if mx.rank = my.rank then mx.rank + 1 else mx.rank; - tag = (match mx.tag, my.tag with - | Some (z, t1), Some (w, t2) -> - if not (T.equal t1 t2) then begin - Log.debugf 3 - (fun k -> k "Tag shenanigan : %a (%a) <> %a (%a)" - T.print t1 T.print z T.print t2 T.print w); - raise (Equal (z, w)) - end else Some (z, t1) - | Some t, None | None, Some t -> Some t - | None, None -> None); - forbidden = M.merge (fun _ b1 b2 -> match b1, b2 with - | Some r, _ | None, Some r -> Some r | _ -> assert false) - mx.forbidden my.forbidden;} - in - let aux m z eq = - match H.find m z with - | Repr r -> - let r' = { r with - forbidden = M.add x eq (M.remove y r.forbidden) } - in - H.add m z (Repr r') - | _ -> assert false - in - M.iter (aux h.repr) my.forbidden; - H.add h.repr y (Follow x); - H.add h.repr x (Repr new_m) - - let union h x y = - let rx, mx = get_repr h x in - let ry, my = get_repr h y in - if T.compare rx ry <> 0 then begin - forbid_aux mx.forbidden ry; - forbid_aux my.forbidden rx; - if mx.rank > my.rank then begin - link h rx mx ry my - end else begin - link h ry my rx mx - end - end - - let forbid h x y = - let rx, mx = get_repr h x in - let ry, my = get_repr h y in - if T.compare rx ry = 0 then - raise (Equal (x, y)) - else match mx.tag, my.tag with - | Some (a, v), Some (b, v') when T.compare v v' = 0 -> - raise (Same_tag(a, b)) - | _ -> - H.add h.repr ry (Repr { my with forbidden = M.add rx (x, y) my.forbidden }); - H.add h.repr rx (Repr { mx with forbidden = M.add ry (x, y) mx.forbidden }) - - (* Equivalence closure with explanation output *) - let find_parent v m = find_hash m v v - - let rec root m acc curr = - let parent = find_parent curr m in - if T.compare curr parent = 0 then - curr :: acc - else - root m (curr :: acc) parent - - let rec rev_root m curr = - let next = find_parent curr m in - if T.compare curr next = 0 then - curr - else begin - H.remove m curr; - let res = rev_root m next in - H.add m next curr; - res - end - - let expl t a b = - let rec aux last = function - | x :: r, y :: r' when T.compare x y = 0 -> - aux (Some x) (r, r') - | l, l' -> begin match last with - | Some z -> List.rev_append (z :: l) l' - | None -> List.rev_append l l' - end - in - aux None (root t.expl [] a, root t.expl [] b) - - let add_eq_aux t i j = - if T.compare (find t i) (find t j) = 0 then - () - else begin - let old_root_i = rev_root t.expl i in - let old_root_j = rev_root t.expl j in - let nb_i = find_hash t.size old_root_i 0 in - let nb_j = find_hash t.size old_root_j 0 in - if nb_i < nb_j then begin - H.add t.expl i j; - H.add t.size j (nb_i + nb_j + 1) - end else begin - H.add t.expl j i; - H.add t.size i (nb_i + nb_j + 1) - end - end - - (* Functions wrapped to produce explanation in case - * something went wrong *) - let add_tag t x v = - match tag t x v with - | () -> () - | exception Equal (a, b) -> - raise (Unsat (a, b, expl t a b)) - - let add_eq t i j = - add_eq_aux t i j; - match union t i j with - | () -> () - | exception Equal (a, b) -> - raise (Unsat (a, b, expl t a b)) - - let add_neq t i j = - match forbid t i j with - | () -> () - | exception Equal (a, b) -> - raise (Unsat (a, b, expl t a b)) - | exception Same_tag (_, _) -> - add_eq_aux t i j; - let res = expl t i j in - raise (Unsat (i, j, res)) - -end diff --git a/src/mcsat/eclosure.mli b/src/mcsat/eclosure.mli deleted file mode 100644 index d4d5f32c..00000000 --- a/src/mcsat/eclosure.mli +++ /dev/null @@ -1,60 +0,0 @@ - -(** Equality closure using an union-find structure. - This module implements a equality closure algorithm using an union-find structure. - It supports adding of equality as well as inequalities, and raises exceptions - when trying to build an incoherent closure. - Please note that this does not implement congruence closure, as we do not - look inside the terms we are given. *) - -module type Key = sig - (** The type of keys used by the equality closure algorithm *) - - type t - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - val print : Format.formatter -> t -> unit -end - -module type S = sig - (** Type signature for the equality closure algorithm *) - - type t - (** Mutable state of the equality closure algorithm. *) - - type var - (** The type of expressions on which equality closure is built *) - - exception Unsat of var * var * var list - (** Raise when trying to build an incoherent equality closure, with an explanation - of the incoherence. - [Unsat (a, b, l)] is such that: - - [a <> b] has been previously added to the closure. - - [l] start with [a] and ends with [b] - - for each consecutive terms [p] and [q] in [l], - an equality [p = q] has been added to the closure. - *) - - val create : Backtrack.Stack.t -> t - (** Creates an empty state which uses the given backtrack stack *) - - val find : t -> var -> var - (** Returns the representative of the given expression in the current closure state *) - - val add_eq : t -> var -> var -> unit - val add_neq : t -> var -> var -> unit - (** Add an equality of inequality to the closure. *) - - val add_tag : t -> var -> var -> unit - (** Add a tag to an expression. The algorithm ensures that each equality class - only has one tag. If incoherent tags are added, an exception is raised. *) - - val find_tag : t -> var -> var * (var * var) option - (** Returns the tag associated with the equality class of the given term, if any. - More specifically, [find_tag e] returns a pair [(repr, o)] where [repr] is the representant of the equality - class of [e]. If the class has a tag, then [o = Some (e', t)] such that [e'] has been tagged with [t] previously. *) - -end - -module Make(T : Key) : S with type var = T.t - diff --git a/src/mcsat/plugin_mcsat.ml b/src/mcsat/plugin_mcsat.ml deleted file mode 100644 index d1c588c7..00000000 --- a/src/mcsat/plugin_mcsat.ml +++ /dev/null @@ -1,200 +0,0 @@ - -(* Module initialization *) - -module Expr_smt = Minismt_smt.Expr - -module E = Eclosure.Make(Expr_smt.Term) -module H = Backtrack.Hashtbl(Expr_smt.Term) -module M = Hashtbl.Make(Expr_smt.Term) - -(* Type definitions *) - -type proof = unit -type term = Expr_smt.Term.t -type formula = Expr_smt.Atom.t -type level = Backtrack.Stack.level - -exception Absurd of Expr_smt.Atom.t list - -(* Backtracking *) - -let stack = Backtrack.Stack.create () - -let dummy = Backtrack.Stack.dummy_level - -let current_level () = Backtrack.Stack.level stack - -let backtrack = Backtrack.Stack.backtrack stack - -(* Equality closure *) - -let uf = E.create stack - -let assign t = - match E.find_tag uf t with - | _, None -> t - | _, Some (_, v) -> v - -(* Propositional constants *) - -let true_ = Expr_smt.(Term.of_id (Id.ty "true" Ty.prop)) -let false_ = Expr_smt.(Term.of_id (Id.ty "false" Ty.prop)) - -(* Uninterpreted functions and predicates *) - -let map : Expr_smt.term H.t = H.create stack -let watch = M.create 4096 -let interpretation = H.create stack - -let pop_watches t = - try - let l = M.find watch t in - M.remove watch t; - l - with Not_found -> - [] - -let add_job j x = - let l = try M.find watch x with Not_found -> [] in - M.add watch x (j :: l) - -let update_job x ((t, watchees) as job) = - try - let y = List.find (fun y -> not (H.mem map y)) watchees in - add_job job y - with Not_found -> - add_job job x; - begin match t with - | { Expr_smt.term = Expr_smt.App (f, tys, l);_ } -> - let is_prop = Expr_smt.(Ty.equal t.t_type Ty.prop) in - let t_v = H.find map t in - let l' = List.map (H.find map) l in - let u = Expr_smt.Term.apply f tys l' in - begin try - let t', u_v = H.find interpretation u in - if not (Expr_smt.Term.equal t_v u_v) then begin - match t' with - | { Expr_smt.term = Expr_smt.App (_, _, r); _ } when is_prop -> - let eqs = List.map2 (fun a b -> Expr_smt.Atom.neg (Expr_smt.Atom.eq a b)) l r in - if Expr_smt.(Term.equal u_v true_) then begin - let res = Expr_smt.Atom.pred t :: - Expr_smt.Atom.neg (Expr_smt.Atom.pred t') :: eqs in - raise (Absurd res) - end else begin - let res = Expr_smt.Atom.pred t' :: - Expr_smt.Atom.neg (Expr_smt.Atom.pred t) :: eqs in - raise (Absurd res) - end - | { Expr_smt.term = Expr_smt.App (_, _, r); _ } -> - let eqs = List.map2 (fun a b -> Expr_smt.Atom.neg (Expr_smt.Atom.eq a b)) l r in - let res = Expr_smt.Atom.eq t t' :: eqs in - raise (Absurd res) - | _ -> assert false - end - with Not_found -> - H.add interpretation u (t, t_v); - end - | _ -> assert false - end - -let rec update_watches x = function - | [] -> () - | job :: r -> - begin - try - update_job x job; - with exn -> - List.iter (fun j -> add_job j x) r; - raise exn - end; - update_watches x r - -let add_watch t l = - update_job t (t, l) - -let add_assign t v = - H.add map t v; - update_watches t (pop_watches t) - -(* Assignemnts *) - -let rec iter_aux f = function - | { Expr_smt.term = Expr_smt.Var _; _ } as t -> - Log.debugf 10 (fun k -> k "Adding %a as assignable" Expr_smt.Term.print t); - f t - | { Expr_smt.term = Expr_smt.App (_, _, l); _ } as t -> - if l <> [] then add_watch t (t :: l); - List.iter (iter_aux f) l; - Log.debugf 10 (fun k -> k "Adding %a as assignable" Expr_smt.Term.print t); - f t - -let iter_assignable f = function - | { Expr_smt.atom = Expr_smt.Pred { Expr_smt.term = Expr_smt.Var _;_ }; _ } -> () - | { Expr_smt.atom = Expr_smt.Pred ({ Expr_smt.term = Expr_smt.App (_, _, l);_} as t); _ } -> - if l <> [] then add_watch t (t :: l); - List.iter (iter_aux f) l; - | { Expr_smt.atom = Expr_smt.Equal (a, b);_ } -> - iter_aux f a; iter_aux f b - -let eval = function - | { Expr_smt.atom = Expr_smt.Pred t; _ } -> - begin try - let v = H.find map t in - if Expr_smt.Term.equal v true_ then - Plugin_intf.Valued (true, [t]) - else if Expr_smt.Term.equal v false_ then - Plugin_intf.Valued (false, [t]) - else - Plugin_intf.Unknown - with Not_found -> - Plugin_intf.Unknown - end - | { Expr_smt.atom = Expr_smt.Equal (a, b); sign; _ } -> - begin try - let v_a = H.find map a in - let v_b = H.find map b in - if Expr_smt.Term.equal v_a v_b then - Plugin_intf.Valued(sign, [a; b]) - else - Plugin_intf.Valued(not sign, [a; b]) - with Not_found -> - Plugin_intf.Unknown - end - - -(* Theory propagation *) - -let rec chain_eq = function - | [] | [_] -> [] - | a :: ((b :: _) as l) -> (Expr_smt.Atom.eq a b) :: chain_eq l - -let assume s = - let open Plugin_intf in - try - for i = s.start to s.start + s.length - 1 do - match s.get i with - | Assign (t, v) -> - add_assign t v; - E.add_tag uf t v - | Lit f -> - begin match f with - | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = true;_ } -> - E.add_eq uf u v - | { Expr_smt.atom = Expr_smt.Equal (u, v); sign = false;_ } -> - E.add_neq uf u v - | { Expr_smt.atom = Expr_smt.Pred p; sign;_ } -> - let v = if sign then true_ else false_ in - add_assign p v - end - done; - Plugin_intf.Sat - with - | Absurd l -> - Plugin_intf.Unsat (l, ()) - | E.Unsat (a, b, l) -> - let c = Expr_smt.Atom.eq a b :: List.map Expr_smt.Atom.neg (chain_eq l) in - Plugin_intf.Unsat (c, ()) - -let if_sat _ = - Plugin_intf.Sat - diff --git a/src/sat/Minismt_sat.ml b/src/sat/Msat_sat.ml similarity index 58% rename from src/sat/Minismt_sat.ml rename to src/sat/Msat_sat.ml index cbb2b082..1fc3d1ee 100644 --- a/src/sat/Minismt_sat.ml +++ b/src/sat/Msat_sat.ml @@ -4,7 +4,7 @@ Copyright 2016 Guillaume Bury *) module Expr = Expr_sat -module Type = Type_sat -include Minismt.Solver.Make(Expr)(Minismt.Solver.DummyTheory(Expr)) +module F = Msat.Make_smt_expr(Expr) +include Msat.Make(F)(Msat.Make_dummy(F)) diff --git a/src/sat/Minismt_sat.mli b/src/sat/Msat_sat.mli similarity index 80% rename from src/sat/Minismt_sat.mli rename to src/sat/Msat_sat.mli index 9dbd63f1..c28f5032 100644 --- a/src/sat/Minismt_sat.mli +++ b/src/sat/Msat_sat.mli @@ -10,8 +10,7 @@ Copyright 2016 Guillaume Bury *) module Expr = Expr_sat -module Type = Type_sat -include Minismt.Solver.S with type formula = Expr.t +include Msat.S with type formula = Expr.t (** A functor that can generate as many solvers as needed. *) diff --git a/src/sat/dune b/src/sat/dune index 650ba4c3..c321e17c 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -1,12 +1,10 @@ (library - (name minismt_sat) - (public_name minismt.sat) - (libraries msat msat.tseitin minismt dolmen) - (synopsis "sat interface") + (name msat_sat) + ; private + (libraries msat) (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -bin-annot + (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) ) - diff --git a/src/sat/type_sat.ml b/src/sat/type_sat.ml deleted file mode 100644 index b809cb4f..00000000 --- a/src/sat/type_sat.ml +++ /dev/null @@ -1,90 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(* Log&Module Init *) -(* ************************************************************************ *) - -module Id = Dolmen.Id -module Ast = Dolmen.Term -module H = Hashtbl.Make(Id) -module Formula = Msat_tseitin.Make(Expr_sat) - -(* Exceptions *) -(* ************************************************************************ *) - -exception Typing_error of string * Dolmen.Term.t - -(* Identifiers *) -(* ************************************************************************ *) - -let symbols = H.create 42 - -let find_id id = - try - H.find symbols id - with Not_found -> - let res = Expr_sat.fresh () in - H.add symbols id res; - res - -(* Actual parsing *) -(* ************************************************************************ *) - -[@@@ocaml.warning "-9"] - -let rec parse = function - | { Ast.term = Ast.Builtin Ast.True } -> - Formula.f_true - | { Ast.term = Ast.Builtin Ast.False } -> - Formula.f_false - | { Ast.term = Ast.Symbol id } -> - let s = find_id id in - Formula.make_atom s - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Not }, [p]) } - | { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "not" } }, [p]) } -> - Formula.make_not (parse p) - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.And }, l) } - | { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "and" } }, l) } -> - Formula.make_and (List.map parse l) - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Or }, l) } - | { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "or" } }, l) } -> - Formula.make_or (List.map parse l) - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Imply }, [p; q]) } -> - Formula.make_imply (parse p) (parse q) - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Equiv }, [p; q]) } -> - Formula.make_equiv (parse p) (parse q) - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Xor }, [p; q]) } -> - Formula.make_xor (parse p) (parse q) - | t -> - raise (Typing_error ("Term is not a pure proposition", t)) - -[@@@ocaml.warning "+9"] - -(* Exported functions *) -(* ************************************************************************ *) - -let decl _ t = - raise (Typing_error ("Declarations are not allowed in pure sat", t)) - -let def _ t = - raise (Typing_error ("Definitions are not allowed in pure sat", t)) - -let assumptions t = - let f = parse t in - let cnf = Formula.make_cnf f in - List.map (function - | [ x ] -> x - | _ -> assert false - ) cnf - -let antecedent t = - let f = parse t in - Formula.make_cnf f - -let consequent t = - let f = parse t in - Formula.make_cnf @@ Formula.make_not f - diff --git a/src/sat/type_sat.mli b/src/sat/type_sat.mli deleted file mode 100644 index a088602c..00000000 --- a/src/sat/type_sat.mli +++ /dev/null @@ -1,12 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Typechecking of terms from Dolmen.Term.t - This module provides functions to parse terms from the untyped syntax tree - defined in Dolmen, and generate formulas as defined in the Expr_sat module. *) - -include Minismt.Type.S with type atom := Expr_sat.t - diff --git a/src/smt/Minismt_smt.ml b/src/smt/Minismt_smt.ml deleted file mode 100644 index 473b12de..00000000 --- a/src/smt/Minismt_smt.ml +++ /dev/null @@ -1,13 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module Expr = Expr_smt -module Type = Type_smt - -module Th = Minismt.Solver.DummyTheory(Expr.Atom) - -include Minismt.Solver.Make(Expr.Atom)(Th) - diff --git a/src/smt/Minismt_smt.mli b/src/smt/Minismt_smt.mli deleted file mode 100644 index f81b5277..00000000 --- a/src/smt/Minismt_smt.mli +++ /dev/null @@ -1,11 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module Expr = Expr_smt -module Type = Type_smt - -include Minismt.Solver.S with type formula = Expr_smt.atom - diff --git a/src/smt/dune b/src/smt/dune deleted file mode 100644 index c2bd637f..00000000 --- a/src/smt/dune +++ /dev/null @@ -1,12 +0,0 @@ - -(library - (name minismt_smt) - (public_name minismt.smt) - (libraries msat minismt msat.tseitin dolmen) - (synopsis "smt interface") - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) - - diff --git a/src/smt/expr_smt.ml b/src/smt/expr_smt.ml deleted file mode 100644 index f3b8961f..00000000 --- a/src/smt/expr_smt.ml +++ /dev/null @@ -1,525 +0,0 @@ -(* - Base modules that defines the terms used in the prover. -*) - -(* Type definitions *) -(* ************************************************************************ *) - -(* Private aliases *) -type hash = int -type index = int - -(* Identifiers, parametrized by the kind of the type of the variable *) -type 'ty id = { - id_type : 'ty; - id_name : string; - index : index; (** unique *) -} - -(* Type for first order types *) -type ttype = Type - -(* The type of functions *) -type 'ty function_descr = { - fun_vars : ttype id list; (* prenex forall *) - fun_args : 'ty list; - fun_ret : 'ty; -} - -(* Types *) -type ty_descr = - | TyVar of ttype id (** Bound variables *) - | TyApp of ttype function_descr id * ty list - -and ty = { - ty : ty_descr; - mutable ty_hash : hash; (** lazy hash *) -} - -(* Terms & formulas *) -type term_descr = - | Var of ty id - | App of ty function_descr id * ty list * term list - -and term = { - term : term_descr; - t_type : ty; - mutable t_hash : hash; (* lazy hash *) -} - -type atom_descr = - | Pred of term - | Equal of term * term - -and atom = { - sign : bool; - atom : atom_descr; - mutable f_hash : hash; (* lazy hash *) -} - -(* Utilities *) -(* ************************************************************************ *) - -let rec list_cmp ord l1 l2 = - match l1, l2 with - | [], [] -> 0 - | [], _ -> -1 - | _, [] -> 1 - | x1::l1', x2::l2' -> - let c = ord x1 x2 in - if c = 0 - then list_cmp ord l1' l2' - else c - -(* Exceptions *) -(* ************************************************************************ *) - -exception Type_mismatch of term * ty * ty -exception Bad_arity of ty function_descr id * ty list * term list -exception Bad_ty_arity of ttype function_descr id * ty list - -(* Printing functions *) -(* ************************************************************************ *) - -module Print = struct - let rec list f sep fmt = function - | [] -> () - | [x] -> f fmt x - | x :: ((_ :: _) as r) -> - Format.fprintf fmt "%a%s" f x sep; - list f sep fmt r - - let id fmt v = Format.fprintf fmt "%s" v.id_name - let ttype fmt = function Type -> Format.fprintf fmt "Type" - - let rec ty fmt t = match t.ty with - | TyVar v -> id fmt v - | TyApp (f, []) -> - Format.fprintf fmt "%a" id f - | TyApp (f, l) -> - Format.fprintf fmt "%a(%a)" id f (list ty ", ") l - - let params fmt = function - | [] -> () - | l -> Format.fprintf fmt "∀ %a. " (list id ", ") l - - let signature print fmt f = - match f.fun_args with - | [] -> Format.fprintf fmt "%a%a" params f.fun_vars print f.fun_ret - | l -> Format.fprintf fmt "%a%a -> %a" params f.fun_vars - (list print " -> ") l print f.fun_ret - - let fun_ty = signature ty - let fun_ttype = signature ttype - - let id_type print fmt v = Format.fprintf fmt "%a : %a" id v print v.id_type - - let id_ty = id_type ty - let id_ttype = id_type ttype - let const_ty = id_type fun_ty - let const_ttype = id_type fun_ttype - - let rec term fmt t = match t.term with - | Var v -> id fmt v - | App (f, [], []) -> - Format.fprintf fmt "%a" id f - | App (f, [], args) -> - Format.fprintf fmt "%a(%a)" id f - (list term ", ") args - | App (f, tys, args) -> - Format.fprintf fmt "%a(%a; %a)" id f - (list ty ", ") tys - (list term ", ") args - - let atom_aux fmt f = - match f.atom with - | Equal (a, b) -> - Format.fprintf fmt "%a %s %a" - term a (if f.sign then "=" else "<>") term b - | Pred t -> - Format.fprintf fmt "%s%a" (if f.sign then "" else "¬ ") term t - - let atom fmt f = Format.fprintf fmt "⟦%a⟧" atom_aux f - -end - -(* Substitutions *) -(* ************************************************************************ *) - -module Subst = struct - module Mi = Map.Make(struct - type t = int * int - let compare (a, b) (c, d) = match compare a c with 0 -> compare b d | x -> x - end) - - type ('a, 'b) t = ('a * 'b) Mi.t - - (* Usual functions *) - let empty = Mi.empty - - let is_empty = Mi.is_empty - - let iter f = Mi.iter (fun _ (key, value) -> f key value) - - let fold f = Mi.fold (fun _ (key, value) acc -> f key value acc) - - let bindings s = Mi.fold (fun _ (key, value) acc -> (key, value) :: acc) s [] - - (* Comparisons *) - let equal f = Mi.equal (fun (_, value1) (_, value2) -> f value1 value2) - let compare f = Mi.compare (fun (_, value1) (_, value2) -> f value1 value2) - let hash h s = Mi.fold (fun i (_, value) acc -> Hashtbl.hash (acc, i, h value)) s 1 - - let choose m = snd (Mi.choose m) - - (* Iterators *) - let exists pred s = - try - iter (fun m s -> if pred m s then raise Exit) s; - false - with Exit -> - true - - let for_all pred s = - try - iter (fun m s -> if not (pred m s) then raise Exit) s; - true - with Exit -> - false - - let print print_key print_value fmt map = - let aux _ (key, value) = - Format.fprintf fmt "%a -> %a@ " print_key key print_value value - in - Format.fprintf fmt "@[%a@]" (fun _ -> Mi.iter aux) map - - module type S = sig - type 'a key - val get : 'a key -> ('a key, 'b) t -> 'b - val mem : 'a key -> ('a key, 'b) t -> bool - val bind : 'a key -> 'b -> ('a key, 'b) t -> ('a key, 'b) t - val remove : 'a key -> ('a key, 'b) t -> ('a key, 'b) t - end - - (* Variable substitutions *) - module Id = struct - type 'a key = 'a id - let tok v = (v.index, 0) - let get v s = snd (Mi.find (tok v) s) - let mem v s = Mi.mem (tok v) s - let bind v t s = Mi.add (tok v) (v, t) s - let remove v s = Mi.remove (tok v) s - end - -end - -(* Dummies *) -(* ************************************************************************ *) - -module Dummy = struct - - let id_ttype = - { index = -1; id_name = ""; id_type = Type; } - - let ty = - { ty = TyVar id_ttype; ty_hash = -1; } - - let id = - { index = -2; id_name = ""; id_type = ty; } - - let term = - { term = Var id; t_type = ty; t_hash = -1; } - - let atom = - { atom = Pred term; sign = true; f_hash = -1; } - -end - -(* Variables *) -(* ************************************************************************ *) - -module Id = struct - type 'a t = 'a id - - (* Hash & comparisons *) - let hash v = v.index - - let compare: 'a. 'a id -> 'a id -> int = - fun v1 v2 -> compare v1.index v2.index - - let equal v1 v2 = compare v1 v2 = 0 - - (* Printing functions *) - let print = Print.id - - (* Id count *) - let _count = ref 0 - - (* Constructors *) - let mk_new id_name id_type = - incr _count; - let index = !_count in - { index; id_name; id_type } - - let ttype name = mk_new name Type - let ty name ty = mk_new name ty - - let const name fun_vars fun_args fun_ret = - mk_new name { fun_vars; fun_args; fun_ret; } - - let ty_fun name n = - let rec replicate acc n = - if n <= 0 then acc - else replicate (Type :: acc) (n - 1) - in - const name [] (replicate [] n) Type - - let term_fun = const - - (* Builtin Types *) - let prop = ty_fun "Prop" 0 - let base = ty_fun "$i" 0 - -end - -(* Types *) -(* ************************************************************************ *) - -module Ty = struct - type t = ty - type subst = (ttype id, ty) Subst.t - - (* Hash & Comparisons *) - let rec hash_aux t = match t.ty with - | TyVar v -> Id.hash v - | TyApp (f, args) -> - Hashtbl.hash (Id.hash f, List.map hash args) - - and hash t = - if t.ty_hash = -1 then - t.ty_hash <- hash_aux t; - t.ty_hash - - let discr ty = match ty.ty with - | TyVar _ -> 1 - | TyApp _ -> 2 - - let rec compare u v = - let hu = hash u and hv = hash v in - if hu <> hv then Pervasives.compare hu hv - else match u.ty, v.ty with - | TyVar v1, TyVar v2 -> Id.compare v1 v2 - | TyApp (f1, args1), TyApp (f2, args2) -> - begin match Id.compare f1 f2 with - | 0 -> list_cmp compare args1 args2 - | x -> x - end - | _, _ -> Pervasives.compare (discr u) (discr v) - - let equal u v = - u == v || (hash u = hash v && compare u v = 0) - - (* Printing functions *) - let print = Print.ty - - (* Constructors *) - let mk_ty ty = { ty; ty_hash = -1; } - - let of_id v = mk_ty (TyVar v) - - let apply f args = - assert (f.id_type.fun_vars = []); - if List.length args <> List.length f.id_type.fun_args then - raise (Bad_ty_arity (f, args)) - else - mk_ty (TyApp (f, args)) - - (* Builtin types *) - let prop = apply Id.prop [] - let base = apply Id.base [] - - (* Substitutions *) - let rec subst_aux map t = match t.ty with - | TyVar v -> begin try Subst.Id.get v map with Not_found -> t end - | TyApp (f, args) -> - let new_args = List.map (subst_aux map) args in - if List.for_all2 (==) args new_args then t - else apply f new_args - - let subst map t = if Subst.is_empty map then t else subst_aux map t - - (* Typechecking *) - let instantiate f tys args = - if List.length f.id_type.fun_vars <> List.length tys || - List.length f.id_type.fun_args <> List.length args then - raise (Bad_arity (f, tys, args)) - else - let map = List.fold_left2 (fun acc v ty -> Subst.Id.bind v ty acc) Subst.empty f.id_type.fun_vars tys in - let fun_args = List.map (subst map) f.id_type.fun_args in - List.iter2 (fun t ty -> - if not (equal t.t_type ty) then raise (Type_mismatch (t, t.t_type, ty))) - args fun_args; - subst map f.id_type.fun_ret - -end - -(* Terms *) -(* ************************************************************************ *) - -module Term = struct - type t = term - type subst = (ty id, term) Subst.t - - (* Hash & Comparisons *) - let rec hash_aux t = match t.term with - | Var v -> Id.hash v - | App (f, tys, args) -> - let l = List.map Ty.hash tys in - let l' = List.map hash args in - Hashtbl.hash (Id.hash f, l, l') - - and hash t = - if t.t_hash = -1 then - t.t_hash <- hash_aux t; - t.t_hash - - let discr t = match t.term with - | Var _ -> 1 - | App _ -> 2 - - let rec compare u v = - let hu = hash u and hv = hash v in - if hu <> hv then Pervasives.compare hu hv - else match u.term, v.term with - | Var v1, Var v2 -> Id.compare v1 v2 - | App (f1, tys1, args1), App (f2, tys2, args2) -> - begin match Id.compare f1 f2 with - | 0 -> - begin match list_cmp Ty.compare tys1 tys2 with - | 0 -> list_cmp compare args1 args2 - | x -> x - end - | x -> x - end - | _, _ -> Pervasives.compare (discr u) (discr v) - - let equal u v = - u == v || (hash u = hash v && compare u v = 0) - - (* Printing functions *) - let print = Print.term - - (* Constructors *) - let mk_term term t_type = - { term; t_type; t_hash = -1; } - - let of_id v = - mk_term (Var v) v.id_type - - let apply f ty_args t_args = - mk_term (App (f, ty_args, t_args)) (Ty.instantiate f ty_args t_args) - - (* Substitutions *) - let rec subst_aux ty_map t_map t = match t.term with - | Var v -> begin try Subst.Id.get v t_map with Not_found -> t end - | App (f, tys, args) -> - let new_tys = List.map (Ty.subst ty_map) tys in - let new_args = List.map (subst_aux ty_map t_map) args in - if List.for_all2 (==) new_tys tys && List.for_all2 (==) new_args args then t - else apply f new_tys new_args - - let subst ty_map t_map t = - if Subst.is_empty ty_map && Subst.is_empty t_map then - t - else - subst_aux ty_map t_map t - - let rec replace (t, t') t'' = match t''.term with - | _ when equal t t'' -> t' - | App (f, ty_args, t_args) -> - apply f ty_args (List.map (replace (t, t')) t_args) - | _ -> t'' - -end - -(* Formulas *) -(* ************************************************************************ *) - -module Atom = struct - type t = atom - - type proof = unit - - (* Hash & Comparisons *) - let h_eq = 2 - let h_pred = 3 - - let rec hash_aux f = match f.atom with - | Equal (t1, t2) -> - Hashtbl.hash (h_eq, Term.hash t1, Term.hash t2) - | Pred t -> - Hashtbl.hash (h_pred, Term.hash t) - - and hash f = - if f.f_hash = -1 then - f.f_hash <- hash_aux f; - f.f_hash - - let discr f = match f.atom with - | Equal _ -> 1 - | Pred _ -> 2 - - let compare f g = - let hf = hash f and hg = hash g in - if hf <> hg then Pervasives.compare hf hg - else match f.atom, g.atom with - | Equal (u1, v1), Equal(u2, v2) -> - list_cmp Term.compare [u1; v1] [u2; v2] - | Pred t1, Pred t2 -> Term.compare t1 t2 - | _, _ -> Pervasives.compare (discr f) (discr g) - - let equal u v = - u == v || (hash u = hash v && compare u v = 0) - - (* Printing functions *) - let print = Print.atom - - (* Constructors *) - let mk_formula f = { - sign = true; - atom = f; - f_hash = -1; - } - - let dummy = Dummy.atom - - let pred t = - if not (Ty.equal Ty.prop t.t_type) then - raise (Type_mismatch (t, t.t_type, Ty.prop)) - else - mk_formula (Pred t) - - let fresh () = - let id = Id.ty "fresh" Ty.prop in - pred (Term.of_id id) - - let neg f = - { f with sign = not f.sign } - - let eq a b = - if not (Ty.equal a.t_type b.t_type) then - raise (Type_mismatch (b, b.t_type, a.t_type)) - else if Term.compare a b < 0 then - mk_formula (Equal (a, b)) - else - mk_formula (Equal (b, a)) - - let norm f = - { f with sign = true }, - if f.sign then Formula_intf.Same_sign - else Formula_intf.Negated - -end - -module Formula = Msat_tseitin.Make(Atom) - diff --git a/src/smt/expr_smt.mli b/src/smt/expr_smt.mli deleted file mode 100644 index 03f0b07c..00000000 --- a/src/smt/expr_smt.mli +++ /dev/null @@ -1,326 +0,0 @@ - -(** Expressions for TabSat *) - -(** {2 Type definitions} *) - -(** These are custom types used in functions later. *) - -(** {3 Identifiers} *) - -(** Identifiers are the basic building blocks used to build types terms and expressions. *) - -type hash -type index = private int - -(** Private aliases to provide access. You should not have any need - to use these, instead use the functions provided by this module. *) - -type 'ty id = private { - id_type : 'ty; - id_name : string; - index : index; (** unique *) -} - -(** The type of identifiers. An ['a id] is an identifier whose solver-type - is represented by an inhabitant of type ['a]. - All identifier have an unique [index] which is used for comparison, - so that the name of a variable is only there for tracability - and/or pretty-printing. *) - -(** {3 Types} *) - -type ttype = Type - -(** The caml type of solver-types. *) - -type 'ty function_descr = private { - fun_vars : ttype id list; (* prenex forall *) - fun_args : 'ty list; - fun_ret : 'ty; -} - -(** This represents the solver-type of a function. - Functions can be polymorphic in the variables described in the - [fun_vars] field. *) - -type ty_descr = private - | TyVar of ttype id - (** bound variables (i.e should only appear under a quantifier) *) - | TyApp of ttype function_descr id * ty list - (** application of a constant to some arguments *) - -and ty = private { - ty : ty_descr; - mutable ty_hash : hash; (** Use Ty.hash instead *) -} - -(** These types defines solver-types, i.e the representation of the types - of terms in the solver. Record definition for [type ty] is shown in order - to be able to use the [ty.ty] field in patter matches. Other fields shoud not - be accessed directly, but throught the functions provided by the [Ty] module. *) - -(** {3 Terms} *) - -type term_descr = private - | Var of ty id - (** bound variables (i.e should only appear under a quantifier) *) - | App of ty function_descr id * ty list * term list - (** application of a constant to some arguments *) - -and term = private { - term : term_descr; - t_type : ty; - mutable t_hash : hash; (** Do not use this filed, call Term.hash instead *) -} - -(** Types defining terms in the solver. The definition is vary similar to that - of solver-types, except for type arguments of polymorphic functions which - are explicit. This has the advantage that there is a clear and typed distinction - between solver-types and terms, but may lead to some duplication of code - in some places. *) - -(** {3 Formulas} *) - -type atom_descr = private - (** Atoms *) - | Pred of term - | Equal of term * term - -and atom = private { - sign : bool; - atom : atom_descr; - mutable f_hash : hash; (** Use Formula.hash instead *) -} - -(** The type of atoms in the solver. The list of free arguments in quantifiers - is a bit tricky, so you should not touch it (see full doc for further - explanations). *) - -(** {3 Exceptions} *) - -exception Type_mismatch of term * ty * ty -(* Raised when as Type_mismatch(term, actual_type, expected_type) *) - -exception Bad_arity of ty function_descr id * ty list * term list -exception Bad_ty_arity of ttype function_descr id * ty list -(** Raised when trying to build an application with wrong arity *) - -(** {2 Printing} *) - -module Print : sig - (** Pretty printing functions *) - - val id : Format.formatter -> 'a id -> unit - val id_ty : Format.formatter -> ty id -> unit - val id_ttype : Format.formatter -> ttype id -> unit - - val const_ty : Format.formatter -> ty function_descr id -> unit - val const_ttype : Format.formatter -> ttype function_descr id -> unit - - val ty : Format.formatter -> ty -> unit - val fun_ty : Format.formatter -> ty function_descr -> unit - - val ttype : Format.formatter -> ttype -> unit - val fun_ttype : Format.formatter -> ttype function_descr -> unit - - val term : Format.formatter -> term -> unit - val atom : Format.formatter -> atom -> unit -end - -(** {2 Identifiers & Metas} *) - -module Id : sig - type 'a t = 'a id - (* Type alias *) - - val hash : 'a t -> int - val equal : 'a t -> 'a t -> bool - val compare : 'a t -> 'a t -> int - (** Usual functions for hash/comparison *) - - val print : Format.formatter -> 'a t -> unit - (** Printing for variables *) - - val prop : ttype function_descr id - val base : ttype function_descr id - (** Constants representing the type for propositions and a default type - for term, respectively. *) - - val ttype : string -> ttype id - (** Create a fresh type variable with the given name. *) - - val ty : string -> ty -> ty id - (** Create a fresh variable with given name and type *) - - val ty_fun : string -> int -> ttype function_descr id - (** Create a fresh type constructor with given name and arity *) - - val term_fun : string -> ttype id list -> ty list -> ty -> ty function_descr id - (** [ty_fun name type_vars arg_types return_type] returns a fresh constant symbol, - possibly polymorphic with respect to the variables in [type_vars] (which may appear in the - types in [arg_types] and in [return_type]). *) - -end - -(** {2 Substitutions} *) - -module Subst : sig - (** Module to handle substitutions *) - - type ('a, 'b) t - (** The type of substitutions from values of type ['a] to values of type ['b]. *) - - val empty : ('a, 'b) t - (** The empty substitution *) - - val is_empty : ('a, 'b) t -> bool - (** Test wether a substitution is empty *) - - val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit - (** Iterates over the bindings of the substitution. *) - - val fold : ('a -> 'b -> 'c -> 'c) -> ('a, 'b) t -> 'c -> 'c - (** Fold over the elements *) - - val bindings : ('a, 'b) t -> ('a * 'b) list - (** Returns the list of bindings ofa substitution. *) - - val exists : ('a -> 'b -> bool) -> ('a, 'b) t -> bool - (** Tests wether the predicate holds for at least one binding. *) - - val for_all : ('a -> 'b -> bool) -> ('a, 'b) t -> bool - (** Tests wether the predicate holds for all bindings. *) - - val hash : ('b -> int) -> ('a, 'b) t -> int - val compare : ('b -> 'b -> int) -> ('a, 'b) t -> ('a, 'b) t -> int - val equal : ('b -> 'b -> bool) -> ('a, 'b) t -> ('a, 'b) t -> bool - (** Comparison and hash functions, with a comparison/hash function on values as parameter *) - - val print : - (Format.formatter -> 'a -> unit) -> - (Format.formatter -> 'b -> unit) -> - Format.formatter -> ('a, 'b) t -> unit - (** Prints the substitution, using the given functions to print keys and values. *) - - val choose : ('a, 'b) t -> 'a * 'b - (** Return one binding of the given substitution, or raise Not_found if the substitution is empty.*) - - (** {5 Concrete subtitutions } *) - module type S = sig - type 'a key - val get : 'a key -> ('a key, 'b) t -> 'b - (** [get v subst] returns the value associated with [v] in [subst], if it exists. - @raise Not_found if there is no binding for [v]. *) - val mem : 'a key -> ('a key, 'b) t -> bool - (** [get v subst] returns wether there is a value associated with [v] in [subst]. *) - val bind : 'a key -> 'b -> ('a key, 'b) t -> ('a key, 'b) t - (** [bind v t subst] returns the same substitution as [subst] with the additional binding from [v] to [t]. - Erases the previous binding of [v] if it exists. *) - val remove : 'a key -> ('a key, 'b) t -> ('a key, 'b) t - (** [remove v subst] returns the same substitution as [subst] except for [v] which is unbound in the returned substitution. *) - end - - module Id : S with type 'a key = 'a id -end - -(** {2 Types} *) - -module Ty : sig - type t = ty - (** Type alias *) - - type subst = (ttype id, ty) Subst.t - (** The type of substitutions over types. *) - - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - (** Usual hash/compare functions *) - - val print : Format.formatter -> t -> unit - - val prop : ty - val base : ty - (** The type of propositions and individuals *) - - val of_id : ttype id -> ty - (** Creates a type from a variable *) - - val apply : ttype function_descr id -> ty list -> ty - (** Applies a constant to a list of types *) - - val subst : subst -> ty -> ty - (** Substitution over types. *) - -end - -(** {2 Terms} *) - -module Term : sig - type t = term - (** Type alias *) - - type subst = (ty id, term) Subst.t - (** The type of substitutions in types. *) - - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - (** Usual hash/compare functions *) - - val print : Format.formatter -> t -> unit - (** Printing functions *) - - val of_id : ty id -> term - (** Create a term from a variable *) - - val apply : ty function_descr id -> ty list -> term list -> term - (** Applies a constant function to type arguments, then term arguments *) - - val subst : Ty.subst -> subst -> term -> term - (** Substitution over types. *) - - val replace : term * term -> term -> term - (** [replace (t, t') t''] returns the term [t''] where every occurence of [t] - has been replace by [t']. *) - -end - -(** {2 Formulas} *) - -module Atom : sig - type t = atom - type proof = unit - (** Type alias *) - - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - (** Usual hash/compare functions *) - - val print : Format.formatter -> t -> unit - (** Printing functions *) - - val dummy : atom - (** A dummy atom, different from any other atom. *) - - val fresh : unit -> atom - (** Create a fresh propositional atom. *) - - val eq : term -> term -> atom - (** Create an equality over two terms. The two given terms - must have the same type [t], which must be different from {!Ty.prop} *) - - val pred : term -> atom - (** Create a atom from a term. The given term must have type {!Ty.prop} *) - - val neg : atom -> atom - (** Returns the negation of the given atom *) - - val norm : atom -> atom * Formula_intf.negated - (** Normalization functions as required by msat. *) - -end - -module Formula : Msat_tseitin.S with type atom = atom - diff --git a/src/smt/type_smt.ml b/src/smt/type_smt.ml deleted file mode 100644 index 5dbe3837..00000000 --- a/src/smt/type_smt.ml +++ /dev/null @@ -1,628 +0,0 @@ - -(* Log&Module Init *) -(* ************************************************************************ *) - -module Ast = Dolmen.Term -module Id = Dolmen.Id -module M = Map.Make(Id) -module H = Hashtbl.Make(Id) -module Expr = Expr_smt - -(* Types *) -(* ************************************************************************ *) - -(* The type of potentially expected result type for parsing an expression *) -type expect = - | Nothing - | Type - | Typed of Expr.ty - -(* The type returned after parsing an expression. *) -type res = - | Ttype - | Ty of Expr.ty - | Term of Expr.term - | Formula of Expr.Formula.t - - -(* The local environments used for type-checking. *) -type env = { - - (* local variables (mostly quantified variables) *) - type_vars : (Expr.ttype Expr.id) M.t; - term_vars : (Expr.ty Expr.id) M.t; - - (* Bound variables (through let constructions) *) - term_lets : Expr.term M.t; - prop_lets : Expr.Formula.t M.t; - - (* Typing options *) - expect : expect; -} - -(* Exceptions *) -(* ************************************************************************ *) - -(* Internal exception *) -exception Found of Ast.t - -(* Exception for typing errors *) -exception Typing_error of string * Ast.t - -(* Convenience functions *) -let _expected s t = raise (Typing_error ( - Format.asprintf "Expected a %s" s, t)) -let _bad_arity s n t = raise (Typing_error ( - Format.asprintf "Bad arity for operator '%s' (expected %d arguments)" s n, t)) -let _type_mismatch t ty ty' ast = raise (Typing_error ( - Format.asprintf "Type Mismatch: '%a' has type %a, but an expression of type %a was expected" - Expr.Print.term t Expr.Print.ty ty Expr.Print.ty ty', ast)) -let _fo_term s t = raise (Typing_error ( - Format.asprintf "Let-bound variable '%a' is applied to terms" Id.print s, t)) - -(* Global Environment *) -(* ************************************************************************ *) - -(* Global identifier table; stores declared types and aliases. *) -let global_env = H.create 42 - -let find_global name = - try H.find global_env name - with Not_found -> `Not_found - -(* Symbol declarations *) -let decl_ty_cstr id c = - if H.mem global_env id then - Log.debugf 0 - (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); - H.add global_env id (`Ty c); - Log.debugf 1 (fun k -> k "New type constructor : %a" Expr.Print.const_ttype c) - -let decl_term id c = - if H.mem global_env id then - Log.debugf 0 - (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); - H.add global_env id (`Term c); - Log.debugf 1 (fun k -> k "New constant : %a" Expr.Print.const_ty c) - -(* Symbol definitions *) -let def_ty id args body = - if H.mem global_env id then - Log.debugf 0 - (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); - H.add global_env id (`Ty_alias (args, body)) - -let def_term id ty_args args body = - if H.mem global_env id then - Log.debugf 0 - (fun k -> k "Symbol '%a' has already been defined, overwriting previous definition" Id.print id); - H.add global_env id (`Term_alias (ty_args, args, body)) - -(* Local Environment *) -(* ************************************************************************ *) - -(* Make a new empty environment *) -let empty_env ?(expect=Nothing) () = { - type_vars = M.empty; - term_vars = M.empty; - term_lets = M.empty; - prop_lets = M.empty; - expect; -} - -(* Generate new fresh names for shadowed variables *) -let new_name pre = - let i = ref 0 in - (fun () -> incr i; pre ^ (string_of_int !i)) - -let new_ty_name = new_name "ty#" -let new_term_name = new_name "term#" - -(* Add local variables to environment *) -let add_type_var env id v = - let v' = - if M.mem id env.type_vars then - Expr.Id.ttype (new_ty_name ()) - else - v - in - Log.debugf 1 - (fun k -> k "New binding : %a -> %a" Id.print id Expr.Print.id_ttype v'); - v', { env with type_vars = M.add id v' env.type_vars } - -let add_type_vars env l = - let l', env' = List.fold_left (fun (l, acc) (id, v) -> - let v', acc' = add_type_var acc id v in - v' :: l, acc') ([], env) l in - List.rev l', env' - -let add_term_var env id v = - let v' = - if M.mem id env.type_vars then - Expr.Id.ty (new_term_name ()) Expr.(v.id_type) - else - v - in - Log.debugf 1 - (fun k -> k "New binding : %a -> %a" Id.print id Expr.Print.id_ty v'); - v', { env with term_vars = M.add id v' env.term_vars } - -let find_var env name = - try `Ty (M.find name env.type_vars) - with Not_found -> - begin - try - `Term (M.find name env.term_vars) - with Not_found -> - `Not_found - end - -(* Add local bound variables to env *) -let add_let_term env id t = - Log.debugf 1 - (fun k -> k "New let-binding : %s -> %a" id.Id.name Expr.Print.term t); - { env with term_lets = M.add id t env.term_lets } - -let add_let_prop env id t = - Log.debugf 1 - (fun k -> k "New let-binding : %s -> %a" id.Id.name Expr.Formula.print t); - { env with prop_lets = M.add id t env.prop_lets } - -let find_let env name = - try `Term (M.find name env.term_lets) - with Not_found -> - begin - try - `Prop (M.find name env.prop_lets) - with Not_found -> - `Not_found - end - -(* Some helper functions *) -(* ************************************************************************ *) - -let flat_map f l = List.flatten (List.map f l) - -let take_drop n l = - let rec aux acc = function - | 0, res | _, ([] as res) -> List.rev acc, res - | m, x :: r -> aux (x :: acc) (m - 1, r) - in - aux [] (n, l) - -let diagonal l = - let rec single x acc = function - | [] -> acc - | y :: r -> single x ((x, y) :: acc) r - and aux acc = function - | [] -> acc - | x :: r -> aux (single x acc r) r - in - aux [] l - -(* Wrappers for expression building *) -(* ************************************************************************ *) - -let arity f = - List.length Expr.(f.id_type.fun_vars) + - List.length Expr.(f.id_type.fun_args) - -let ty_apply _env ast f args = - try - Expr.Ty.apply f args - with Expr.Bad_ty_arity _ -> - _bad_arity Expr.(f.id_name) (arity f) ast - -let term_apply _env ast f ty_args t_args = - try - Expr.Term.apply f ty_args t_args - with - | Expr.Bad_arity _ -> - _bad_arity Expr.(f.id_name) (arity f) ast - | Expr.Type_mismatch (t, ty, ty') -> - _type_mismatch t ty ty' ast - -let ty_subst ast_term id args f_args body = - let aux s v ty = Expr.Subst.Id.bind v ty s in - match List.fold_left2 aux Expr.Subst.empty f_args args with - | subst -> - Expr.Ty.subst subst body - | exception Invalid_argument _ -> - _bad_arity id.Id.name (List.length f_args) ast_term - -let term_subst ast_term id ty_args t_args f_ty_args f_t_args body = - let aux s v ty = Expr.Subst.Id.bind v ty s in - match List.fold_left2 aux Expr.Subst.empty f_ty_args ty_args with - | ty_subst -> - begin - let aux s v t = Expr.Subst.Id.bind v t s in - match List.fold_left2 aux Expr.Subst.empty f_t_args t_args with - | t_subst -> - Expr.Term.subst ty_subst t_subst body - | exception Invalid_argument _ -> - _bad_arity id.Id.name (List.length f_ty_args + List.length f_t_args) ast_term - end - | exception Invalid_argument _ -> - _bad_arity id.Id.name (List.length f_ty_args + List.length f_t_args) ast_term - -let make_eq ast_term a b = - try - Expr.Formula.make_atom @@ Expr.Atom.eq a b - with Expr.Type_mismatch (t, ty, ty') -> - _type_mismatch t ty ty' ast_term - -let make_pred ast_term p = - try - Expr.Formula.make_atom @@ Expr.Atom.pred p - with Expr.Type_mismatch (t, ty, ty') -> - _type_mismatch t ty ty' ast_term - -let infer env s args = - match env.expect with - | Nothing -> `Nothing - | Type -> - let n = List.length args in - let res = Expr.Id.ty_fun s.Id.name n in - decl_ty_cstr s res; - `Ty res - | Typed ty -> - let n = List.length args in - let rec replicate acc n = - if n <= 0 then acc else replicate (Expr.Ty.base :: acc) (n - 1) - in - let res = Expr.Id.term_fun s.Id.name [] (replicate [] n) ty in - decl_term s res; - `Term res - -(* Expression parsing *) -(* ************************************************************************ *) - -[@@@ocaml.warning "-9"] - -let rec parse_expr (env : env) t = - match t with - (* Base Types *) - | { Ast.term = Ast.Builtin Ast.Ttype } -> - Ttype - | { Ast.term = Ast.Symbol { Id.name = "Bool" } } -> - Ty (Expr_smt.Ty.prop) - - (* Basic formulas *) - | { Ast.term = Ast.App ({ Ast.term = Ast.Builtin Ast.True }, []) } - | { Ast.term = Ast.Builtin Ast.True } -> - Formula Expr.Formula.f_true - - | { Ast.term = Ast.App ({ Ast.term = Ast.Builtin Ast.False }, []) } - | { Ast.term = Ast.Builtin Ast.False } -> - Formula Expr.Formula.f_false - - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.And}, l) } - | { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "and" }}, l) } -> - Formula (Expr.Formula.make_and (List.map (parse_formula env) l)) - - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Or}, l) } - | { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "or" }}, l) } -> - Formula (Expr.Formula.make_or (List.map (parse_formula env) l)) - - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Xor}, l) } as t -> - begin match l with - | [p; q] -> - let f = parse_formula env p in - let g = parse_formula env q in - Formula (Expr.Formula.make_not (Expr.Formula.make_equiv f g)) - | _ -> _bad_arity "xor" 2 t - end - - | ({ Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Imply}, l) } as t) - | ({ Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "=>" }}, l) } as t) -> - begin match l with - | [p; q] -> - let f = parse_formula env p in - let g = parse_formula env q in - Formula (Expr.Formula.make_imply f g) - | _ -> _bad_arity "=>" 2 t - end - - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Equiv}, l) } as t -> - begin match l with - | [p; q] -> - let f = parse_formula env p in - let g = parse_formula env q in - Formula (Expr.Formula.make_equiv f g) - | _ -> _bad_arity "<=>" 2 t - end - - | ({ Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Not}, l) } as t) - | ({ Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "not" }}, l) } as t) -> - begin match l with - | [p] -> - Formula (Expr.Formula.make_not (parse_formula env p)) - | _ -> _bad_arity "not" 1 t - end - - (* (Dis)Equality *) - | ({ Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Eq}, l) } as t) - | ({ Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "=" }}, l) } as t) -> - begin match l with - | [a; b] -> - Formula ( - make_eq t - (parse_term env a) - (parse_term env b) - ) - | _ -> _bad_arity "=" 2 t - end - - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Distinct}, args) } as t -> - let l' = List.map (parse_term env) args in - let l'' = diagonal l' in - Formula ( - Expr.Formula.make_and - (List.map (fun (a, b) -> - Expr.Formula.make_not - (make_eq t a b)) l'') - ) - - (* General case: application *) - | { Ast.term = Ast.Symbol s } as ast -> - parse_app env ast s [] - | { Ast.term = Ast.App ({ Ast.term = Ast.Symbol s }, l) } as ast -> - parse_app env ast s l - - (* Local bindings *) - | { Ast.term = Ast.Binder (Ast.Let, vars, f) } -> - parse_let env f vars - - (* Other cases *) - | ast -> raise (Typing_error ("Couldn't parse the expression", ast)) - -and parse_var env = function - | { Ast.term = Ast.Colon ({ Ast.term = Ast.Symbol s }, e) } -> - begin match parse_expr env e with - | Ttype -> `Ty (s, Expr.Id.ttype s.Id.name) - | Ty ty -> `Term (s, Expr.Id.ty s.Id.name ty) - | _ -> _expected "type (or Ttype)" e - end - | { Ast.term = Ast.Symbol s } -> - begin match env.expect with - | Nothing -> assert false - | Type -> `Ty (s, Expr.Id.ttype s.Id.name) - | Typed ty -> `Term (s, Expr.Id.ty s.Id.name ty) - end - | t -> _expected "(typed) variable" t - -and parse_quant_vars env l = - let ttype_vars, typed_vars, env' = List.fold_left ( - fun (l1, l2, acc) v -> - match parse_var acc v with - | `Ty (id, v') -> - let v'', acc' = add_type_var acc id v' in - (v'' :: l1, l2, acc') - | `Term (id, v') -> - let v'', acc' = add_term_var acc id v' in - (l1, v'' :: l2, acc') - ) ([], [], env) l in - List.rev ttype_vars, List.rev typed_vars, env' - -and parse_let env f = function - | [] -> parse_expr env f - | x :: r -> - begin match x with - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Eq}, [ - { Ast.term = Ast.Symbol s }; e]) } -> - let t = parse_term env e in - let env' = add_let_term env s t in - parse_let env' f r - | { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Equiv}, [ - { Ast.term = Ast.Symbol s }; e]) } -> - let t = parse_formula env e in - let env' = add_let_prop env s t in - parse_let env' f r - | { Ast.term = Ast.Colon ({ Ast.term = Ast.Symbol s }, e) } -> - begin match parse_expr env e with - | Term t -> - let env' = add_let_term env s t in - parse_let env' f r - | Formula t -> - let env' = add_let_prop env s t in - parse_let env' f r - | _ -> _expected "term of formula" e - end - | t -> _expected "let-binding" t - end - -and parse_app env ast s args = - match find_let env s with - | `Term t -> - if args = [] then Term t - else _fo_term s ast - | `Prop p -> - if args = [] then Formula p - else _fo_term s ast - | `Not_found -> - begin match find_var env s with - | `Ty f -> - if args = [] then Ty (Expr.Ty.of_id f) - else _fo_term s ast - | `Term f -> - if args = [] then Term (Expr.Term.of_id f) - else _fo_term s ast - | `Not_found -> - begin match find_global s with - | `Ty f -> - parse_app_ty env ast f args - | `Term f -> - parse_app_term env ast f args - | `Ty_alias (f_args, body) -> - parse_app_subst_ty env ast s args f_args body - | `Term_alias (f_ty_args, f_t_args, body) -> - parse_app_subst_term env ast s args f_ty_args f_t_args body - | `Not_found -> - begin match infer env s args with - | `Ty f -> parse_app_ty env ast f args - | `Term f -> parse_app_term env ast f args - | `Nothing -> - raise (Typing_error ( - Format.asprintf "Scoping error: '%a' not found" Id.print s, ast)) - end - end - end - -and parse_app_ty env ast f args = - let l = List.map (parse_ty env) args in - Ty (ty_apply env ast f l) - -and parse_app_term env ast f args = - let n = List.length Expr.(f.id_type.fun_vars) in - let ty_l, t_l = take_drop n args in - let ty_args = List.map (parse_ty env) ty_l in - let t_args = List.map (parse_term env) t_l in - Term (term_apply env ast f ty_args t_args) - -and parse_app_subst_ty env ast id args f_args body = - let l = List.map (parse_ty env) args in - Ty (ty_subst ast id l f_args body) - -and parse_app_subst_term env ast id args f_ty_args f_t_args body = - let n = List.length f_ty_args in - let ty_l, t_l = take_drop n args in - let ty_args = List.map (parse_ty env) ty_l in - let t_args = List.map (parse_term env) t_l in - Term (term_subst ast id ty_args t_args f_ty_args f_t_args body) - -and parse_ty env ast = - match parse_expr { env with expect = Type } ast with - | Ty ty -> ty - | _ -> _expected "type" ast - -and parse_term env ast = - match parse_expr { env with expect = Typed Expr.Ty.base } ast with - | Term t -> t - | _ -> _expected "term" ast - -and parse_formula env ast = - match parse_expr { env with expect = Typed Expr.Ty.prop } ast with - | Term t when Expr.(Ty.equal Ty.prop t.t_type) -> - make_pred ast t - | Formula p -> p - | _ -> _expected "formula" ast - -let parse_ttype_var env t = - match parse_var env t with - | `Ty (id, v) -> (id, v) - | `Term _ -> _expected "type variable" t - -let rec parse_sig_quant env = function - | { Ast.term = Ast.Binder (Ast.Pi, vars, t) } -> - let ttype_vars = List.map (parse_ttype_var env) vars in - let ttype_vars', env' = add_type_vars env ttype_vars in - let l = List.combine vars ttype_vars' in - parse_sig_arrow l [] env' t - | t -> - parse_sig_arrow [] [] env t - -and parse_sig_arrow ttype_vars (ty_args: (Ast.t * res) list) env = function - | { Ast.term = Ast.Binder (Ast.Arrow, args, ret) } -> - let t_args = parse_sig_args env args in - parse_sig_arrow ttype_vars (ty_args @ t_args) env ret - | t -> - begin match parse_expr env t with - | Ttype -> - begin match ttype_vars with - | (h, _) :: _ -> - raise (Typing_error ( - "Type constructor signatures cannot have quantified type variables", h)) - | [] -> - let aux n = function - | (_, Ttype) -> n + 1 - | (ast, _) -> raise (Found ast) - in - begin - match List.fold_left aux 0 ty_args with - | n -> `Ty_cstr n - | exception Found err -> - raise (Typing_error ( - Format.asprintf - "Type constructor signatures cannot have non-ttype arguments,", err)) - end - end - | Ty ret -> - let aux acc = function - | (_, Ty t) -> t :: acc - | (ast, _) -> raise (Found ast) - in - begin - match List.fold_left aux [] ty_args with - | exception Found err -> _expected "type" err - | l -> `Fun_ty (List.map snd ttype_vars, List.rev l, ret) - end - | _ -> _expected "Ttype of type" t - end - -and parse_sig_args env l = - flat_map (parse_sig_arg env) l - -and parse_sig_arg env = function - | { Ast.term = Ast.App ({ Ast.term = Ast.Builtin Ast.Product}, l) } -> - List.map (fun x -> x, parse_expr env x) l - | t -> - [t, parse_expr env t] - -let parse_sig = parse_sig_quant - -let rec parse_fun ty_args t_args env = function - | { Ast.term = Ast.Binder (Ast.Fun, l, ret) } -> - let ty_args', t_args', env' = parse_quant_vars env l in - parse_fun (ty_args @ ty_args') (t_args @ t_args') env' ret - | ast -> - begin match parse_expr env ast with - | Ttype -> raise (Typing_error ("Cannot redefine Ttype", ast)) - | Ty body -> - if t_args = [] then `Ty (ty_args, body) - else _expected "term" ast - | Term body -> `Term (ty_args, t_args, body) - | Formula _ -> _expected "type or term" ast - end - -[@@@ocaml.warning "+9"] - -(* High-level parsing functions *) -(* ************************************************************************ *) - -let decl id t = - let env = empty_env () in - Log.debugf 5 - (fun k -> k "Typing declaration: %s : %a" id.Id.name Ast.print t); - begin match parse_sig env t with - | `Ty_cstr n -> decl_ty_cstr id (Expr.Id.ty_fun id.Id.name n) - | `Fun_ty (vars, args, ret) -> - decl_term id (Expr.Id.term_fun id.Id.name vars args ret) - end - -let def id t = - let env = empty_env () in - Log.debugf 5 - (fun k -> k "Typing definition: %s = %a" id.Id.name Ast.print t); - begin match parse_fun [] [] env t with - | `Ty (ty_args, body) -> def_ty id ty_args body - | `Term (ty_args, t_args, body) -> def_term id ty_args t_args body - end - -let formula t = - let env = empty_env () in - Log.debugf 5 (fun k -> k "Typing top-level formula: %a" Ast.print t); - parse_formula env t - -let assumptions t = - let cnf = Expr.Formula.make_cnf (formula t) in - List.map (function - | [ x ] -> x - | _ -> assert false - ) cnf - -let antecedent t = - Expr.Formula.make_cnf (formula t) - -let consequent t = - Expr.Formula.make_cnf (Expr.Formula.make_not (formula t)) - diff --git a/src/smt/type_smt.mli b/src/smt/type_smt.mli deleted file mode 100644 index 1613debb..00000000 --- a/src/smt/type_smt.mli +++ /dev/null @@ -1,7 +0,0 @@ - -(** Typechecking of terms from Dolmen.Term.t - This module provides functions to parse terms from the untyped syntax tree - defined in Dolmen, and generate formulas as defined in the Expr_smt module. *) - -include Minismt.Type.S with type atom := Expr_smt.Atom.t - diff --git a/src/smt/unionfind.ml b/src/smt/unionfind.ml deleted file mode 100644 index ab81a1ad..00000000 --- a/src/smt/unionfind.ml +++ /dev/null @@ -1,90 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module type OrderedType = sig - type t - val compare : t -> t -> int -end - -(* Union-find Module *) -module Make(T : OrderedType) = struct - exception Unsat of T.t * T.t - - type var = T.t - module M = Map.Make(T) - - (* TODO: better treatment of inequalities *) - - type t = { - rank : int M.t; - forbid : ((var * var) list); - mutable parent : var M.t; - } - - let empty = { - rank = M.empty; - forbid = []; - parent = M.empty; - } - - let find_map m i default = - try - M.find i m - with Not_found -> - default - - let rec find_aux f i = - let fi = find_map f i i in - if fi = i then - (f, i) - else - let f, r = find_aux f fi in - let f = M.add i r f in - (f, r) - - let find h x = - let f, cx = find_aux h.parent x in - h.parent <- f; - cx - - (* Highly inefficient treatment of inequalities *) - let possible h = - let aux (a, b) = - let ca = find h a in - let cb = find h b in - if T.compare ca cb = 0 then - raise (Unsat (a, b)) - in - List.iter aux h.forbid; - h - - let union_aux h x y = - let cx = find h x in - let cy = find h y in - if cx != cy then begin - let rx = find_map h.rank cx 0 in - let ry = find_map h.rank cy 0 in - if rx > ry then - { h with parent = M.add cy cx h.parent } - else if ry > rx then - { h with parent = M.add cx cy h.parent } - else - { rank = M.add cx (rx + 1) h.rank; - parent = M.add cy cx h.parent; - forbid = h.forbid; } - end else - h - - let union h x y = possible (union_aux h x y) - - let forbid h x y = - let cx = find h x in - let cy = find h y in - if cx = cy then - raise (Unsat (x, y)) - else - { h with forbid = (x, y) :: h.forbid } -end diff --git a/src/smt/unionfind.mli b/src/smt/unionfind.mli deleted file mode 100644 index adf2cd25..00000000 --- a/src/smt/unionfind.mli +++ /dev/null @@ -1,20 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module type OrderedType = sig - type t - val compare : t -> t -> int -end - -module Make(T : OrderedType) : sig - type t - exception Unsat of T.t * T.t - val empty : t - val find : t -> T.t -> T.t - val union : t -> T.t -> T.t -> t - val forbid : t -> T.t -> T.t -> t -end - diff --git a/src/solver/dune b/src/solver/dune deleted file mode 100644 index 31c93320..00000000 --- a/src/solver/dune +++ /dev/null @@ -1,11 +0,0 @@ - -(library - (name minismt) - (public_name minismt) - (libraries msat dolmen) - (synopsis "minismt") - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) - diff --git a/src/solver/mcsolver.ml b/src/solver/mcsolver.ml deleted file mode 100644 index f6eb0c68..00000000 --- a/src/solver/mcsolver.ml +++ /dev/null @@ -1,15 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module type S = Msat.S - -module Make (E : Expr_intf.S) - (Th : Plugin_intf.S with type term = E.Term.t - and type formula = E.Formula.t - and type proof = E.proof) - = Msat.Make (Make_mcsat_expr(E)) (Th) - - diff --git a/src/solver/mcsolver.mli b/src/solver/mcsolver.mli deleted file mode 100644 index 51f05b8a..00000000 --- a/src/solver/mcsolver.mli +++ /dev/null @@ -1,23 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Create McSat solver - - This module provides a functor to create an McSAt solver. -*) - -module type S = Msat.S -(** The interface exposed by the solver. *) - -module Make (E : Expr_intf.S) - (Th : Plugin_intf.S with type term = E.Term.t - and type formula = E.Formula.t - and type proof = E.proof) - : S with type term = E.Term.t - and type formula = E.Formula.t - and type Proof.lemma = E.proof -(** Functor to create a solver parametrised by the atomic formulas and a theory. *) - diff --git a/src/solver/solver.ml b/src/solver/solver.ml deleted file mode 100644 index d06de2e8..00000000 --- a/src/solver/solver.ml +++ /dev/null @@ -1,81 +0,0 @@ -(**************************************************************************) -(* *) -(* Alt-Ergo Zero *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - -module type S = Msat.S - -module DummyTheory(F : Formula_intf.S) = struct - (* We don't have anything to do since the SAT Solver already - * does propagation and conflict detection *) - - type formula = F.t - type proof = F.proof - type level = unit - - let dummy = () - let current_level () = () - let assume _ = Theory_intf.Sat - let backtrack _ = () - let if_sat _ = Theory_intf.Sat -end - -module Plugin(E : Formula_intf.S) - (Th : Theory_intf.S with type formula = E.t and type proof = E.proof) = struct - - type term = E.t - type formula = E.t - type proof = Th.proof - type level = Th.level - - let dummy = Th.dummy - - let current_level = Th.current_level - - let assume_get get = - function i -> - match get i with - | Plugin_intf.Lit f -> f - | _ -> assert false - - let assume_propagate propagate = - fun f l proof -> - propagate f (Plugin_intf.Consequence (l, proof)) - - let mk_slice s = { - Theory_intf.start = s.Plugin_intf.start; - length = s.Plugin_intf.length; - get = assume_get s.Plugin_intf.get; - push = s.Plugin_intf.push; - propagate = assume_propagate s.Plugin_intf.propagate; - } - - let assume s = Th.assume (mk_slice s) - - let backtrack = Th.backtrack - - let if_sat s = Th.if_sat (mk_slice s) - - - (* McSat specific functions *) - let assign _ = assert false - - let iter_assignable _ _ = () - - let eval _ = Plugin_intf.Unknown - -end - - -module Make (E : Formula_intf.S) - (Th : Theory_intf.S with type formula = E.t and type proof = E.proof) - = Msat.Make (Make_smt_expr(E)) (Plugin(E)(Th)) - - diff --git a/src/solver/solver.mli b/src/solver/solver.mli deleted file mode 100644 index 77efd675..00000000 --- a/src/solver/solver.mli +++ /dev/null @@ -1,30 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Create SAT/SMT Solvers - - This module provides a functor to create an SMT solver. Additionally, it also - gives a functor that produce an adequate empty theory that can be given to the [Make] - functor in order to create a pure SAT solver. -*) - -module type S = Msat.S -(** The interface of instantiated solvers. *) - -module DummyTheory(F : Formula_intf.S) : - Theory_intf.S with type formula = F.t - and type proof = F.proof -(** Simple case where the theory is empty and - the proof type is the one given in the formula interface *) - -module Make (F : Formula_intf.S) - (Th : Theory_intf.S with type formula = F.t - and type proof = F.proof) - : S with type formula = F.t - and type Proof.lemma = F.proof -(** Functor to create a SMT Solver parametrised by the atomic - formulas and a theory. *) - diff --git a/src/solver/type.ml b/src/solver/type.ml deleted file mode 100644 index 693f35ce..00000000 --- a/src/solver/type.ml +++ /dev/null @@ -1,28 +0,0 @@ - -(** Typechecking of terms from Dolmen.Term.t - This module defines the requirements for typing expre'ssions from dolmen. *) - -module type S = sig - - type atom - (** The type of atoms that will be fed to tha sovler. *) - - exception Typing_error of string * Dolmen.Term.t - (** Exception raised during typechecking. *) - - val decl : Dolmen.Id.t -> Dolmen.Term.t -> unit - (** New declaration, i.e an identifier and its type. *) - - val def : Dolmen.Id.t -> Dolmen.Term.t -> unit - (** New definition, i.e an identifier and the term it is equal to. *) - - val assumptions : Dolmen.Term.t -> atom list - (** Parse a list of local assumptions. *) - - val consequent : Dolmen.Term.t -> atom list list - val antecedent : Dolmen.Term.t -> atom list list - (** Parse a formula, and return a cnf ready to be given to the solver. - Consequent is for hypotheses (left of the sequent), while antecedent - is for goals (i.e formulas on the right of a sequent). *) - -end diff --git a/tests/dune b/tests/dune index 34c62686..6e357a4c 100644 --- a/tests/dune +++ b/tests/dune @@ -1,8 +1,8 @@ (executable (name test_api) - (libraries msat msat.tseitin msat.backend minismt.sat minismt.smt minismt.mcsat dolmen) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (libraries msat msat_sat) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) ) diff --git a/tests/run b/tests/run index 463ab373..a8c33d98 100755 --- a/tests/run +++ b/tests/run @@ -1,13 +1,13 @@ #!/bin/bash CURDIR=`dirname $0` -SOLVER="$CURDIR/../msat.exe" +SOLVER="$CURDIR/../msat.sh" solvertest () { - for f in `find -L $1 -type f -name '*.cnf' -o -name '*.smt2'` + for f in `find -L $1 -type f -name '*.cnf' # -o -name '*.smt2'` do echo -ne "\r\033[KTesting $f..." - "$SOLVER" -s $3 -time 30s -size 1G -check $f | grep $2 + "$SOLVER" -time 30s -size 1G -check $f | grep $2 RET=$? if [ $RET -ne 0 ]; then diff --git a/tests/test_api.ml b/tests/test_api.ml index 838ceed0..89e8cafb 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -6,10 +6,7 @@ Copyright 2014 Simon Cruanes (* Tests that require the API *) -open Msat - -module F = Minismt_sat.Expr -module T = Msat_tseitin.Make(F) +module F = Msat_sat.Expr let (|>) x f = f x @@ -48,7 +45,7 @@ end let mk_solver (): (module BASIC_SOLVER) = let module S = struct - include Minismt_sat + include Msat_sat let create() = create() let solve st ?assumptions () = match solve st ?assumptions() with From b3fc070d09664202bc71b2554d5483f388510604 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 19:42:41 -0600 Subject: [PATCH 051/182] style: remove old headers --- src/core/Heap.ml | 12 ------------ src/core/Heap.mli | 13 ------------- src/core/Plugin_intf.ml | 13 ------------- src/core/Solver_types.ml | 12 ------------ src/core/Solver_types.mli | 12 ------------ src/core/Solver_types_intf.ml | 12 ------------ src/core/Theory_intf.ml | 13 ------------- 7 files changed, 87 deletions(-) diff --git a/src/core/Heap.ml b/src/core/Heap.ml index a8f78d08..1e309ab1 100644 --- a/src/core/Heap.ml +++ b/src/core/Heap.ml @@ -1,15 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) module type RANKED = Heap_intf.RANKED diff --git a/src/core/Heap.mli b/src/core/Heap.mli index 24c47bd6..a621885c 100644 --- a/src/core/Heap.mli +++ b/src/core/Heap.mli @@ -1,16 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - module type RANKED = Heap_intf.RANKED module type S = Heap_intf.S diff --git a/src/core/Plugin_intf.ml b/src/core/Plugin_intf.ml index 2584eb52..51a4da72 100644 --- a/src/core/Plugin_intf.ml +++ b/src/core/Plugin_intf.ml @@ -1,16 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Sylvain Conchon, Evelyne Contejean *) -(* Francois Bobot, Mohamed Iguernelala, Alain Mebsout *) -(* CNRS, Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) (* MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 5e2686ab..add513cf 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -1,15 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) (* MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli index 4d12d93d..62219a3a 100644 --- a/src/core/Solver_types.mli +++ b/src/core/Solver_types.mli @@ -1,15 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) (* MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index f5076d30..4a1816be 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -1,15 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) (* MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury diff --git a/src/core/Theory_intf.ml b/src/core/Theory_intf.ml index f0183342..7a6621d8 100644 --- a/src/core/Theory_intf.ml +++ b/src/core/Theory_intf.ml @@ -1,16 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Sylvain Conchon, Evelyne Contejean *) -(* Francois Bobot, Mohamed Iguernelala, Alain Mebsout *) -(* CNRS, Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) (* MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury From e60aff60b6028b8eac4fe81a17abad995eea5a91 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 19:59:23 -0600 Subject: [PATCH 052/182] refactor: simplify `vec`, remove the need to provide dummy elt --- src/backend/Dimacs.ml | 4 +- src/core/Expr_intf.ml | 3 - src/core/Formula_intf.ml | 3 - src/core/Heap.ml | 10 +- src/core/Heap_intf.ml | 5 - src/core/Internal.ml | 46 +++----- src/core/Solver.ml | 10 +- src/core/Solver_types.ml | 54 ++------- src/core/Solver_types_intf.ml | 3 - src/core/Theory_intf.ml | 4 - src/core/Vec.ml | 204 +++++++++------------------------- src/core/Vec.mli | 64 ++--------- src/core/dune | 1 + src/sat/Expr_sat.ml | 3 - 14 files changed, 100 insertions(+), 314 deletions(-) diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index 07b1ee02..3339175d 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -34,7 +34,7 @@ module Make(St : Solver_types_intf.S) = struct (* Dimacs & iCNF export *) let export_vec name fmt vec = - Format.fprintf fmt "c %s@,%a@," name (Vec.print ~sep:"" St.Clause.pp_dimacs) vec + Format.fprintf fmt "c %s@,%a@," name (Vec.pp ~sep:"" St.Clause.pp_dimacs) vec let export_assumption fmt vec = Format.fprintf fmt "c Local assumptions@,a %a@," St.Clause.pp_dimacs vec @@ -66,7 +66,7 @@ module Make(St : Solver_types_intf.S) = struct end let filter_vec learnt = - let lemmas = Vec.make (Vec.size learnt) St.Clause.dummy in + let lemmas = Vec.create() in Vec.iter (fun c -> match map_filter_learnt c with | None -> () diff --git a/src/core/Expr_intf.ml b/src/core/Expr_intf.ml index 172ab472..4536d36c 100644 --- a/src/core/Expr_intf.ml +++ b/src/core/Expr_intf.ml @@ -55,9 +55,6 @@ module type S = sig val print : Format.formatter -> t -> unit (** Printing function used among other thing for debugging. *) - val dummy : t - (** Constant formula. A valid formula should never be physically equal to [dummy] *) - val neg : t -> t (** Formula negation *) diff --git a/src/core/Formula_intf.ml b/src/core/Formula_intf.ml index fe45e908..edf8b3ff 100644 --- a/src/core/Formula_intf.ml +++ b/src/core/Formula_intf.ml @@ -35,9 +35,6 @@ module type S = sig val print : Format.formatter -> t -> unit (** Printing function used among other thing for debugging. *) - val dummy : t - (** Formula constant. A valid formula should never be physically equal to [dummy] *) - val neg : t -> t (** Formula negation. Should be an involution, i.e. [equal a (neg neg a)] should always hold. *) diff --git a/src/core/Heap.ml b/src/core/Heap.ml index 1e309ab1..175a4007 100644 --- a/src/core/Heap.ml +++ b/src/core/Heap.ml @@ -8,12 +8,12 @@ module Make(Elt : RANKED) = struct type t = { heap : elt Vec.t; - } + } [@@unboxed] let _absent_index = -1 let create () = - { heap = Vec.make_empty Elt.dummy; } + { heap = Vec.create(); } let[@inline] left i = (i lsl 1) + 1 (* i*2 + 1 *) let[@inline] right i = (i + 1) lsl 1 (* (i+1)*2 *) @@ -109,9 +109,6 @@ module Make(Elt : RANKED) = struct percolate_up s elt; ) - let[@inline] grow_to_at_least s sz = - Vec.grow_to_at_least s.heap sz - (* let update cmp s n = assert (heap_property cmp s); @@ -130,10 +127,9 @@ module Make(Elt : RANKED) = struct if Vec.size heap=0 then raise Not_found; let x = Vec.get heap 0 in Elt.set_idx x _absent_index; - let new_hd = Vec.last heap in (* heap.last() *) + let new_hd = Vec.pop heap in (* new head *) Vec.set heap 0 new_hd; Elt.set_idx new_hd 0; - Vec.pop heap; (* remove last *) (* enforce heap property again *) if Vec.size heap > 1 then ( percolate_down s new_hd; diff --git a/src/core/Heap_intf.ml b/src/core/Heap_intf.ml index a5d11100..bee623e6 100644 --- a/src/core/Heap_intf.ml +++ b/src/core/Heap_intf.ml @@ -3,7 +3,6 @@ module type RANKED = sig type t val idx: t -> int (** Index in heap. return -1 if never set *) val set_idx : t -> int -> unit (** Update index in heap *) - val dummy : t val cmp : t -> t -> bool end @@ -36,10 +35,6 @@ module type S = sig val insert : t -> elt -> unit (** Insert a new element into the heap *) - val grow_to_at_least: t -> int -> unit - (** Hint: augment the internal capacity of the heap until it reaches at - least the given integer *) - (*val update : (int -> int -> bool) -> t -> int -> unit*) val remove_min : t -> elt diff --git a/src/core/Internal.ml b/src/core/Internal.ml index a613820a..2fcf5645 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -17,7 +17,6 @@ module Make module H = Heap.Make(struct type t = St.Elt.t let[@inline] cmp i j = Elt.weight j < Elt.weight i (* comparison by weight *) - let dummy = Elt.of_var St.Var.dummy let idx = Elt.idx let set_idx = Elt.set_idx end) @@ -122,14 +121,14 @@ module Make } (* Starting environment. *) - let create_ ~st ~size_trail ~size_lvl () : t = { + let create_ ~st () : t = { st; unsat_conflict = None; next_decision = None; - clauses_hyps = Vec.make 0 Clause.dummy; - clauses_learnt = Vec.make 0 Clause.dummy; - clauses_temp = Vec.make 0 Clause.dummy; + clauses_hyps = Vec.create(); + clauses_learnt = Vec.create(); + clauses_temp = Vec.create(); clauses_root = Stack.create (); clauses_to_add = Stack.create (); @@ -137,10 +136,10 @@ module Make th_head = 0; elt_head = 0; - trail = Vec.make size_trail (Trail_elt.of_atom Atom.dummy); - elt_levels = Vec.make size_lvl (-1); - th_levels = Vec.make size_lvl Plugin.dummy; - user_levels = Vec.make 0 (-1); + trail = Vec.create (); + elt_levels = Vec.create(); + th_levels = Vec.create(); + user_levels = Vec.create(); order = H.create(); @@ -153,13 +152,9 @@ module Make dirty=false; } - let create ?(size=`Big) ?st () : t = - let st = match st with Some s -> s | None -> St.create ~size () in - let size_trail, size_lvl = match size with - | `Tiny -> 0, 0 - | `Small -> 32, 16 - | `Big -> 600, 50 - in create_ ~st ~size_trail ~size_lvl () + let create ?(size=`Big) () : t = + let st = St.create ~size () in + create_ ~st () (* Misc functions *) let to_float = float_of_int @@ -504,8 +499,8 @@ module Make Log.debugf error (fun k -> k "@[Failed at reason simplification:@,%a@,%a@]" - (Vec.print ~sep:"" Atom.debug) - (Vec.from_list l Atom.dummy) + (Vec.pp ~sep:"" Atom.debug) + (Vec.of_list l) Clause.debug cl); assert false end @@ -537,7 +532,6 @@ module Make let l = List.map (Lit.make st.st) terms in let lvl = List.fold_left (fun acc {l_level; _} -> assert (l_level > 0); max acc l_level) 0 l in - H.grow_to_at_least st.order (St.nb_elt st.st); enqueue_bool st a ~level:lvl Semantic ) @@ -864,8 +858,6 @@ module Make let flush_clauses st = if not (Stack.is_empty st.clauses_to_add) then ( - let nbv = St.nb_elt st.st in - H.grow_to_at_least st.order nbv; while not (Stack.is_empty st.clauses_to_add) do let c = Stack.pop st.clauses_to_add in add_clause st c @@ -986,7 +978,6 @@ module Make else if p.neg.is_true then ( Stack.push c st.clauses_to_add ) else ( - H.grow_to_at_least st.order (St.nb_elt st.st); insert_subterms_order st p.var; let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in enqueue_bool st p ~level (Bcp c) @@ -1033,7 +1024,6 @@ module Make if not @@ List.for_all (fun a -> a.neg.is_true) l then ( raise (Invalid_argument "msat:core/internal: invalid conflict"); ); - H.grow_to_at_least st.order (St.nb_elt st.st); List.iter (fun a -> insert_var_order st (Elt.of_var a.var)) l; (* Create the clause and return it. *) let c = St.Clause.make l (Lemma p) in @@ -1215,14 +1205,14 @@ module Make cancel_until st (base_level st); Log.debugf debug (fun k -> k "@[Status:@,@[trail: %d - %d@,%a@]" - st.elt_head st.th_head (Vec.print ~sep:"" Trail_elt.debug) st.trail); + st.elt_head st.th_head (Vec.pp ~sep:"" Trail_elt.debug) st.trail); begin match propagate st with | Some confl -> report_unsat st confl | None -> Log.debugf debug (fun k -> k "@[Current trail:@,@[%a@]@]" - (Vec.print ~sep:"" Trail_elt.debug) st.trail); + (Vec.pp ~sep:"" Trail_elt.debug) st.trail); Log.debug info "Creating new user level"; new_decision_level st; Vec.push st.user_levels (Vec.size st.clauses_temp); @@ -1237,8 +1227,7 @@ module Make Log.debug info "Popping user level"; assert (base_level st > 0); st.unsat_conflict <- None; - let n = Vec.last st.user_levels in - Vec.pop st.user_levels; (* before the [cancel_until]! *) + let n = Vec.pop st.user_levels in (* before the [cancel_until]! *) (* Add the root clauses to the clauses to add *) Stack.iter (fun c -> Stack.push c st.clauses_to_add) st.clauses_root; Stack.clear st.clauses_root; @@ -1263,9 +1252,6 @@ module Make (* conflict between assumptions: UNSAT *) report_unsat st c; ) else ( - (* Grow the heap, because when the lit is backtracked, - it will be added to the heap. *) - H.grow_to_at_least st.order (St.nb_elt st.st); (* make a decision, propagate *) let level = decision_level st in enqueue_bool st a ~level (Bcp c); diff --git a/src/core/Solver.ml b/src/core/Solver.ml index 883636f0..bc55b0af 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -31,7 +31,7 @@ module Make type t = S.t type solver = t - let[@inline] create ?size () = S.create ?size () + let create = S.create (* Result type *) type res = @@ -44,10 +44,10 @@ module Make "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ @[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." status - (Vec.print ~sep:"" St.Trail_elt.debug) (S.trail st) - (Vec.print ~sep:"" St.Clause.debug) (S.temp st) - (Vec.print ~sep:"" St.Clause.debug) (S.hyps st) - (Vec.print ~sep:"" St.Clause.debug) (S.history st) + (Vec.pp ~sep:"" St.Trail_elt.debug) (S.trail st) + (Vec.pp ~sep:"" St.Clause.debug) (S.temp st) + (Vec.pp ~sep:"" St.Clause.debug) (S.hyps st) + (Vec.pp ~sep:"" St.Clause.debug) (S.history st) ) let mk_sat (st:S.t) : (_,_) sat_state = diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index add513cf..9f3a4f2d 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -24,8 +24,6 @@ module McMake (E : Expr_intf.S) = struct type formula = E.Formula.t type proof = E.proof - let pp_form = E.Formula.dummy - type seen = | Nope | Both @@ -91,37 +89,6 @@ module McMake (E : Expr_intf.S) = struct | Lit of lit | Atom of atom - let rec dummy_var = - { vid = -101; - pa = dummy_atom; - na = dummy_atom; - v_fields = Var_fields.empty; - v_level = -1; - v_weight = -1.; - v_idx= -1; - v_assignable = None; - reason = None; - } - and dummy_atom = - { var = dummy_var; - lit = E.Formula.dummy; - watched = Obj.magic 0; - (* should be [Vec.make_empty dummy_clause] - but we have to break the cycle *) - neg = dummy_atom; - is_true = false; - aid = -102 } - let dummy_clause = - { name = -1; - tag = None; - atoms = [| |]; - activity = -1.; - attached = false; - visited = false; - cpremise = History [] } - - let () = dummy_atom.watched <- Vec.make_empty dummy_clause - (* Constructors *) module MF = Hashtbl.Make(E.Formula) module MT = Hashtbl.Make(E.Term) @@ -136,21 +103,21 @@ module McMake (E : Expr_intf.S) = struct type state = t - let create_ size_map size_vars () : t = { + let create_ size_map () : t = { f_map = MF.create size_map; t_map = MT.create size_map; - vars = Vec.make size_vars (E_var dummy_var); + vars = Vec.create(); cpt_mk_var = 0; cpt_mk_clause = 0; } let create ?(size=`Big) () : t = - let size_map, size_vars = match size with - | `Tiny -> 8, 0 - | `Small -> 16, 10 - | `Big -> 4096, 128 + let size_map = match size with + | `Tiny -> 8 + | `Small -> 16 + | `Big -> 4096 in - create_ size_map size_vars () + create_ size_map () let nb_elt st = Vec.size st.vars let get_elt st i = Vec.get st.vars i @@ -200,7 +167,6 @@ module McMake (E : Expr_intf.S) = struct module Var = struct type t = var - let dummy = dummy_var let[@inline] level v = v.v_level let[@inline] pos v = v.pa let[@inline] neg v = v.na @@ -228,14 +194,14 @@ module McMake (E : Expr_intf.S) = struct and pa = { var = var; lit = lit; - watched = Vec.make 10 dummy_clause; + watched = Vec.create(); neg = na; is_true = false; aid = cpt_double (* aid = vid*2 *) } and na = { var = var; lit = E.Formula.neg lit; - watched = Vec.make 10 dummy_clause; + watched = Vec.create(); neg = pa; is_true = false; aid = cpt_double + 1 (* aid = vid*2+1 *) } in @@ -255,7 +221,6 @@ module McMake (E : Expr_intf.S) = struct module Atom = struct type t = atom - let dummy = dummy_atom let[@inline] level a = a.var.v_level let[@inline] var a = a.var let[@inline] neg a = a.neg @@ -370,7 +335,6 @@ module McMake (E : Expr_intf.S) = struct module Clause = struct type t = clause - let dummy = dummy_clause let make = let n = ref 0 in diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 4a1816be..61d1727a 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -156,7 +156,6 @@ module type S = sig module Var : sig type t = var - val dummy : t val pos : t -> atom val neg : t -> atom @@ -180,7 +179,6 @@ module type S = sig module Atom : sig type t = atom - val dummy : t val level : t -> int val reason : t -> reason option val lit : t -> formula @@ -227,7 +225,6 @@ module type S = sig module Clause : sig type t = clause - val dummy : t val name : t -> string val equal : t -> t -> bool diff --git a/src/core/Theory_intf.ml b/src/core/Theory_intf.ml index 7a6621d8..1cb7e672 100644 --- a/src/core/Theory_intf.ml +++ b/src/core/Theory_intf.ml @@ -49,9 +49,6 @@ module type S = sig type level (** The type for levels to allow backtracking. *) - val dummy : level - (** A dummy level. *) - val current_level : unit -> level (** Return the current level of the theory (either the empty/beginning state, or the last level returned by the [assume] function). *) @@ -76,7 +73,6 @@ module Dummy(F: Formula_intf.S) type formula = F.t type proof = unit type level = unit - let dummy = () let current_level () = () let assume _ = Sat let if_sat _ = Sat diff --git a/src/core/Vec.ml b/src/core/Vec.ml index c8c68f84..6223dfd9 100644 --- a/src/core/Vec.ml +++ b/src/core/Vec.ml @@ -1,49 +1,12 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) type 'a t = { - mutable dummy: 'a; mutable data : 'a array; mutable sz : int; } -let _size_too_big()= - failwith "Vec: capacity exceeds maximum array size" +let make n x = {data=Array.make n x; sz=0} -let make capa d = - if capa > Sys.max_array_length then _size_too_big(); - {data = Array.make capa d; sz = 0; dummy = d} - -let[@inline] make_empty d = {data = [||]; sz=0; dummy=d } - -let init capa f d = - if capa > Sys.max_array_length then _size_too_big(); - {data = Array.init capa (fun i -> f i); sz = capa; dummy = d} - -let from_array data sz d = - assert (sz <= Array.length data); - {data = data; sz = sz; dummy = d} - -let from_list l d = - let a = Array.of_list l in - from_array a (Array.length a) d - -let to_list s = - let l = ref [] in - for i = 0 to s.sz - 1 do - l := s.data.(i) :: !l - done; - List.rev !l +let[@inline] create () = {data = [||]; sz = 0} let[@inline] clear s = s.sz <- 0 @@ -54,159 +17,96 @@ let[@inline] shrink t i = let[@inline] pop t = if t.sz = 0 then invalid_arg "vec.pop"; - t.sz <- t.sz - 1 + let x = Array.unsafe_get t.data (t.sz - 1) in + t.sz <- t.sz - 1; + x let[@inline] size t = t.sz let[@inline] is_empty t = t.sz = 0 -let grow_to_exact t new_capa = - assert (new_capa > Array.length t.data); - let new_data = Array.make new_capa t.dummy in - assert (t.sz <= new_capa); - Array.blit t.data 0 new_data 0 t.sz; - t.data <- new_data - -let grow_to_double_size t = - if Array.length t.data = Sys.max_array_length then _size_too_big(); - let size = min Sys.max_array_length (2* Array.length t.data + 1) in - grow_to_exact t size - -let grow_to_at_least t new_capa = - assert (new_capa >= 0); - if new_capa > Sys.max_array_length then _size_too_big (); - let data = t.data in - let capa = ref (max (Array.length data) 1) in - while !capa < new_capa do - capa := min (2 * !capa + 1) Sys.max_array_length; - done; - if !capa > Array.length data then ( - grow_to_exact t !capa - ) - let[@inline] is_full t = Array.length t.data = t.sz -let[@inline] push t e = - if is_full t then grow_to_double_size t; - t.data.(t.sz) <- e; +let[@inline] copy t : _ t = + let data = Array.copy t.data in + {t with data} + +(* grow the array *) +let[@inline never] grow_to_double_size t x : unit = + if Array.length t.data = Sys.max_array_length then ( + failwith "vec: cannot resize"; + ); + let size = + min Sys.max_array_length (max 4 (2 * Array.length t.data)) + in + let arr' = Array.make size x in + Array.blit t.data 0 arr' 0 (Array.length t.data); + t.data <- arr'; + assert (Array.length t.data > t.sz); + () + +let[@inline] push t x : unit = + if is_full t then grow_to_double_size t x; + Array.unsafe_set t.data t.sz x; t.sz <- t.sz + 1 -let[@inline] last t = - if t.sz = 0 then invalid_arg "vec.last"; - t.data.(t.sz - 1) - -let[@inline] pop_last t = - if t.sz = 0 then invalid_arg "vec.pop_last"; - let x = t.data.(t.sz - 1) in - t.sz <- t.sz - 1; - x - let[@inline] get t i = if i < 0 || i >= t.sz then invalid_arg "vec.get"; Array.unsafe_get t.data i let[@inline] set t i v = if i < 0 || i > t.sz then invalid_arg "vec.set"; - if i = t.sz then + if i = t.sz then ( push t v - else + ) else ( Array.unsafe_set t.data i v - -let[@inline] copy t = - let data = Array.copy t.data in - {t with data; } - -let[@inline] move_to t t' = - t'.data <- Array.copy t.data; - t'.sz <- t.sz - -let remove t e = - let j = ref 0 in - while (!j < t.sz && not (t.data.(!j) == e)) do incr j done; - assert (!j < t.sz); - for i = !j to t.sz - 2 do t.data.(i) <- t.data.(i+1) done; - pop t + ) let[@inline] fast_remove t i = - assert (i < t.sz); - t.data.(i) <- t.data.(t.sz - 1); + assert (i>= 0 && i < t.sz); + Array.unsafe_set t.data i @@ Array.unsafe_get t.data (t.sz - 1); t.sz <- t.sz - 1 let filter_in_place f vec = let i = ref 0 in while !i < size vec do - if f (get vec !i) then incr i else fast_remove vec !i + if f (Array.unsafe_get vec.data !i) then incr i else fast_remove vec !i done -let sort t f = - let sub_arr = Array.sub t.data 0 t.sz in +let sort t f : unit = + let sub_arr = if is_full t then t.data else Array.sub t.data 0 t.sz in Array.fast_sort f sub_arr; t.data <- sub_arr -let iter f t = +let[@inline] iter f t = for i = 0 to size t - 1 do f (Array.unsafe_get t.data i) done -let append a b = - grow_to_at_least a (size a + size b); - iter (push a) b +let[@inline] iteri f t = + for i = 0 to size t - 1 do + f i (Array.unsafe_get t.data i) + done -let fold f acc t = - let rec _fold f acc t i = - if i=t.sz - then acc - else ( - let acc' = f acc (Array.unsafe_get t.data i) in - _fold f acc' t (i+1) - ) - in _fold f acc t 0 +let[@inline] to_seq a k = iter k a -exception ExitVec +let exists p t = Sequence.exists p @@ to_seq t +let for_all p t = Sequence.for_all p @@ to_seq t +let fold f acc a = Sequence.fold f acc @@ to_seq a +let to_list a = Sequence.to_list @@ to_seq a -let exists p t = - try - for i = 0 to t.sz - 1 do - if p (Array.unsafe_get t.data i) then raise ExitVec - done; - false - with ExitVec -> true +let of_list l : _ t = + match l with + | [] -> create() + | x :: tl -> + let v = make (List.length tl+1) x in + List.iter (push v) l; + v -let for_all p t = - try - for i = 0 to t.sz - 1 do - if not (p (Array.unsafe_get t.data i)) then raise ExitVec - done; - true - with ExitVec -> false - -let print ?(sep=", ") pp out v = +let pp ?(sep=", ") pp out v = let first = ref true in iter (fun x -> if !first then first := false else Format.fprintf out "%s@," sep; pp out x) v - -(* -template -static inline void remove(V& ts, const T& t) -{ - int j = 0; - for (; j < ts.size() && ts[j] != t; j++); - assert(j < ts.size()); - ts[j] = ts.last(); - ts.pop(); -} -#endif - -template -static inline bool find(V& ts, const T& t) -{ - int j = 0; - for (; j < ts.size() && ts[j] != t; j++); - return j < ts.size(); -} - -#endif -*) diff --git a/src/core/Vec.mli b/src/core/Vec.mli index 914d4eba..b35e0f04 100644 --- a/src/core/Vec.mli +++ b/src/core/Vec.mli @@ -1,15 +1,3 @@ -(**************************************************************************) -(* *) -(* Cubicle *) -(* Combining model checking algorithms and SMT solvers *) -(* *) -(* Mohamed Iguernelala *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) type 'a t (** Abstract type of vectors of 'a *) @@ -19,22 +7,15 @@ val make : int -> 'a -> 'a t is initially empty but its underlying array has capacity [cap]. [dummy] will stay alive as long as the vector *) -val make_empty : 'a -> 'a t -(** Vector with an empty capacity. The only argument is the dummy. *) - -val init : int -> (int -> 'a) -> 'a -> 'a t -(** Same as {!Array.init}, but also with a dummy element *) - -val from_array : 'a array -> int -> 'a -> 'a t -(** [from_array arr size dummy] takes ownership of [data] (no copy) - to create a vector. [size] is the length of the slice of [data] that is - used ([size <= Array.length data] must hold) *) - -val from_list : 'a list -> 'a -> 'a t +val create : unit -> 'a t val to_list : 'a t -> 'a list (** Returns the list of elements of the vector *) +val of_list : 'a list -> 'a t + +val to_seq : 'a t -> 'a Sequence.t + val clear : 'a t -> unit (** Set size to 0, doesn't free elements *) @@ -42,37 +23,19 @@ val shrink : 'a t -> int -> unit (** [shrink vec sz] resets size of [vec] to [sz]. Assumes [sz >=0 && sz <= size vec] *) -val pop : 'a t -> unit -(** Pop last element +val pop : 'a t -> 'a +(** Pop last element and return it. @raise Invalid_argument if the vector is empty *) val size : 'a t -> int val is_empty : 'a t -> bool -val grow_to_exact : 'a t -> int -> unit - -val grow_to_double_size : 'a t -> unit - -val grow_to_at_least : 'a t -> int -> unit -(** [grow_to_at_least vec n] ensures that [capacity vec >= n] in - the most efficient way *) - val is_full : 'a t -> bool (** Is the capacity of the vector equal to the number of its elements? *) val push : 'a t -> 'a -> unit - -val append : 'a t -> 'a t -> unit -(** [append v1 v2] pushes all elements of [v2] into [v1] *) - -val last : 'a t -> 'a -(** Last element, or - @raise Invalid_argument if the vector is empty *) - -val pop_last : 'a t -> 'a -(** Combine {!last} and {!pop}: remove last element and return it - @raise Invalid_argument if empty *) +(** Push element into the vector *) val get : 'a t -> int -> 'a (** get the element at the given index, or @@ -86,12 +49,6 @@ val set : 'a t -> int -> 'a -> unit val copy : 'a t -> 'a t (** Fresh copy *) -val move_to : 'a t -> 'a t -> unit -(** [move_to a b] copies the content of [a] to [b], discarding [b]'s old content *) - -val remove : 'a t -> 'a -> unit -(** Uses [(==)] for comparison *) - val fast_remove : 'a t -> int -> unit (** Remove element at index [i] without preserving order (swap with last element) *) @@ -106,6 +63,9 @@ val sort : 'a t -> ('a -> 'a -> int) -> unit val iter : ('a -> unit) -> 'a t -> unit (** Iterate on elements *) +val iteri : (int -> 'a -> unit) -> 'a t -> unit +(** Iterate on elements with their index *) + val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b (** Fold over elements *) @@ -115,7 +75,7 @@ val exists : ('a -> bool) -> 'a t -> bool val for_all : ('a -> bool) -> 'a t -> bool (** Do all elements satisfy the predicate? *) -val print : +val pp : ?sep:string -> (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit diff --git a/src/core/dune b/src/core/dune index c72f5475..e5c561a3 100644 --- a/src/core/dune +++ b/src/core/dune @@ -2,6 +2,7 @@ (library (name msat) (public_name msat) + (libraries sequence) (synopsis "core data structures and algorithms for msat") (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot diff --git a/src/sat/Expr_sat.ml b/src/sat/Expr_sat.ml index efb92778..86fc6242 100644 --- a/src/sat/Expr_sat.ml +++ b/src/sat/Expr_sat.ml @@ -26,9 +26,6 @@ let _make i = end else raise Bad_atom -(** A dummy atom *) -let dummy = 0 - (** *) let neg a = - a From 8b4458b066835d2bcf5b97446ee39ab50e37816c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 20:14:47 -0600 Subject: [PATCH 053/182] refactor(api): make theory state also explicit --- src/core/Heap.ml | 2 +- src/core/Internal.ml | 31 ++++++++++++++++++------------- src/core/Plugin_intf.ml | 37 +++++++++++++++++++------------------ src/core/Solver.ml | 1 + src/core/Solver.mli | 1 + src/core/Solver_intf.ml | 5 ++++- src/core/Theory_intf.ml | 21 ++++++++++++--------- src/sat/Msat_sat.mli | 2 +- 8 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/core/Heap.ml b/src/core/Heap.ml index 175a4007..08aab303 100644 --- a/src/core/Heap.ml +++ b/src/core/Heap.ml @@ -136,4 +136,4 @@ module Make(Elt : RANKED) = struct ); x -end +end [@@inline] diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 2fcf5645..e5bbddf9 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -4,6 +4,8 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) +(* TODO: move solver types here *) + module Make (St : Solver_types.S) (Plugin : Plugin_intf.S with type term = St.term @@ -13,8 +15,9 @@ module Make module Proof = Res.Make(St) open St + type theory = Plugin.t - module H = Heap.Make(struct + module H = (Heap.Make [@specialise]) (struct type t = St.Elt.t let[@inline] cmp i j = Elt.weight j < Elt.weight i (* comparison by weight *) let idx = Elt.idx @@ -49,6 +52,8 @@ module Make type t = { st : St.t; + th: Plugin.t; + (* Clauses are simplified for eficiency purposes. In the following vectors, the comments actually refer to the original non-simplified clause. *) @@ -121,8 +126,8 @@ module Make } (* Starting environment. *) - let create_ ~st () : t = { - st; + let create_ ~st (th:theory) : t = { + st; th; unsat_conflict = None; next_decision = None; @@ -152,9 +157,9 @@ module Make dirty=false; } - let create ?(size=`Big) () : t = + let create ?(size=`Big) (th:theory) : t = let st = St.create ~size () in - create_ ~st () + create_ ~st th (* Misc functions *) let to_float = float_of_int @@ -195,7 +200,7 @@ module Make | Some _ -> () | None -> let l = ref [] in - Plugin.iter_assignable + Plugin.iter_assignable st.th (fun t -> l := Lit.make st.st t :: !l) res.var.pa.lit; res.var.v_assignable <- Some !l; @@ -386,7 +391,7 @@ module Make assert (st.th_head = Vec.size st.trail); assert (st.elt_head = Vec.size st.trail); Vec.push st.elt_levels (Vec.size st.trail); - Vec.push st.th_levels (Plugin.current_level ()); (* save the current theory state *) + Vec.push st.th_levels (Plugin.current_level st.th); (* save the current theory state *) () (* Attach/Detach a clause. @@ -454,7 +459,7 @@ module Make ) done; (* Recover the right theory state. *) - Plugin.backtrack (Vec.get st.th_levels lvl); + Plugin.backtrack st.th (Vec.get st.th_levels lvl); (* Resize the vectors according to their new size. *) Vec.shrink st.trail !head; Vec.shrink st.elt_levels lvl; @@ -582,7 +587,7 @@ module Make by boolean propagation/decision *) let th_eval st a : bool option = if a.is_true || a.neg.is_true then None - else match Plugin.eval a.lit with + else match Plugin.eval st.th a.lit with | Plugin_intf.Unknown -> None | Plugin_intf.Valued (b, l) -> if l = [] then @@ -1014,7 +1019,7 @@ module Make ) else ( let slice = current_slice st in st.th_head <- st.elt_head; (* catch up *) - match Plugin.assume slice with + match Plugin.assume st.th slice with | Plugin_intf.Sat -> propagate st | Plugin_intf.Unsat (l, p) -> @@ -1067,7 +1072,7 @@ module Make if v.v_level >= 0 then ( assert (v.pa.is_true || v.na.is_true); pick_branch_lit st - ) else match Plugin.eval atom.lit with + ) else match Plugin.eval st.th atom.lit with | Plugin_intf.Unknown -> new_decision_level st; let current_level = decision_level st in @@ -1087,7 +1092,7 @@ module Make if Lit.level l >= 0 then pick_branch_lit st else ( - let value = Plugin.assign l.term in + let value = Plugin.assign st.th l.term in new_decision_level st; let current_level = decision_level st in enqueue_assign st l value current_level @@ -1174,7 +1179,7 @@ module Make n_of_learnts := !n_of_learnts *. learntsize_inc | Sat -> assert (st.elt_head = Vec.size st.trail); - begin match Plugin.if_sat (full_slice st) with + begin match Plugin.if_sat st.th (full_slice st) with | Plugin_intf.Sat -> () | Plugin_intf.Unsat (l, p) -> let atoms = List.rev_map (create_atom st) l in diff --git a/src/core/Plugin_intf.ml b/src/core/Plugin_intf.ml index 51a4da72..98e87f53 100644 --- a/src/core/Plugin_intf.ml +++ b/src/core/Plugin_intf.ml @@ -60,8 +60,11 @@ type ('term, 'formula, 'proof) slice = { } (** The type for a slice of assertions to assume/propagate in the theory. *) +(** Signature for theories to be given to the Model Constructing Solver. *) module type S = sig - (** Signature for theories to be given to the Model Constructing Solver. *) + + type t + (** The plugin state itself *) type term (** The type of terms. Should be compatible with Expr_intf.Term.t*) @@ -75,33 +78,30 @@ module type S = sig type level (** The type for levels to allow backtracking. *) - val dummy : level - (** A dummy level. *) - - val current_level : unit -> level + val current_level : t -> level (** Return the current level of the theory (either the empty/beginning state, or the last level returned by the [assume] function). *) - val assume : (term, formula, proof) slice -> (formula, proof) res + val assume : t -> (term, formula, proof) slice -> (formula, proof) res (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, and returns the result of the new assumptions. *) - val if_sat : (term, formula, proof) slice -> (formula, proof) res + val if_sat : t -> (term, formula, proof) slice -> (formula, proof) res (** Called at the end of the search in case a model has been found. If no new clause is pushed and the function returns [Sat], then proof search ends and 'sat' is returned, else search is resumed. *) - val backtrack : level -> unit + val backtrack : t -> level -> unit (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the same state as when it returned the value [l], *) - val assign : term -> term + val assign : t -> term -> term (** Returns an assignment value for the given term. *) - val iter_assignable : (term -> unit) -> formula -> unit + val iter_assignable : t -> (term -> unit) -> formula -> unit (** An iterator over the subterms of a formula that should be assigned a value (usually the poure subterms) *) - val eval : formula -> term eval_res + val eval : t -> formula -> term eval_res (** Returns the evaluation of the formula in the current assignment *) end @@ -110,18 +110,19 @@ module Dummy(F: Solver_types.S) : S with type formula = F.formula and type term = F.term and type proof = F.proof + and type t = unit = struct + type t = unit type formula = F.formula type term = F.term type proof = F.proof type level = unit - let dummy = () let current_level () = () - let assume _ = Sat - let if_sat _ = Sat - let backtrack _ = () - let eval _ = Unknown - let assign t = t + let assume () _ = Sat + let if_sat () _ = Sat + let backtrack () _ = () + let eval () _ = Unknown + let assign () t = t let mcsat = false - let iter_assignable _ _ = () + let iter_assignable () _ _ = () end diff --git a/src/core/Solver.ml b/src/core/Solver.ml index bc55b0af..6d788347 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -27,6 +27,7 @@ module Make type term = St.term type atom = St.formula type clause = St.clause + type theory = Th.t type t = S.t type solver = t diff --git a/src/core/Solver.mli b/src/core/Solver.mli index 3272a62b..69441e0d 100644 --- a/src/core/Solver.mli +++ b/src/core/Solver.mli @@ -22,6 +22,7 @@ module Make and type formula = St.formula and type clause = St.clause and type Proof.lemma = St.proof + and type theory = Th.t (** Functor to make a safe external interface. *) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index aebfe2b2..a28cb248 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -60,14 +60,17 @@ module type S = sig type clause + type theory + module Proof : Res.S with type clause = clause (** A module to manipulate proofs. *) type t (** Main solver type, containing all state for solving. *) - val create : ?size:[`Tiny|`Small|`Big] -> unit -> t + val create : ?size:[`Tiny|`Small|`Big] -> theory -> t (** Create new solver + @param theory the theory @param size the initial size of internal data structures. The bigger, the faster, but also the more RAM it uses. *) diff --git a/src/core/Theory_intf.ml b/src/core/Theory_intf.ml index 1cb7e672..d377509d 100644 --- a/src/core/Theory_intf.ml +++ b/src/core/Theory_intf.ml @@ -37,8 +37,10 @@ type ('form, 'proof) slice = { propagation queue. They allow to look at the propagated literals, and to add new clauses to the solver. *) +(** Signature for theories to be given to the Solver. *) module type S = sig - (** Signature for theories to be given to the Solver. *) + type t + (** The state of the theory itself *) type formula (** The type of formulas. Should be compatble with Formula_intf.S *) @@ -49,32 +51,33 @@ module type S = sig type level (** The type for levels to allow backtracking. *) - val current_level : unit -> level + val current_level : t -> level (** Return the current level of the theory (either the empty/beginning state, or the last level returned by the [assume] function). *) - val assume : (formula, proof) slice -> (formula, proof) res + val assume : t -> (formula, proof) slice -> (formula, proof) res (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, and returns the result of the new assumptions. *) - val if_sat : (formula, proof) slice -> (formula, proof) res + val if_sat : t -> (formula, proof) slice -> (formula, proof) res (** Called at the end of the search in case a model has been found. If no new clause is pushed, then 'sat' is returned, else search is resumed. *) - val backtrack : level -> unit + val backtrack : t -> level -> unit (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the same state as when it returned the value [l], *) end module Dummy(F: Formula_intf.S) - : S with type formula = F.t + : S with type formula = F.t and type t = unit = struct + type t = unit type formula = F.t type proof = unit type level = unit let current_level () = () - let assume _ = Sat - let if_sat _ = Sat - let backtrack _ = () + let assume () _ = Sat + let if_sat () _ = Sat + let backtrack () _ = () end diff --git a/src/sat/Msat_sat.mli b/src/sat/Msat_sat.mli index c28f5032..95695269 100644 --- a/src/sat/Msat_sat.mli +++ b/src/sat/Msat_sat.mli @@ -11,6 +11,6 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat -include Msat.S with type formula = Expr.t +include Msat.S with type formula = Expr.t and type theory = unit (** A functor that can generate as many solvers as needed. *) From 7e9fd1a363d20d03281e8ead7a43c475601da764 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 20:19:23 -0600 Subject: [PATCH 054/182] perf: remove bitfield, implement it manually --- src/core/BitField.ml | 135 ---------------------------------- src/core/BitField.mli | 69 ----------------- src/core/Solver_types.ml | 31 ++++---- src/core/Solver_types.mli | 2 - src/core/Solver_types_intf.ml | 4 +- 5 files changed, 16 insertions(+), 225 deletions(-) delete mode 100644 src/core/BitField.ml delete mode 100644 src/core/BitField.mli diff --git a/src/core/BitField.ml b/src/core/BitField.ml deleted file mode 100644 index 697320a8..00000000 --- a/src/core/BitField.ml +++ /dev/null @@ -1,135 +0,0 @@ -(* This file is free software, part of containers. See file "license" for more details. *) - -(** {1 Bit Field} *) - -exception TooManyFields -exception Frozen - -let max_width = Sys.word_size - 2 - -(*$R - let module B = CCBitField.Make(struct end) in - let x = B.mk_field () in - let y = B.mk_field () in - let z = B.mk_field () in - - let f = B.empty |> B.set x true |> B.set y true in - - assert_bool "z_false" (not (B.get z f)) ; - - assert_bool "z_true" (f |> B.set z true |> B.get z); -*) - -(*$R - let module B = CCBitField.Make(struct end) in - let _ = B.mk_field () in - B.freeze(); - assert_bool "must raise" - (try ignore (B.mk_field()); false with Frozen -> true); - -*) - -(*$R - let module B = CCBitField.Make(struct end) in - - let x = B.mk_field () in - let y = B.mk_field () in - let z = B.mk_field () in - let u = B.mk_field () in - B.freeze(); - - let f = B.empty - |> B.set y true - |> B.set z true - in - - assert_equal ~printer:CCInt.to_string 6 (f :> int) ; - - assert_equal false (B.get x f) ; - assert_equal true (B.get y f) ; - assert_equal true (B.get z f); - - let f' = B.set u true f in - - assert_equal false (B.get x f') ; - assert_equal true (B.get y f') ; - assert_equal true (B.get z f'); - assert_equal true (B.get u f'); - - () -*) - - -module type S = sig - type t = private int - (** Generative type of bitfields. Each instantiation of the functor - should create a new, incompatible type *) - - val empty : t - (** Empty bitfields (all bits 0) *) - - type field - - val get : field -> t -> bool - (** Get the value of this field *) - - val set : field -> bool -> t -> t - (** Set the value of this field *) - - val mk_field : unit -> field - (** Make a new field *) - - val freeze : unit -> unit - (** Prevent new fields from being added. From now on, creating - a field will raise Frozen *) - - val total_width : unit -> int - (** Current width of the bitfield *) -end - -(* all bits from 0 to w-1 set to true *) -let rec all_bits_ acc w = - if w=0 then acc - else - let acc = acc lor (1 lsl w-1) in - all_bits_ acc (w-1) - -(*$T - all_bits_ 0 1 = 1 - all_bits_ 0 2 = 3 - all_bits_ 0 3 = 7 - all_bits_ 0 4 = 15 -*) - -(* increment and return previous value *) -let get_then_incr n = - let x = !n in - incr n; - x - -module Make() : S = struct - type t = int - - let empty = 0 - - let width_ = ref 0 - let frozen_ = ref false - - type field = int (* a mask *) - - let[@inline] get field x = (x land field) <> 0 - - let[@inline] set field b x = - if b then x lor field else x land (lnot field) - - let mk_field () = - if !frozen_ then raise Frozen; - let n = get_then_incr width_ in - if n > max_width then raise TooManyFields; - let mask = 1 lsl n in - mask - - let freeze () = frozen_ := true - - let total_width () = !width_ -end diff --git a/src/core/BitField.mli b/src/core/BitField.mli deleted file mode 100644 index ac92ffd8..00000000 --- a/src/core/BitField.mli +++ /dev/null @@ -1,69 +0,0 @@ -(* This file is free software, part of containers. See file "license" for more details. *) - -(** {1 Bit Field} - - This module defines efficient bitfields - up to 30 or 62 bits (depending on the architecture) in - a relatively type-safe way. - - {[ - module B = CCBitField.Make(struct end);; - - #install_printer B.pp;; - - let x = B.mk_field () - let y = B.mk_field () - let z = B.mk_field () - - let f = B.empty |> B.set x true |> B.set y true;; - - assert (not (B.get z f)) ;; - - assert (f |> B.set z true |> B.get z);; - - ]} -*) - -exception TooManyFields -(** Raised when too many fields are packed into one bitfield *) - -exception Frozen -(** Raised when a frozen bitfield is modified *) - -val max_width : int -(** System-dependent maximum width for a bitfield, typically 30 or 62 *) - -(** {2 Bitfield Signature} *) -module type S = sig - type t = private int - (** Generative type of bitfields. Each instantiation of the functor - should create a new, incompatible type *) - - val empty : t - (** Empty bitfields (all bits 0) *) - - type field - - val get : field -> t -> bool - (** Get the value of this field *) - - val set : field -> bool -> t -> t - (** Set the value of this field *) - - val mk_field : unit -> field - (** Make a new field *) - - val freeze : unit -> unit - (** Prevent new fields from being added. From now on, creating - a field will raise Frozen *) - - val total_width : unit -> int - (** Current width of the bitfield *) -end - -(** Create a new bitfield type *) -module Make() : S - -(**/**) -val all_bits_ : int -> int -> int -(**/**) diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index 9f3a4f2d..dc73d889 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -6,12 +6,6 @@ Copyright 2016 Simon Cruanes module type S = Solver_types_intf.S -module Var_fields = Solver_types_intf.Var_fields - -let v_field_seen_neg = Var_fields.mk_field() -let v_field_seen_pos = Var_fields.mk_field() -let () = Var_fields.freeze() - (* Solver types for McSat Solving *) (* ************************************************************************ *) @@ -43,7 +37,7 @@ module McMake (E : Expr_intf.S) = struct vid : int; pa : atom; na : atom; - mutable v_fields : Var_fields.t; + mutable v_fields : int; mutable v_level : int; mutable v_idx: int; (** position in heap *) mutable v_weight : float; (** Weight (for the heap), tracking activity *) @@ -165,6 +159,9 @@ module McMake (E : Expr_intf.S) = struct (v.lid+1) debug_assign v E.Term.print v.term end + let seen_pos = 0b1 + let seen_neg = 0b10 + module Var = struct type t = var let[@inline] level v = v.v_level @@ -184,7 +181,7 @@ module McMake (E : Expr_intf.S) = struct { vid = st.cpt_mk_var; pa = pa; na = na; - v_fields = Var_fields.empty; + v_fields = 0; v_level = -1; v_idx= -1; v_weight = 0.; @@ -212,11 +209,11 @@ module McMake (E : Expr_intf.S) = struct (* Marking helpers *) let[@inline] clear v = - v.v_fields <- Var_fields.empty + v.v_fields <- 0 let[@inline] seen_both v = - Var_fields.get v_field_seen_pos v.v_fields && - Var_fields.get v_field_seen_neg v.v_fields + (seen_pos land v.v_fields <> 0) && + (seen_neg land v.v_fields <> 0) end module Atom = struct @@ -237,14 +234,16 @@ module McMake (E : Expr_intf.S) = struct let[@inline] seen a = let pos = equal a (abs a) in if pos - then Var_fields.get v_field_seen_pos a.var.v_fields - else Var_fields.get v_field_seen_neg a.var.v_fields + then (seen_pos land a.var.v_fields <> 0) + else (seen_neg land a.var.v_fields <> 0) let[@inline] mark a = let pos = equal a (abs a) in - if pos - then a.var.v_fields <- Var_fields.set v_field_seen_pos true a.var.v_fields - else a.var.v_fields <- Var_fields.set v_field_seen_neg true a.var.v_fields + if pos then ( + a.var.v_fields <- seen_pos lor a.var.v_fields + ) else ( + a.var.v_fields <- seen_neg lor a.var.v_fields + ) let[@inline] make st lit = let var, negated = Var.make st lit in diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli index 62219a3a..fb1e03c3 100644 --- a/src/core/Solver_types.mli +++ b/src/core/Solver_types.mli @@ -16,8 +16,6 @@ Copyright 2016 Simon Cruanes module type S = Solver_types_intf.S (** Interface for the internal types. *) -module Var_fields = Solver_types_intf.Var_fields - module McMake (E : Expr_intf.S): S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof (** Functor to instantiate the types of clauses for a solver. *) diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml index 61d1727a..c5d0ff37 100644 --- a/src/core/Solver_types_intf.ml +++ b/src/core/Solver_types_intf.ml @@ -10,8 +10,6 @@ Copyright 2016 Simon Cruanes used in the core solver. *) -module Var_fields = BitField.Make() - type 'a printer = Format.formatter -> 'a -> unit module type S = sig @@ -56,7 +54,7 @@ module type S = sig vid : int; (** Unique identifier *) pa : atom; (** Link for the positive atom *) na : atom; (** Link for the negative atom *) - mutable v_fields : Var_fields.t; (** bool fields *) + mutable v_fields : int; (** bool fields *) mutable v_level : int; (** Level of decision/propagation *) mutable v_idx: int; (** rank in variable heap *) mutable v_weight : float; (** Variable weight (for the heap) *) From c815ccf6480a12b39b5b4655fed0b774de84e7d5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 20:24:39 -0600 Subject: [PATCH 055/182] refactor: use `pp` instead of `print` --- src/backend/Backend_intf.ml | 2 +- src/backend/Coq.ml | 2 +- src/backend/Dedukti.ml | 6 +++--- src/backend/Dedukti.mli | 2 +- src/backend/Dot.ml | 2 +- src/core/Expr_intf.ml | 4 ++-- src/core/Formula_intf.ml | 2 +- src/core/Solver_types.ml | 21 +++++++-------------- src/main/main.ml | 2 +- src/sat/Expr_sat.ml | 2 +- src/tseitin/Msat_tseitin.ml | 18 +++++++++--------- src/tseitin/Tseitin_intf.ml | 4 ++-- 12 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/backend/Backend_intf.ml b/src/backend/Backend_intf.ml index 9cfff729..29900a0c 100644 --- a/src/backend/Backend_intf.ml +++ b/src/backend/Backend_intf.ml @@ -20,7 +20,7 @@ module type S = sig type t (** The type of proofs. *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** A function for printing proofs in the desired format. *) end diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index bba56ac7..7f536d67 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -149,7 +149,7 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause (* Here the main idea is to always try and have exactly one goal to prove, i.e False. So each *) - let print fmt p = + let pp fmt p = let h = count_uses p in let aux () node = Format.fprintf fmt "%a" (prove_node h) node diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml index 76c9db7f..46aa96a3 100644 --- a/src/backend/Dedukti.ml +++ b/src/backend/Dedukti.ml @@ -13,7 +13,7 @@ module type Arg = sig type lemma type formula - val print : Format.formatter -> formula -> unit + val pp : Format.formatter -> formula -> unit val prove : Format.formatter -> lemma -> unit val context : Format.formatter -> proof -> unit end @@ -38,7 +38,7 @@ module Make(S : Res.S)(A : Arg with type formula := S.formula S.Atom.lit (S.Atom.neg a), false in fprintf fmt "%s _b %a ->@ %a" - (if pos then "_pos" else "_neg") A.print f aux r + (if pos then "_pos" else "_neg") A.pp f aux r in fprintf fmt "_b : Prop ->@ %a ->@ _proof _b" aux (S.Clause.atoms_l c) @@ -53,7 +53,7 @@ module Make(S : Res.S)(A : Arg with type formula := S.formula fprintf fmt "[b: Prop, p: Prop] _neg b p --> _pos b p -> _proof b."; A.context fmt p - let print fmt p = + let pp fmt p = fprintf fmt "#NAME Proof."; fprintf fmt "(; Dedukti file automatically generated by mSAT ;)"; context fmt p; diff --git a/src/backend/Dedukti.mli b/src/backend/Dedukti.mli index 826c8031..bb15697c 100644 --- a/src/backend/Dedukti.mli +++ b/src/backend/Dedukti.mli @@ -19,7 +19,7 @@ module type Arg = sig type proof type formula - val print : Format.formatter -> formula -> unit + val pp : Format.formatter -> formula -> unit val prove : Format.formatter -> lemma -> unit val context : Format.formatter -> proof -> unit end diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index d25f1a09..5a33f054 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -140,7 +140,7 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom print_contents fmt n; print_edges fmt n - let print fmt p = + let pp fmt p = Format.fprintf fmt "digraph proof {@\n"; S.fold (fun () -> print_node fmt) () p; Format.fprintf fmt "}@." diff --git a/src/core/Expr_intf.ml b/src/core/Expr_intf.ml index 4536d36c..15f9fe99 100644 --- a/src/core/Expr_intf.ml +++ b/src/core/Expr_intf.ml @@ -34,7 +34,7 @@ module type S = sig (** Hashing function for terms. Should be such that two terms equal according to {!val:Expr_intf.S.equal} have the same hash. *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** Printing function used among other for debugging. *) end @@ -52,7 +52,7 @@ module type S = sig (** Hashing function for formulas. Should be such that two formulas equal according to {!val:Expr_intf.S.equal} have the same hash. *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** Printing function used among other thing for debugging. *) val neg : t -> t diff --git a/src/core/Formula_intf.ml b/src/core/Formula_intf.ml index edf8b3ff..26226c91 100644 --- a/src/core/Formula_intf.ml +++ b/src/core/Formula_intf.ml @@ -32,7 +32,7 @@ module type S = sig (** Hashing function for formulas. Should be such that two formulas equal according to {!val:Expr_intf.S.equal} have the same hash. *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** Printing function used among other thing for debugging. *) val neg : t -> t diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml index dc73d889..d585f5dd 100644 --- a/src/core/Solver_types.ml +++ b/src/core/Solver_types.ml @@ -151,12 +151,12 @@ module McMake (E : Expr_intf.S) = struct | None -> Format.fprintf fmt "" | Some t -> - Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level E.Term.print t + Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level E.Term.pp t - let pp out v = E.Term.print out v.term + let pp out v = E.Term.pp out v.term let debug out v = Format.fprintf out "%d[%a][lit:@[%a@]]" - (v.lid+1) debug_assign v E.Term.print v.term + (v.lid+1) debug_assign v E.Term.pp v.term end let seen_pos = 0b1 @@ -251,7 +251,7 @@ module McMake (E : Expr_intf.S) = struct | Formula_intf.Negated -> var.na | Formula_intf.Same_sign -> var.pa - let pp fmt a = E.Formula.print fmt a.lit + let pp fmt a = E.Formula.pp fmt a.lit let pp_a fmt v = if Array.length v = 0 then ( @@ -293,7 +293,7 @@ module McMake (E : Expr_intf.S) = struct let debug out a = Format.fprintf out "%s%d[%a][atom:@[%a@]]" - (sign a) (a.var.vid+1) debug_value a E.Formula.print a.lit + (sign a) (a.var.vid+1) debug_value a E.Formula.pp a.lit let debug_a out vec = Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec @@ -400,15 +400,8 @@ module McMake (E : Expr_intf.S) = struct Format.fprintf fmt "%a0" aux atoms end - module Term = struct - include E.Term - let pp = print - end - - module Formula = struct - include E.Formula - let pp = print - end + module Term = E.Term + module Formula = E.Formula end[@@inline] diff --git a/src/main/main.ml b/src/main/main.ml index 28400e72..177b5b32 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -52,7 +52,7 @@ module Process = struct S.Proof.check p; if !p_dot_proof <> "" then begin let fmt = Format.formatter_of_out_channel (open_out !p_dot_proof) in - D.print fmt p + D.pp fmt p end end; let t' = Sys.time () -. t in diff --git a/src/sat/Expr_sat.ml b/src/sat/Expr_sat.ml index 86fc6242..f41ba5d5 100644 --- a/src/sat/Expr_sat.ml +++ b/src/sat/Expr_sat.ml @@ -60,7 +60,7 @@ let iter: (t -> unit) -> unit = fun f -> done *) -let print fmt a = +let pp fmt a = Format.fprintf fmt "%s%s%d" (if a < 0 then "~" else "") (if a mod 2 = 0 then "v" else "f") diff --git a/src/tseitin/Msat_tseitin.ml b/src/tseitin/Msat_tseitin.ml index e1901aa1..3f162c8d 100644 --- a/src/tseitin/Msat_tseitin.ml +++ b/src/tseitin/Msat_tseitin.ml @@ -25,21 +25,21 @@ module Make (F : Tseitin_intf.Arg) = struct | Lit of atom | Comb of combinator * t list - let rec print fmt phi = + let rec pp fmt phi = match phi with | True -> Format.fprintf fmt "true" - | Lit a -> F.print fmt a + | Lit a -> F.pp fmt a | Comb (Not, [f]) -> - Format.fprintf fmt "not (%a)" print f - | Comb (And, l) -> Format.fprintf fmt "(%a)" (print_list "and") l - | Comb (Or, l) -> Format.fprintf fmt "(%a)" (print_list "or") l + Format.fprintf fmt "not (%a)" pp f + | Comb (And, l) -> Format.fprintf fmt "(%a)" (pp_list "and") l + | Comb (Or, l) -> Format.fprintf fmt "(%a)" (pp_list "or") l | Comb (Imp, [f1; f2]) -> - Format.fprintf fmt "(%a => %a)" print f1 print f2 + Format.fprintf fmt "(%a => %a)" pp f1 pp f2 | _ -> assert false - and print_list sep fmt = function + and pp_list sep fmt = function | [] -> () - | [f] -> print fmt f - | f::l -> Format.fprintf fmt "%a %s %a" print f sep (print_list sep) l + | [f] -> pp fmt f + | f::l -> Format.fprintf fmt "%a %s %a" pp f sep (pp_list sep) l let make comb l = Comb (comb, l) diff --git a/src/tseitin/Tseitin_intf.ml b/src/tseitin/Tseitin_intf.ml index abdfcf63..6ead857f 100644 --- a/src/tseitin/Tseitin_intf.ml +++ b/src/tseitin/Tseitin_intf.ml @@ -28,7 +28,7 @@ module type Arg = sig val fresh : unit -> t (** Generate fresh formulas (that are different from any other). *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** Print the given formula. *) end @@ -80,7 +80,7 @@ module type S = sig list (which is a conjunction) of lists (which are disjunctions) of atomic formulas. *) - val print : Format.formatter -> t -> unit + val pp : Format.formatter -> t -> unit (** [print fmt f] prints the formula on the formatter [fmt].*) end From 1b6ff0e84973ec501e4fd75342dcde724e533c78 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 20:34:17 -0600 Subject: [PATCH 056/182] chore: update travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c6315169..dc316dd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,8 @@ before_install: - export OPAMVERBOSE=1 - opam switch ${OCAML_VERSION} - eval `opam config env` - - opam install ocamlfind jbuilder - - if ${RUN_TEST}; then opam pin add dolmen https://github.com/Gbury/dolmen.git; fi + - opam install ocamlfind dune sequence + - if ${RUN_TEST}; then opam install containers ; fi install: - make build script: From c4da650e4516e3ba2077fd7bc5024a2f62109e5b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 22:38:03 -0600 Subject: [PATCH 057/182] perf: use release profile in msat.sh --- msat.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msat.sh b/msat.sh index d111c6b0..6428999f 100755 --- a/msat.sh +++ b/msat.sh @@ -1,3 +1,3 @@ #!/bin/sh -exec dune exec src/main/main.exe -- $@ +exec dune exec --profile=release src/main/main.exe -- $@ From 1655d09035a4f16a6c9ae08c65b3b2e11310b278 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 22:38:15 -0600 Subject: [PATCH 058/182] refactor: simpler, cleaner functors --- src/backend/Coq.ml | 40 +- src/backend/Coq.mli | 7 +- src/backend/Dedukti.ml | 11 +- src/backend/Dedukti.mli | 4 +- src/backend/Dimacs.ml | 30 +- src/backend/Dimacs.mli | 8 +- src/backend/Dot.ml | 58 +-- src/backend/Dot.mli | 7 +- src/backend/dune | 2 +- src/core/Expr_intf.ml | 70 --- src/core/Formula_intf.ml | 50 -- src/core/Internal.ml | 944 +++++++++++++++++++++++++++++++--- src/core/Msat.ml | 24 +- src/core/Plugin_intf.ml | 128 ----- src/core/Res.ml | 302 ----------- src/core/Res.mli | 19 - src/core/Res_intf.ml | 136 ----- src/core/Solver.ml | 149 +----- src/core/Solver.mli | 26 +- src/core/Solver_intf.ml | 371 +++++++++++-- src/core/Solver_types.ml | 420 --------------- src/core/Solver_types.mli | 26 - src/core/Solver_types_intf.ml | 271 ---------- src/core/Theory_intf.ml | 83 --- src/main/main.ml | 4 +- src/sat/Expr_sat.ml | 7 +- src/sat/Expr_sat.mli | 2 +- src/sat/Msat_sat.ml | 4 +- src/sat/Msat_sat.mli | 2 +- tests/test_api.ml | 37 +- 30 files changed, 1342 insertions(+), 1900 deletions(-) delete mode 100644 src/core/Expr_intf.ml delete mode 100644 src/core/Formula_intf.ml delete mode 100644 src/core/Plugin_intf.ml delete mode 100644 src/core/Res.ml delete mode 100644 src/core/Res.mli delete mode 100644 src/core/Res_intf.ml delete mode 100644 src/core/Solver_types.ml delete mode 100644 src/core/Solver_types.mli delete mode 100644 src/core/Solver_types_intf.ml delete mode 100644 src/core/Theory_intf.ml diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index 7f536d67..865c1e72 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -2,7 +2,6 @@ MSAT is free software, using the Apache license, see file LICENSE Copyright 2015 Guillaume Bury *) -open Msat module type S = Backend_intf.S @@ -18,7 +17,7 @@ module type Arg = sig end -module Make(S : Res.S)(A : Arg with type hyp := S.clause +module Make(S : Msat.S)(A : Arg with type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct @@ -26,6 +25,7 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause module Clause = S.Clause module M = Map.Make(S.Atom) module C_tbl = S.Clause.Tbl + module P = S.Proof let name = Clause.name @@ -122,29 +122,29 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause Format.fprintf fmt "(* Clearing unused clauses *)@\n%a" clean_aux l' let prove_node t fmt node = - let clause = node.S.conclusion in - match node.S.step with - | S.Hypothesis -> + let clause = node.P.conclusion in + match node.P.step with + | P.Hypothesis -> A.prove_hyp fmt (name clause) clause - | S.Assumption -> + | P.Assumption -> A.prove_assumption fmt (name clause) clause - | S.Lemma _ -> + | P.Lemma _ -> A.prove_lemma fmt (name clause) clause - | S.Duplicate (p, l) -> - let c = S.conclusion p in + | P.Duplicate (p, l) -> + let c = P.conclusion p in let () = elim_duplicate fmt clause c l in clean t fmt [c] - | S.Resolution (p1, p2, a) -> - let c1 = S.conclusion p1 in - let c2 = S.conclusion p2 in + | P.Resolution (p1, p2, a) -> + let c1 = P.conclusion p1 in + let c2 = P.conclusion p2 in if resolution fmt clause c1 c2 a then clean t fmt [c1; c2] let count_uses p = let h = C_tbl.create 128 in let aux () node = - List.iter (fun p' -> incr_use h S.(conclusion p')) (S.parents node.S.step) + List.iter (fun p' -> incr_use h P.(conclusion p')) (P.parents node.P.step) in - let () = S.fold aux () p in + let () = P.fold aux () p in h (* Here the main idea is to always try and have exactly @@ -155,19 +155,19 @@ module Make(S : Res.S)(A : Arg with type hyp := S.clause Format.fprintf fmt "%a" (prove_node h) node in Format.fprintf fmt "(* Coq proof generated by mSAT*)@\n"; - S.fold aux () p - + P.fold aux () p end -module Simple(S : Res.S) +module Simple(S : Msat.S) (A : Arg with type hyp = S.formula list and type lemma := S.lemma and type assumption := S.formula) = Make(S)(struct + module P = S.Proof (* Some helpers *) - let lit = S.Atom.lit + let lit = S.Atom.formula let get_assumption c = match S.Clause.atoms_l c with @@ -175,8 +175,8 @@ module Simple(S : Res.S) | _ -> assert false let get_lemma c = - match S.expand (S.prove c) with - | {S.step=S.Lemma p; _} -> p + match P.expand (P.prove c) with + | {P.step=P.Lemma p; _} -> p | _ -> assert false let prove_hyp fmt name c = diff --git a/src/backend/Coq.mli b/src/backend/Coq.mli index ec743ff2..3d34f549 100644 --- a/src/backend/Coq.mli +++ b/src/backend/Coq.mli @@ -8,7 +8,6 @@ Copyright 2015 Guillaume Bury This module provides an easy way to produce coq scripts corresponding to the resolution proofs output by the sat solver. *) -open Msat module type S = Backend_intf.S (** Interface for exporting proofs. *) @@ -34,14 +33,14 @@ module type Arg = sig end -module Make(S : Res.S)(A : Arg with type hyp := S.clause +module Make(S : Msat.S)(A : Arg with type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) : S with type t := S.proof (** Base functor to output Coq proofs *) -module Simple(S : Res.S)(A : Arg with type hyp = S.formula list +module Simple(S : Msat.S)(A : Arg with type hyp = S.formula list and type lemma := S.lemma and type assumption := S.formula) : S with type t := S.proof -(** Simple functo to output Coq proofs *) +(** Simple functor to output Coq proofs *) diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml index 46aa96a3..7f8526eb 100644 --- a/src/backend/Dedukti.ml +++ b/src/backend/Dedukti.ml @@ -3,8 +3,6 @@ MSAT is free software, using the Apache license, see file LICENSE Copyright 2015 Guillaume Bury *) -open Msat - module type S = Backend_intf.S module type Arg = sig @@ -18,9 +16,10 @@ module type Arg = sig val context : Format.formatter -> proof -> unit end -module Make(S : Res.S)(A : Arg with type formula := S.formula +module Make(S : Msat.S)(A : Arg with type formula := S.formula and type lemma := S.lemma and type proof := S.proof) = struct + module P = S.Proof let pp_nl fmt = Format.fprintf fmt "@\n" let fprintf fmt format = Format.kfprintf pp_nl fmt format @@ -32,10 +31,10 @@ module Make(S : Res.S)(A : Arg with type formula := S.formula | [] -> () | a :: r -> let f, pos = - if S.Atom.is_pos a then - S.Atom.lit a, true + if S.Atom.sign a then + S.Atom.formula a, true else - S.Atom.lit (S.Atom.neg a), false + S.Atom.formula (S.Atom.neg a), false in fprintf fmt "%s _b %a ->@ %a" (if pos then "_pos" else "_neg") A.pp f aux r diff --git a/src/backend/Dedukti.mli b/src/backend/Dedukti.mli index bb15697c..ef7cc88e 100644 --- a/src/backend/Dedukti.mli +++ b/src/backend/Dedukti.mli @@ -9,8 +9,6 @@ Copyright 2014 Simon Cruanes Work in progress... *) -open Msat - module type S = Backend_intf.S module type Arg = sig @@ -25,7 +23,7 @@ module type Arg = sig end module Make : - functor(S : Res.S) -> + functor(S : Msat.S) -> functor(A : Arg with type formula := S.formula and type lemma := S.lemma diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml index 3339175d..cb17851d 100644 --- a/src/backend/Dimacs.ml +++ b/src/backend/Dimacs.ml @@ -6,6 +6,11 @@ Copyright 2014 Simon Cruanes open Msat +module type ARG = sig + type clause + val lits : clause -> int list +end + module type S = sig type st @@ -29,15 +34,21 @@ module type S = sig end -module Make(St : Solver_types_intf.S) = struct +module Make(St : Msat.S)(A: ARG with type clause = St.clause) = struct type st = St.t + let pp_dimacs fmt c = + let lits = A.lits c in + List.iter (fun p -> Format.fprintf fmt "%d " p) lits; + Format.fprintf fmt "0" + + (* Dimacs & iCNF export *) let export_vec name fmt vec = - Format.fprintf fmt "c %s@,%a@," name (Vec.pp ~sep:"" St.Clause.pp_dimacs) vec + Format.fprintf fmt "c %s@,%a@," name (Vec.pp ~sep:"" pp_dimacs) vec let export_assumption fmt vec = - Format.fprintf fmt "c Local assumptions@,a %a@," St.Clause.pp_dimacs vec + Format.fprintf fmt "c Local assumptions@,a %a@," pp_dimacs vec let export_icnf_aux r name map_filter fmt vec = let aux fmt _ = @@ -45,14 +56,15 @@ module Make(St : Solver_types_intf.S) = struct let x = Vec.get vec i in match map_filter x with | None -> () - | Some _ -> Format.fprintf fmt "%a@," St.Clause.pp_dimacs (Vec.get vec i) + | Some _ -> Format.fprintf fmt "%a@," pp_dimacs (Vec.get vec i) done; r := Vec.size vec in Format.fprintf fmt "c %s@,%a" name aux vec + (* TODO let map_filter_learnt c = - match St.Clause.premise c with + match P.Clause.premise c with | St.Hyp | St.Local -> assert false | St.Lemma _ -> Some c | St.History l -> @@ -73,13 +85,17 @@ module Make(St : Solver_types_intf.S) = struct | Some d -> Vec.push lemmas d ) learnt; lemmas + *) let export st fmt ~hyps ~history ~local = + assert false + (* FIXME assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); (* Learnt clauses, then filtered to only keep only the theory lemmas; all other learnt clauses should be logical consequences of the rest. *) let lemmas = filter_vec history in + let lemmas = history in (* Local assertions *) assert (Vec.for_all (fun c -> St.Local = St.Clause.premise c) local); (* Number of atoms and clauses *) @@ -90,12 +106,15 @@ module Make(St : Solver_types_intf.S) = struct (export_vec "Local assumptions") local (export_vec "Hypotheses") hyps (export_vec "Lemmas") lemmas + *) (* Refs to remember what portion of a problem has been printed *) let icnf_hyp = ref 0 let icnf_lemmas = ref 0 let export_icnf fmt ~hyps ~history ~local = + assert false + (* FIXME assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); let lemmas = history in (* Local assertions *) @@ -113,6 +132,7 @@ module Make(St : Solver_types_intf.S) = struct (export_icnf_aux icnf_hyp "Hypotheses" (fun x -> Some x)) hyps (export_icnf_aux icnf_lemmas "Lemmas" map_filter_learnt) lemmas export_assumption local + *) end diff --git a/src/backend/Dimacs.mli b/src/backend/Dimacs.mli index e829bebd..aada5010 100644 --- a/src/backend/Dimacs.mli +++ b/src/backend/Dimacs.mli @@ -12,6 +12,11 @@ Copyright 2014 Simon Cruanes open Msat +module type ARG = sig + type clause + val lits : clause -> int list +end + module type S = sig type st @@ -44,6 +49,7 @@ module type S = sig end -module Make(St: Solver_types_intf.S) : S with type clause := St.clause and type st = St.t +module Make(St: Msat.S)(A: ARG with type clause = St.clause) + : S with type clause := St.clause and type st = St.t (** Functor to create a module for exporting probems to the dimacs (& iCNF) formats. *) diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 5a33f054..b00178a7 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -4,8 +4,6 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -open Msat - (** Output interface for the backend *) module type S = Backend_intf.S @@ -30,7 +28,7 @@ module type Arg = sig end -module Default(S : Res.S) = struct +module Default(S : Msat.S) = struct module Atom = S.Atom module Clause = S.Clause @@ -51,18 +49,19 @@ module Default(S : Res.S) = struct end (** Functor to provide dot printing *) -module Make(S : Res.S)(A : Arg with type atom := S.atom +module Make(S : Msat.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct module Atom = S.Atom module Clause = S.Clause + module P = S.Proof - let node_id n = Clause.name n.S.conclusion + let node_id n = Clause.name n.P.conclusion let res_node_id n = (node_id n) ^ "_res" - let proof_id p = node_id (S.expand p) + let proof_id p = node_id (P.expand p) let print_clause fmt c = let v = Clause.atoms c in @@ -80,8 +79,8 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom Format.fprintf fmt "%s -> %s;@\n" j i let print_edges fmt n = - match S.(n.step) with - | S.Resolution (p1, p2, _) -> + match P.(n.step) with + | P.Resolution (p1, p2, _) -> print_edge fmt (res_node_id n) (proof_id p1); print_edge fmt (res_node_id n) (proof_id p2) | _ -> () @@ -109,29 +108,29 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom let ttify f c = fun fmt () -> f fmt c let print_contents fmt n = - match S.(n.step) with + match P.(n.step) with (* Leafs of the proof tree *) - | S.Hypothesis -> - let rule, color, l = A.hyp_info S.(n.conclusion) in + | P.Hypothesis -> + let rule, color, l = A.hyp_info P.(n.conclusion) in let color = match color with None -> "LIGHTBLUE" | Some c -> c in - print_dot_node fmt (node_id n) "LIGHTBLUE" S.(n.conclusion) rule color l - | S.Assumption -> - let rule, color, l = A.assumption_info S.(n.conclusion) in + print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l + | P.Assumption -> + let rule, color, l = A.assumption_info P.(n.conclusion) in let color = match color with None -> "LIGHTBLUE" | Some c -> c in - print_dot_node fmt (node_id n) "LIGHTBLUE" S.(n.conclusion) rule color l - | S.Lemma _ -> - let rule, color, l = A.lemma_info S.(n.conclusion) in + print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l + | P.Lemma _ -> + let rule, color, l = A.lemma_info P.(n.conclusion) in let color = match color with None -> "YELLOW" | Some c -> c in - print_dot_node fmt (node_id n) "LIGHTBLUE" S.(n.conclusion) rule color l + print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l (* Tree nodes *) - | S.Duplicate (p, l) -> - print_dot_node fmt (node_id n) "GREY" S.(n.conclusion) "Duplicate" "GREY" + | P.Duplicate (p, l) -> + print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Duplicate" "GREY" ((fun fmt () -> (Format.fprintf fmt "%s" (node_id n))) :: List.map (ttify A.print_atom) l); - print_edge fmt (node_id n) (node_id (S.expand p)) - | S.Resolution (_, _, a) -> - print_dot_node fmt (node_id n) "GREY" S.(n.conclusion) "Resolution" "GREY" + print_edge fmt (node_id n) (node_id (P.expand p)) + | P.Resolution (_, _, a) -> + print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Resolution" "GREY" [(fun fmt () -> (Format.fprintf fmt "%s" (node_id n)))]; print_dot_res_node fmt (res_node_id n) a; print_edge fmt (node_id n) (res_node_id n) @@ -142,12 +141,12 @@ module Make(S : Res.S)(A : Arg with type atom := S.atom let pp fmt p = Format.fprintf fmt "digraph proof {@\n"; - S.fold (fun () -> print_node fmt) () p; + P.fold (fun () -> print_node fmt) () p; Format.fprintf fmt "}@." end -module Simple(S : Res.S) +module Simple(S : Msat.S) (A : Arg with type atom := S.formula and type hyp = S.formula list and type lemma := S.lemma @@ -155,9 +154,10 @@ module Simple(S : Res.S) Make(S)(struct module Atom = S.Atom module Clause = S.Clause + module P = S.Proof (* Some helpers *) - let lit = Atom.lit + let lit = Atom.formula let get_assumption c = match S.Clause.atoms_l c with @@ -165,13 +165,13 @@ module Simple(S : Res.S) | _ -> assert false let get_lemma c = - match S.expand (S.prove c) with - | {S.step=S.Lemma p;_} -> p + match P.expand (P.prove c) with + | {P.step=P.Lemma p;_} -> p | _ -> assert false (* Actual functions *) let print_atom fmt a = - A.print_atom fmt (Atom.lit a) + A.print_atom fmt (lit a) let hyp_info c = A.hyp_info (List.map lit (S.Clause.atoms_l c)) diff --git a/src/backend/Dot.mli b/src/backend/Dot.mli index a3c9c787..70adffa1 100644 --- a/src/backend/Dot.mli +++ b/src/backend/Dot.mli @@ -9,7 +9,6 @@ Copyright 2014 Simon Cruanes This module provides functions to export proofs into the dot graph format. Graphs in dot format can be used to generates images using the graphviz tool. *) -open Msat module type S = Backend_intf.S (** Interface for exporting proofs. *) @@ -48,20 +47,20 @@ module type Arg = sig end -module Default(S : Res.S) : Arg with type atom := S.atom +module Default(S : Msat.S) : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause (** Provides a reasonnable default to instantiate the [Make] functor, assuming the original printing functions are compatible with DOT html labels. *) -module Make(S : Res.S)(A : Arg with type atom := S.atom +module Make(S : Msat.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) : S with type t := S.proof (** Functor for making a module to export proofs to the DOT format. *) -module Simple(S : Res.S)(A : Arg with type atom := S.formula +module Simple(S : Msat.S)(A : Arg with type atom := S.formula and type hyp = S.formula list and type lemma := S.lemma and type assumption = S.formula) : S with type t := S.proof diff --git a/src/backend/dune b/src/backend/dune index d9a6c6de..7febb3bc 100644 --- a/src/backend/dune +++ b/src/backend/dune @@ -3,7 +3,7 @@ (public_name msat.backend) (synopsis "proof backends for msat") (libraries msat) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -warn-error -27 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) ) diff --git a/src/core/Expr_intf.ml b/src/core/Expr_intf.ml deleted file mode 100644 index 15f9fe99..00000000 --- a/src/core/Expr_intf.ml +++ /dev/null @@ -1,70 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Mcsat expressions - - This modules defines the required implementation of expressions for Mcsat. -*) - -type negated = Formula_intf.negated = - | Negated (** changed sign *) - | Same_sign (** kept sign *) -(** This type is used during the normalisation of formulas. - See {!val:Expr_intf.S.norm} for more details. *) - -module type S = sig - (** Signature of formulas that parametrises the Mcsat Solver Module. *) - - type proof - (** An abstract type for proofs *) - - module Term : sig - (** McSat Terms *) - - type t - (** The type of terms *) - - val equal : t -> t -> bool - (** Equality over terms. *) - - val hash : t -> int - (** Hashing function for terms. Should be such that two terms equal according - to {!val:Expr_intf.S.equal} have the same hash. *) - - val pp : Format.formatter -> t -> unit - (** Printing function used among other for debugging. *) - - end - - module Formula : sig - (** McSat formulas *) - - type t - (** The type of atomic formulas over terms. *) - - val equal : t -> t -> bool - (** Equality over formulas. *) - - val hash : t -> int - (** Hashing function for formulas. Should be such that two formulas equal according - to {!val:Expr_intf.S.equal} have the same hash. *) - - val pp : Format.formatter -> t -> unit - (** Printing function used among other thing for debugging. *) - - val neg : t -> t - (** Formula negation *) - - val norm : t -> t * negated - (** Returns a 'normalized' form of the formula, possibly negated - (in which case return [Negated]). - [norm] must be so that [a] and [neg a] normalise to the same formula, - but one returns [Negated] and the other [Same_sign]. *) - end - - -end - diff --git a/src/core/Formula_intf.ml b/src/core/Formula_intf.ml deleted file mode 100644 index 26226c91..00000000 --- a/src/core/Formula_intf.ml +++ /dev/null @@ -1,50 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** SMT formulas - - This module defines the required implementation of formulas for - an SMT solver. -*) - -type negated = - | Negated (** changed sign *) - | Same_sign (** kept sign *) -(** This type is used during the normalisation of formulas. - See {!val:Expr_intf.S.norm} for more details. *) - -module type S = sig - (** SMT formulas *) - - type t - (** The type of atomic formulas. *) - - type proof - (** An abstract type for proofs *) - - val equal : t -> t -> bool - (** Equality over formulas. *) - - val hash : t -> int - (** Hashing function for formulas. Should be such that two formulas equal according - to {!val:Expr_intf.S.equal} have the same hash. *) - - val pp : Format.formatter -> t -> unit - (** Printing function used among other thing for debugging. *) - - val neg : t -> t - (** Formula negation. Should be an involution, i.e. [equal a (neg neg a)] should - always hold. *) - - val norm : t -> t * negated - (** Returns a 'normalized' form of the formula, possibly negated - (in which case return [Negated]). This function is used to recognize - the link between a formula [a] and its negation [neg a], so the goal is - that [a] and [neg a] normalise to the same formula, - but one returns [Same_sign] and the other one returns [Negated] *) - -end - diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e5bbddf9..42dfd0f0 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -4,28 +4,665 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -(* TODO: move solver types here *) +module type PLUGIN = sig + val mcsat : bool + (** Is this a mcsat plugin? *) -module Make - (St : Solver_types.S) - (Plugin : Plugin_intf.S with type term = St.term - and type formula = St.formula - and type proof = St.proof) + include Solver_intf.PLUGIN_MCSAT +end + +module Make(Plugin : PLUGIN) = struct - module Proof = Res.Make(St) + module Term = Plugin.Term + module Formula = Plugin.Formula - open St + type term = Term.t + type formula = Formula.t type theory = Plugin.t + type lemma = Plugin.proof + + (* MCSAT literal *) + type lit = { + lid : int; + term : term; + mutable l_level : int; + mutable l_idx: int; + mutable l_weight : float; + mutable assigned : term option; + } + + type var = { + vid : int; + pa : atom; + na : atom; + mutable v_fields : int; + mutable v_level : int; + mutable v_idx: int; (** position in heap *) + mutable v_weight : float; (** Weight (for the heap), tracking activity *) + mutable v_assignable: lit list option; + mutable reason : reason option; + } + + and atom = { + aid : int; + var : var; + neg : atom; + lit : formula; + mutable is_true : bool; + watched : clause Vec.t; + } + + and clause = { + name : int; + tag : int option; (* TODO remove *) + atoms : atom array; + mutable cpremise : premise; + mutable activity : float; + mutable attached : bool; (* TODO: use an int field *) + mutable visited : bool; + } + + and reason = + | Decision + | Bcp of clause + | Semantic + + (* TODO: remove, replace with user-provided proof trackng device? + for pure SAT, [reason] is sufficient *) + and premise = + | Hyp + | Local + | Lemma of lemma + | History of clause list + + type elt = + | E_lit of lit + | E_var of var + + type trail_elt = + | Lit of lit + | Atom of atom + + (* Constructors *) + module MF = Hashtbl.Make(Formula) + module MT = Hashtbl.Make(Term) + + type st = { + t_map: lit MT.t; + f_map: var MF.t; + vars: elt Vec.t; + mutable cpt_mk_var: int; + mutable cpt_mk_clause: int; + } + + let create_st ?(size=`Big) () : st = + let size_map = match size with + | `Tiny -> 8 + | `Small -> 16 + | `Big -> 4096 + in + { f_map = MF.create size_map; + t_map = MT.create size_map; + vars = Vec.create(); + cpt_mk_var = 0; + cpt_mk_clause = 0; + } + + let nb_elt st = Vec.size st.vars + let get_elt st i = Vec.get st.vars i + let iter_elt st f = Vec.iter f st.vars + + let name_of_clause c = match c.cpremise with + | Hyp -> "H" ^ string_of_int c.name + | Local -> "L" ^ string_of_int c.name + | Lemma _ -> "T" ^ string_of_int c.name + | History _ -> "C" ^ string_of_int c.name + + module Lit = struct + type t = lit + let[@inline] term l = l.term + let[@inline] level l = l.l_level + let[@inline] assigned l = l.assigned + let[@inline] weight l = l.l_weight + + let make (st:st) (t:term) : t = + try MT.find st.t_map t + with Not_found -> + let res = { + lid = st.cpt_mk_var; + term = t; + l_weight = 1.; + l_idx= -1; + l_level = -1; + assigned = None; + } in + st.cpt_mk_var <- st.cpt_mk_var + 1; + MT.add st.t_map t res; + Vec.push st.vars (E_lit res); + res + + let debug_assign fmt v = + match v.assigned with + | None -> + Format.fprintf fmt "" + | Some t -> + Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level Term.pp t + + let pp out v = Term.pp out v.term + let debug out v = + Format.fprintf out "%d[%a][lit:@[%a@]]" + (v.lid+1) debug_assign v Term.pp v.term + end + + let seen_pos = 0b1 + let seen_neg = 0b10 + + module Var = struct + type t = var + let[@inline] level v = v.v_level + let[@inline] pos v = v.pa + let[@inline] neg v = v.na + let[@inline] reason v = v.reason + let[@inline] assignable v = v.v_assignable + let[@inline] weight v = v.v_weight + + let make (st:st) (t:formula) : var * Solver_intf.negated = + let lit, negated = Formula.norm t in + try + MF.find st.f_map lit, negated + with Not_found -> + let cpt_double = st.cpt_mk_var lsl 1 in + let rec var = + { vid = st.cpt_mk_var; + pa = pa; + na = na; + v_fields = 0; + v_level = -1; + v_idx= -1; + v_weight = 0.; + v_assignable = None; + reason = None; + } + and pa = + { var = var; + lit = lit; + watched = Vec.create(); + neg = na; + is_true = false; + aid = cpt_double (* aid = vid*2 *) } + and na = + { var = var; + lit = Formula.neg lit; + watched = Vec.create(); + neg = pa; + is_true = false; + aid = cpt_double + 1 (* aid = vid*2+1 *) } in + MF.add st.f_map lit var; + st.cpt_mk_var <- st.cpt_mk_var + 1; + Vec.push st.vars (E_var var); + var, negated + + (* Marking helpers *) + let[@inline] clear v = + v.v_fields <- 0 + + let[@inline] seen_both v = + (seen_pos land v.v_fields <> 0) && + (seen_neg land v.v_fields <> 0) + end + + module Atom = struct + type t = atom + let[@inline] level a = a.var.v_level + let[@inline] var a = a.var + let[@inline] neg a = a.neg + let[@inline] abs a = a.var.pa + let[@inline] formula a = a.lit + let[@inline] equal a b = a == b + let[@inline] sign a = a == abs a + let[@inline] hash a = Hashtbl.hash a.aid + let[@inline] compare a b = Pervasives.compare a.aid b.aid + let[@inline] reason a = Var.reason a.var + let[@inline] id a = a.aid + let[@inline] is_true a = a.is_true + let[@inline] is_false a = a.neg.is_true + + let[@inline] seen a = + let pos = equal a (abs a) in + if pos + then (seen_pos land a.var.v_fields <> 0) + else (seen_neg land a.var.v_fields <> 0) + + let[@inline] mark a = + let pos = equal a (abs a) in + if pos then ( + a.var.v_fields <- seen_pos lor a.var.v_fields + ) else ( + a.var.v_fields <- seen_neg lor a.var.v_fields + ) + + let[@inline] make st lit = + let var, negated = Var.make st lit in + match negated with + | Solver_intf.Negated -> var.na + | Solver_intf.Same_sign -> var.pa + + let pp fmt a = Formula.pp fmt a.lit + + let pp_a fmt v = + if Array.length v = 0 then ( + Format.fprintf fmt "∅" + ) else ( + pp fmt v.(0); + if (Array.length v) > 1 then begin + for i = 1 to (Array.length v) - 1 do + Format.fprintf fmt " ∨ %a" pp v.(i) + done + end + ) + + (* Complete debug printing *) + let pp_sign a = if a == a.var.pa then "+" else "-" + + let debug_reason fmt = function + | n, _ when n < 0 -> + Format.fprintf fmt "%%" + | n, None -> + Format.fprintf fmt "%d" n + | n, Some Decision -> + Format.fprintf fmt "@@%d" n + | n, Some Bcp c -> + Format.fprintf fmt "->%d/%s" n (name_of_clause c) + | n, Some Semantic -> + Format.fprintf fmt "::%d" n + + let pp_level fmt a = + debug_reason fmt (a.var.v_level, a.var.reason) + + let debug_value fmt a = + if a.is_true then + Format.fprintf fmt "T%a" pp_level a + else if a.neg.is_true then + Format.fprintf fmt "F%a" pp_level a + else + Format.fprintf fmt "" + + let debug out a = + Format.fprintf out "%s%d[%a][atom:@[%a@]]" + (pp_sign a) (a.var.vid+1) debug_value a Formula.pp a.lit + + let debug_a out vec = + Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec + end + + (* Elements *) + module Elt = struct + type t = elt + let[@inline] of_lit l = E_lit l + let[@inline] of_var v = E_var v + + let[@inline] id = function + | E_lit l -> l.lid | E_var v -> v.vid + let[@inline] level = function + | E_lit l -> l.l_level | E_var v -> v.v_level + let[@inline] idx = function + | E_lit l -> l.l_idx | E_var v -> v.v_idx + let[@inline] weight = function + | E_lit l -> l.l_weight | E_var v -> v.v_weight + + let[@inline] set_level e lvl = match e with + | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl + let[@inline] set_idx e i = match e with + | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i + let[@inline] set_weight e w = match e with + | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w + end + + module Trail_elt = struct + type t = trail_elt + let[@inline] of_lit l = Lit l + let[@inline] of_atom a = Atom a + + let debug fmt = function + | Lit l -> Lit.debug fmt l + | Atom a -> Atom.debug fmt a + end + + module Clause = struct + type t = clause + + let make = + let n = ref 0 in + fun ?tag ali premise -> + let atoms = Array.of_list ali in + let name = !n in + incr n; + { name; + tag = tag; + atoms = atoms; + visited = false; + attached = false; + activity = 0.; + cpremise = premise} + + let empty = make [] (History []) + let name = name_of_clause + let[@inline] equal c1 c2 = c1==c2 + let[@inline] atoms c = c.atoms + let[@inline] atoms_l c = Array.to_list c.atoms + let[@inline] tag c = c.tag + let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms + + let[@inline] premise c = c.cpremise + let[@inline] set_premise c p = c.cpremise <- p + + let[@inline] visited c = c.visited + let[@inline] set_visited c b = c.visited <- b + + let[@inline] attached c = c.attached + let[@inline] set_attached c b = c.attached <- b + + let[@inline] activity c = c.activity + let[@inline] set_activity c w = c.activity <- w + + module Tbl = Hashtbl.Make(struct + type t = clause + let hash = hash + let equal = equal + end) + + let pp fmt c = + Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms + + let debug_premise out = function + | Hyp -> Format.fprintf out "hyp" + | Local -> Format.fprintf out "local" + | Lemma _ -> Format.fprintf out "th_lemma" + | History v -> + List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v + + let debug out ({atoms=arr; cpremise=cp;_}as c) = + Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" + (name c) Atom.debug_a arr debug_premise cp + end + + module Proof = struct + exception Insuficient_hyps + exception Resolution_error of string + + type atom = Atom.t + type clause = Clause.t + type formula = Formula.t + type lemma = Plugin.proof + + let merge = List.merge Atom.compare + + let _c = ref 0 + let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c) + + (* Compute resolution of 2 clauses *) + let resolve l = + let rec aux resolved acc = function + | [] -> resolved, acc + | [a] -> resolved, a :: acc + | a :: b :: r -> + if Atom.equal a b then + aux resolved (a :: acc) r + else if Atom.equal a.neg b then + aux (a.var.pa :: resolved) acc r + else + aux resolved (a :: acc) (b :: r) + in + let resolved, new_clause = aux [] [] l in + resolved, List.rev new_clause + + (* Compute the set of doublons of a clause *) + let list c = List.sort Atom.compare (Array.to_list c.atoms) + + let analyze cl = + let rec aux duplicates free = function + | [] -> duplicates, free + | [ x ] -> duplicates, x :: free + | x :: ((y :: r) as l) -> + if x == y then + count duplicates (x :: free) x [y] r + else + aux duplicates (x :: free) l + and count duplicates free x acc = function + | (y :: r) when x == y -> + count duplicates free x (y :: acc) r + | l -> + aux (acc :: duplicates) free l + in + let doublons, acc = aux [] [] cl in + doublons, List.rev acc + + let to_list c = + let cl = list c in + let doublons, l = analyze cl in + let conflicts, _ = resolve l in + if doublons <> [] then + Log.debug 3 "Input clause has redundancies"; + if conflicts <> [] then + Log.debug 3 "Input clause is a tautology"; + cl + + (* Comparison of clauses *) + let cmp_cl c d = + let rec aux = function + | [], [] -> 0 + | a :: r, a' :: r' -> + begin match Atom.compare a a' with + | 0 -> aux (r, r') + | x -> x + end + | _ :: _ , [] -> -1 + | [], _ :: _ -> 1 + in + aux (c, d) + + let[@inline] prove conclusion = + assert (conclusion.cpremise <> History []); + conclusion + + let rec set_atom_proof a = + let aux acc b = + if Atom.equal a.neg b then acc + else set_atom_proof b :: acc + in + assert (a.var.v_level >= 0); + match (a.var.reason) with + | Some (Bcp c) -> + Log.debugf 5 (fun k->k "Analysing: @[%a@ %a@]" Atom.debug a Clause.debug c); + if Array.length c.atoms = 1 then ( + Log.debugf 5 (fun k -> k "Old reason: @[%a@]" Atom.debug a); + c + ) else ( + assert (a.neg.is_true); + let r = History (c :: (Array.fold_left aux [] c.atoms)) in + let c' = Clause.make [a.neg] r in + a.var.reason <- Some (Bcp c'); + Log.debugf 5 + (fun k -> k "New reason: @[%a@ %a@]" Atom.debug a Clause.debug c'); + c' + ) + | _ -> + Log.debugf 0 (fun k -> k "Error while proving atom %a" Atom.debug a); + raise (Resolution_error "Cannot prove atom") + + let prove_unsat conflict = + if Array.length conflict.atoms = 0 then conflict + else ( + Log.debugf 1 (fun k -> k "Proving unsat from: @[%a@]" Clause.debug conflict); + let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.atoms in + let res = Clause.make [] (History (conflict :: l)) in + Log.debugf 1 (fun k -> k "Proof found: @[%a@]" Clause.debug res); + res + ) + + let prove_atom a = + if (a.is_true && a.var.v_level = 0) then + Some (set_atom_proof a) + else + None + + (* Interface exposed *) + type t = clause + and proof_node = { + conclusion : clause; + step : step; + } + and step = + | Hypothesis + | Assumption + | Lemma of lemma + | Duplicate of t * atom list + | Resolution of t * t * atom + + let rec chain_res (c, cl) = function + | d :: r -> + Log.debugf 5 + (fun k -> k " Resolving clauses : @[%a@\n%a@]" Clause.debug c Clause.debug d); + let dl = to_list d in + begin match resolve (merge cl dl) with + | [ a ], l -> + begin match r with + | [] -> (l, c, d, a) + | _ -> + let new_clause = Clause.make l (History [c; d]) in + chain_res (new_clause, l) r + end + | _ -> + Log.debugf 5 + (fun k -> k "While resolving clauses:@[%a@\n%a@]" + Clause.debug c Clause.debug d); + raise (Resolution_error "Clause mismatch") + end + | _ -> + raise (Resolution_error "Bad history") + + let[@inline] conclusion (p:t) : clause = p + + let expand conclusion = + Log.debugf 5 (fun k -> k "Expanding : @[%a@]" Clause.debug conclusion); + match conclusion.cpremise with + | Lemma l -> + {conclusion; step = Lemma l; } + | Hyp -> + { conclusion; step = Hypothesis; } + | Local -> + { conclusion; step = Assumption; } + | History [] -> + Log.debugf 0 (fun k -> k "Empty history for clause: %a" Clause.debug conclusion); + raise (Resolution_error "Empty history") + | History [ c ] -> + let duplicates, res = analyze (list c) in + assert (cmp_cl res (list conclusion) = 0); + { conclusion; step = Duplicate (c, List.concat duplicates) } + | History ( c :: ([_] as r)) -> + let (l, c', d', a) = chain_res (c, to_list c) r in + assert (cmp_cl l (to_list conclusion) = 0); + { conclusion; step = Resolution (c', d', a); } + | History ( c :: r ) -> + let (l, c', d', a) = chain_res (c, to_list c) r in + conclusion.cpremise <- History [c'; d']; + assert (cmp_cl l (to_list conclusion) = 0); + { conclusion; step = Resolution (c', d', a); } + + (* Proof nodes manipulation *) + let is_leaf = function + | Hypothesis + | Assumption + | Lemma _ -> true + | Duplicate _ + | Resolution _ -> false + + let parents = function + | Hypothesis + | Assumption + | Lemma _ -> [] + | Duplicate (p, _) -> [p] + | Resolution (p, p', _) -> [p; p'] + + let expl = function + | Hypothesis -> "hypothesis" + | Assumption -> "assumption" + | Lemma _ -> "lemma" + | Duplicate _ -> "duplicate" + | Resolution _ -> "resolution" + + (* Compute unsat-core + TODO: replace visited bool by a int unique to each call + of unsat_core, so that the cleanup can be removed ? *) + let unsat_core proof = + let rec aux res acc = function + | [] -> res, acc + | c :: r -> + if not c.visited then ( + c.visited <- true; + match c.cpremise with + | Hyp | Local | Lemma _ -> aux (c :: res) acc r + | History h -> + let l = List.fold_left (fun acc c -> + if not c.visited then c :: acc else acc) r h in + aux res (c :: acc) l + ) else ( + aux res acc r + ) + in + let res, tmp = aux [] [] [proof] in + List.iter (fun c -> c.visited <- false) res; + List.iter (fun c -> c.visited <- false) tmp; + res + + module Tbl = Clause.Tbl + + type task = + | Enter of t + | Leaving of t + + let spop s = try Some (Stack.pop s) with Stack.Empty -> None + + let rec fold_aux s h f acc = + match spop s with + | None -> acc + | Some (Leaving c) -> + Tbl.add h c true; + fold_aux s h f (f acc (expand c)) + | Some (Enter c) -> + if not (Tbl.mem h c) then begin + Stack.push (Leaving c) s; + let node = expand c in + begin match node.step with + | Duplicate (p1, _) -> + Stack.push (Enter p1) s + | Resolution (p1, p2, _) -> + Stack.push (Enter p2) s; + Stack.push (Enter p1) s + | Hypothesis | Assumption | Lemma _ -> () + end + end; + fold_aux s h f acc + + let fold f acc p = + let h = Tbl.create 42 in + let s = Stack.create () in + Stack.push (Enter p) s; + fold_aux s h f acc + + let check p = fold (fun () _ -> ()) () p + end + type proof = Proof.t module H = (Heap.Make [@specialise]) (struct - type t = St.Elt.t + type t = Elt.t let[@inline] cmp i j = Elt.weight j < Elt.weight i (* comparison by weight *) let idx = Elt.idx let set_idx = Elt.set_idx end) - exception Sat - exception Unsat + exception E_sat + exception E_unsat exception UndecidedLit exception Restart exception Conflict of clause @@ -50,9 +687,8 @@ module Make (* Singleton type containing the current state *) type t = { - st : St.t; - - th: Plugin.t; + st : st; + th: theory; (* Clauses are simplified for eficiency purposes. In the following vectors, the comments actually refer to the original non-simplified @@ -62,6 +698,7 @@ module Make (* clauses added by the user *) clauses_learnt : clause Vec.t; (* learnt clauses (tautologies true at any time, whatever the user level) *) + (* TODO: remove, replace by vec of assumptions *) clauses_temp : clause Vec.t; (* Temp clauses, corresponding to the local assumptions. This vec is used only to have an efficient way to access the list of local assumptions. *) @@ -85,6 +722,7 @@ module Make th_levels : Plugin.level Vec.t; (* theory states corresponding to elt_levels *) + (* TODO: remove *) user_levels : int Vec.t; (* user levels in [clauses_temp] *) @@ -120,10 +758,12 @@ module Make (* initial limit for the number of learnt clauses, 1/3 of initial number of clauses by default *) + (* TODO: remove *) mutable dirty: bool; (* is there a [pop()] on top of the stack for examining current model/proof? *) } + type solver = t (* Starting environment. *) let create_ ~st (th:theory) : t = { @@ -158,7 +798,7 @@ module Make } let create ?(size=`Big) (th:theory) : t = - let st = St.create ~size () in + let st = create_st ~size () in create_ ~st th (* Misc functions *) @@ -186,16 +826,16 @@ module Make of subterms of each formula, so we have a field [v_assignable] directly in variables to do so. *) let iter_sub f v = - if St.mcsat then + if Plugin.mcsat then match v.v_assignable with | Some l -> List.iter f l | None -> assert false (* When we have a new literal, we need to first create the list of its subterms. *) - let mk_atom st (f:St.formula) : atom = + let mk_atom st (f:formula) : atom = let res = Atom.make st.st f in - if St.mcsat then ( + if Plugin.mcsat then ( begin match res.var.v_assignable with | Some _ -> () | None -> @@ -217,12 +857,13 @@ module Make *) let rec insert_var_order st (elt:elt) : unit = H.insert st.order elt; - begin match elt with + if Plugin.mcsat then ( + match elt with | E_lit _ -> () | E_var v -> insert_subterms_order st v - end + ) - and insert_subterms_order st (v:St.var) : unit = + and insert_subterms_order st (v:var) : unit = iter_sub (fun t -> insert_var_order st (Elt.of_lit t)) v (* Add new litterals/atoms on which to decide on, even if there is no @@ -233,9 +874,10 @@ module Make let l = Lit.make st.st t in insert_var_order st (E_lit l) - let new_atom st (p:formula) : unit = + let make_atom_ st (p:formula) : atom = let a = mk_atom st p in - insert_var_order st (E_var a.var) + insert_var_order st (E_var a.var); + a (* Rather than iterate over all the heap when we want to decrease all the variables/literals activity, we instead increase the value by which @@ -250,8 +892,8 @@ module Make let var_bump_activity_aux st v = v.v_weight <- v.v_weight +. st.var_incr; if v.v_weight > 1e100 then ( - for i = 0 to St.nb_elt st.st - 1 do - Elt.set_weight (St.get_elt st.st i) ((Elt.weight (St.get_elt st.st i)) *. 1e-100) + for i = 0 to nb_elt st.st - 1 do + Elt.set_weight (get_elt st.st i) ((Elt.weight (get_elt st.st i)) *. 1e-100) done; st.var_incr <- st.var_incr *. 1e-100; ); @@ -264,8 +906,8 @@ module Make let lit_bump_activity_aux st (l:lit): unit = l.l_weight <- l.l_weight +. st.var_incr; if l.l_weight > 1e100 then ( - for i = 0 to St.nb_elt st.st - 1 do - Elt.set_weight (St.get_elt st.st i) ((Elt.weight (St.get_elt st.st i)) *. 1e-100) + for i = 0 to nb_elt st.st - 1 do + Elt.set_weight (get_elt st.st i) ((Elt.weight (get_elt st.st i)) *. 1e-100) done; st.var_incr <- st.var_incr *. 1e-100; ); @@ -473,7 +1115,7 @@ module Make let report_unsat st confl : _ = Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" Clause.debug confl); st.unsat_conflict <- Some confl; - raise Unsat + raise E_unsat (* Simplification of boolean propagation reasons. When doing boolean propagation *at level 0*, it can happen @@ -588,8 +1230,8 @@ module Make let th_eval st a : bool option = if a.is_true || a.neg.is_true then None else match Plugin.eval st.th a.lit with - | Plugin_intf.Unknown -> None - | Plugin_intf.Valued (b, l) -> + | Solver_intf.Unknown -> None + | Solver_intf.Valued (b, l) -> if l = [] then raise (Invalid_argument ( Format.asprintf "msat:core/internal.ml: %s" @@ -731,7 +1373,7 @@ module Make let[@inline] analyze st c_clause : conflict_res = analyze_sat st c_clause (* - if St.mcsat + if mcsat then analyze_mcsat c_clause else analyze_sat c_clause *) @@ -959,22 +1601,22 @@ module Make let slice_get st i = match Vec.get st.trail i with | Atom a -> - Plugin_intf.Lit a.lit + Solver_intf.Lit a.lit | Lit {term; assigned = Some v; _} -> - Plugin_intf.Assign (term, v) + Solver_intf.Assign (term, v) | Lit _ -> assert false - let slice_push st (l:formula list) (lemma:proof): unit = + let slice_push st (l:formula list) (lemma:lemma): unit = let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); Stack.push c st.clauses_to_add let slice_propagate (st:t) f = function - | Plugin_intf.Eval l -> + | Solver_intf.Eval l -> let a = mk_atom st f in enqueue_semantic st a l - | Plugin_intf.Consequence (causes, proof) -> + | Solver_intf.Consequence (causes, proof) -> let l = List.rev_map (mk_atom st) causes in if List.for_all (fun a -> a.is_true) l then ( let p = mk_atom st f in @@ -991,8 +1633,8 @@ module Make invalid_arg "Msat.Internal.slice_propagate" ) - let current_slice st : (_,_,_) Plugin_intf.slice = { - Plugin_intf.start = st.th_head; + let current_slice st : (_,_,_) Solver_intf.slice = { + Solver_intf.start = st.th_head; length = Vec.size st.trail - st.th_head; get = slice_get st; push = slice_push st; @@ -1000,8 +1642,8 @@ module Make } (* full slice, for [if_sat] final check *) - let full_slice st : (_,_,_) Plugin_intf.slice = { - Plugin_intf.start = 0; + let full_slice st : (_,_,_) Solver_intf.slice = { + Solver_intf.start = 0; length = Vec.size st.trail; get = slice_get st; push = slice_push st; @@ -1020,9 +1662,9 @@ module Make let slice = current_slice st in st.th_head <- st.elt_head; (* catch up *) match Plugin.assume st.th slice with - | Plugin_intf.Sat -> + | Solver_intf.Th_sat -> propagate st - | Plugin_intf.Unsat (l, p) -> + | Solver_intf.Th_unsat (l, p) -> (* conflict *) let l = List.rev_map (create_atom st) l in (* Assert that the conflcit is indeeed a conflict *) @@ -1031,7 +1673,7 @@ module Make ); List.iter (fun a -> insert_var_order st (Elt.of_var a.var)) l; (* Create the clause and return it. *) - let c = St.Clause.make l (Lemma p) in + let c = Clause.make l (Lemma p) in Some c ) @@ -1072,14 +1714,20 @@ module Make if v.v_level >= 0 then ( assert (v.pa.is_true || v.na.is_true); pick_branch_lit st - ) else match Plugin.eval st.th atom.lit with - | Plugin_intf.Unknown -> + ) else if Plugin.mcsat then ( + match Plugin.eval st.th atom.lit with + | Solver_intf.Unknown -> new_decision_level st; let current_level = decision_level st in enqueue_bool st atom ~level:current_level Decision - | Plugin_intf.Valued (b, l) -> + | Solver_intf.Valued (b, l) -> let a = if b then atom else atom.neg in enqueue_semantic st a l + ) else ( + new_decision_level st; + let current_level = decision_level st in + enqueue_bool st atom ~level:current_level Decision + ) and pick_branch_lit st = match st.next_decision with @@ -1099,7 +1747,7 @@ module Make ) | E_var v -> pick_branch_aux st v.pa - | exception Not_found -> raise Sat + | exception Not_found -> raise E_sat end (* do some amount of search, until the number of conflicts or clause learnt @@ -1122,8 +1770,7 @@ module Make | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); assert (st.elt_head = st.th_head); - if Vec.size st.trail = St.nb_elt st.st - then raise Sat; + if Vec.size st.trail = nb_elt st.st then raise E_sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( Log.debug info "Restarting..."; cancel_until st (base_level st); @@ -1138,19 +1785,18 @@ module Make pick_branch_lit st done - let eval_level (st:t) lit = - let var, negated = Var.make st.st lit in - if not var.pa.is_true && not var.na.is_true - then raise UndecidedLit - else assert (var.v_level >= 0); - let truth = var.pa.is_true in - let value = match negated with - | Formula_intf.Negated -> not truth - | Formula_intf.Same_sign -> truth - in - value, var.v_level + let eval_level (_st:t) (a:atom) = + let lvl = a.var.v_level in + if Atom.is_true a then ( + assert (lvl >= 0); + true, lvl + ) else if Atom.is_false a then ( + false, lvl + ) else ( + raise UndecidedLit + ) - let eval st lit = fst (eval_level st lit) + let[@inline] eval st lit = fst @@ eval_level st lit let[@inline] unsat_conflict st = st.unsat_conflict @@ -1166,7 +1812,7 @@ module Make conflict is reached *) let solve (st:t) : unit = Log.debug 5 "solve"; - if is_unsat st then raise Unsat; + if is_unsat st then raise E_unsat; let n_of_conflicts = ref (to_float st.restart_first) in let n_of_learnts = ref ((to_float (nb_clauses st)) *. st.learntsize_factor) in try @@ -1177,20 +1823,20 @@ module Make | Restart -> n_of_conflicts := !n_of_conflicts *. restart_inc; n_of_learnts := !n_of_learnts *. learntsize_inc - | Sat -> + | E_sat -> assert (st.elt_head = Vec.size st.trail); begin match Plugin.if_sat st.th (full_slice st) with - | Plugin_intf.Sat -> () - | Plugin_intf.Unsat (l, p) -> + | Solver_intf.Th_sat -> () + | Solver_intf.Th_unsat (l, p) -> let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma p) in Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); Stack.push c st.clauses_to_add end; - if Stack.is_empty st.clauses_to_add then raise Sat + if Stack.is_empty st.clauses_to_add then raise E_sat end done - with Sat -> () + with E_sat -> () let assume st ?tag cnf = List.iter @@ -1205,7 +1851,7 @@ module Make let push st : unit = Log.debug debug "Pushing a new user level"; match st.unsat_conflict with - | Some _ -> raise Unsat + | Some _ -> raise E_unsat | None -> cancel_until st (base_level st); Log.debugf debug @@ -1244,9 +1890,8 @@ module Make ) (* Add local hyps to the current decision level *) - let local (st:t) (l:_ list) : unit = - let aux lit = - let a = mk_atom st lit in + let local (st:t) (l:atom list) : unit = + let aux a = Log.debugf info (fun k-> k "Local assumption: @[%a@]" Atom.debug a); assert (decision_level st = base_level st); if not a.is_true then ( @@ -1313,6 +1958,159 @@ module Make let trail env = env.trail -end -[@@inline] + (* Result type *) + type res = + | Sat of (term,atom) Solver_intf.sat_state + | Unsat of (clause,Proof.t) Solver_intf.unsat_state + + let pp_all st lvl status = + Log.debugf lvl + (fun k -> k + "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ + @[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." + status + (Vec.pp ~sep:"" Trail_elt.debug) (trail st) + (Vec.pp ~sep:"" Clause.debug) (temp st) + (Vec.pp ~sep:"" Clause.debug) (hyps st) + (Vec.pp ~sep:"" Clause.debug) (history st) + ) + + let mk_sat (st:t) : (_,_) Solver_intf.sat_state = + pp_all st 99 "SAT"; + let t = trail st in + let iter f f' = + Vec.iter (function + | Atom a -> f a + | Lit l -> f' l.term) + t + in + { Solver_intf. + eval = eval st; + eval_level = eval_level st; + iter_trail = iter; + model = (fun () -> model st); + } + + let mk_unsat (st:t) : (_,_) Solver_intf.unsat_state = + pp_all st 99 "UNSAT"; + let unsat_conflict () = + match unsat_conflict st with + | None -> assert false + | Some c -> c + in + let get_proof () = + let c = unsat_conflict () in + Proof.prove_unsat c + in + { Solver_intf.unsat_conflict; get_proof; } + + (* clean local state *) + let[@inline] cleanup_ (st:t) : unit = + if st.dirty then ( + pop st; (* reset *) + st.dirty <- false; + ) + + (* Wrappers around internal functions*) + let[@inline] assume st ?tag cls : unit = + cleanup_ st; + assume st ?tag cls + + let[@inline] add_clause st ?tag c : unit = + cleanup_ st; + let c = Clause.make ?tag c Hyp in + add_clause st c + + let solve (st:t) ?(assumptions=[]) () = + cleanup_ st; + try + push st; + st.dirty <- true; (* to call [pop] before any other action *) + local st assumptions; + solve st; + Sat (mk_sat st) + with E_unsat -> + Unsat (mk_unsat st) + + let[@inline] push st = + cleanup_ st; + push st + + let[@inline] pop st = + cleanup_ st; + pop st + + let unsat_core = Proof.unsat_core + + let true_at_level0 st a = + try + let b, lev = eval_level st a in + b && lev = 0 + with UndecidedLit -> false + + (* TODO: remove *) + let get_tag cl = cl.tag + + let[@inline] new_lit st t = + cleanup_ st; + new_lit st t + + let[@inline] make_atom st a = + cleanup_ st; + make_atom_ st a + + let export (st:t) : clause Solver_intf.export = + let hyps = hyps st in + let history = history st in + let local = temp st in + {Solver_intf.hyps; history; local} +end +[@@inline][@@specialise] + +module Make_cdcl_t(Plugin : Solver_intf.PLUGIN_CDCL_T) = + Make(struct + include Plugin + module Term = struct + type t = Solver_intf.void + let equal _ _ = assert false + let hash _ = assert false + let pp _ _ = assert false + end + let eval _ _ = Solver_intf.Unknown + let assign _ t = t + let mcsat = false + let iter_assignable _ _ _ = () + end) +[@@inline][@@specialise] + +module Make_mcsat(Plugin : Solver_intf.PLUGIN_MCSAT) = + Make(struct + include Plugin + let mcsat = true + end) +[@@inline][@@specialise] + +module Make_pure_sat(F: Solver_intf.FORMULA) = + Make(struct + module Formula = F + module Term = struct + type t = Solver_intf.void + let equal _ _ = true + let hash _ = 1 + let pp out _ = Format.pp_print_string out "()" + end + type t = unit + type proof = Solver_intf.void + type level = unit + let current_level () = () + let assume () _ = Solver_intf.Th_sat + let if_sat () _ = Solver_intf.Th_sat + let backtrack () _ = () + let eval () _ = Solver_intf.Unknown + let assign () t = t + let mcsat = false + let iter_assignable () _ _ = () + let mcsat = false + end) +[@@inline][@@specialise] diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 6b15cf2f..1dd6b961 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -1,15 +1,15 @@ (** Main API *) -module Formula_intf = Formula_intf -module Plugin_intf = Plugin_intf -module Theory_intf = Theory_intf -module Expr_intf = Expr_intf -module Solver_types_intf = Solver_types_intf -module Res = Res +module Solver_intf = Solver_intf module type S = Solver_intf.S +module type FORMULA = Solver_intf.FORMULA +module type EXPR = Solver_intf.EXPR +module type PLUGIN_CDCL_T = Solver_intf.PLUGIN_CDCL_T +module type PLUGIN_MCSAT = Solver_intf.PLUGIN_MCSAT +module type PROOF = Solver_intf.PROOF type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { eval : 'form -> bool; @@ -27,13 +27,15 @@ type 'clause export = 'clause Solver_intf.export = { history : 'clause Vec.t; local : 'clause Vec.t; } +type ('formula, 'proof) th_res = ('formula, 'proof) Solver_intf.th_res = + | Th_sat + | Th_unsat of 'formula list * 'proof -module Make_smt_expr(E : Formula_intf.S) = Solver_types.SatMake(E) -module Make_mcsat_expr(E : Expr_intf.S) = Solver_types.McMake(E) +type negated = Solver_intf.negated = Negated | Same_sign -module Make = Solver.Make - -module Make_dummy = Plugin_intf.Dummy +module Make_mcsat = Solver.Make_mcsat +module Make_cdcl_t = Solver.Make_cdcl_t +module Make_pure_sat = Solver.Make_pure_sat (**/**) module Vec = Vec diff --git a/src/core/Plugin_intf.ml b/src/core/Plugin_intf.ml deleted file mode 100644 index 98e87f53..00000000 --- a/src/core/Plugin_intf.ml +++ /dev/null @@ -1,128 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) - -(** McSat Theory - - This module defines what a theory must implement in order to - be sued in a McSat solver. -*) - -type 'term eval_res = - | Unknown (** The given formula does not have an evaluation *) - | Valued of bool * ('term list) (** The given formula can be evaluated to the given bool. - The list of terms to give is the list of terms that - were effectively used for the evaluation. *) -(** The type of evaluation results for a given formula. - For instance, let's suppose we want to evaluate the formula [x * y = 0], the - following result are correct: - - [Unknown] if neither [x] nor [y] are assigned to a value - - [Valued (true, [x])] if [x] is assigned to [0] - - [Valued (true, [y])] if [y] is assigned to [0] - - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) -*) - -type ('formula, 'proof) res = - | Sat - (** The current set of assumptions is satisfiable. *) - | Unsat of 'formula list * 'proof - (** The current set of assumptions is *NOT* satisfiable, and here is a - theory tautology (with its proof), for which every litteral is false - under the current assumptions. *) -(** Type returned by the theory. Formulas in the unsat clause must come from the - current set of assumptions, i.e must have been encountered in a slice. *) - -type ('term, 'formula) assumption = - | Lit of 'formula (** The given formula is asserted true by the solver *) - | Assign of 'term * 'term (** The first term is assigned to the second *) -(** Asusmptions made by the core SAT solver. *) - -type ('term, 'formula, 'proof) reason = - | Eval of 'term list (** The formula can be evalutaed using the terms in the list *) - | Consequence of 'formula list * 'proof (** [Consequence (l, p)] means that the formulas in [l] imply - the propagated formula [f]. The proof should be a proof of - the clause "[l] implies [f]". *) -(** The type of reasons for propagations of a formula [f]. *) - -type ('term, 'formula, 'proof) slice = { - start : int; (** Start of the slice *) - length : int; (** Length of the slice *) - get : int -> ('term, 'formula) assumption; (** Accessor for the assertions in the slice. - Should only be called on integers [i] s.t. - [start <= i < start + length] *) - push : 'formula list -> 'proof -> unit; (** Add a clause to the solver. *) - propagate : 'formula -> - ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can - evaluate the formula to be true (see the - definition of {!type:eval_res} *) -} -(** The type for a slice of assertions to assume/propagate in the theory. *) - -(** Signature for theories to be given to the Model Constructing Solver. *) -module type S = sig - - type t - (** The plugin state itself *) - - type term - (** The type of terms. Should be compatible with Expr_intf.Term.t*) - - type formula - (** The type of formulas. Should be compatble with Expr_intf.Formula.t *) - - type proof - (** A custom type for the proofs of lemmas produced by the theory. *) - - type level - (** The type for levels to allow backtracking. *) - - val current_level : t -> level - (** Return the current level of the theory (either the empty/beginning state, or the - last level returned by the [assume] function). *) - - val assume : t -> (term, formula, proof) slice -> (formula, proof) res - (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, - and returns the result of the new assumptions. *) - - val if_sat : t -> (term, formula, proof) slice -> (formula, proof) res - (** Called at the end of the search in case a model has been found. If no new clause is - pushed and the function returns [Sat], then proof search ends and 'sat' is returned, - else search is resumed. *) - - val backtrack : t -> level -> unit - (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the - same state as when it returned the value [l], *) - - val assign : t -> term -> term - (** Returns an assignment value for the given term. *) - - val iter_assignable : t -> (term -> unit) -> formula -> unit - (** An iterator over the subterms of a formula that should be assigned a value (usually the poure subterms) *) - - val eval : t -> formula -> term eval_res - (** Returns the evaluation of the formula in the current assignment *) - -end - -module Dummy(F: Solver_types.S) - : S with type formula = F.formula - and type term = F.term - and type proof = F.proof - and type t = unit -= struct - type t = unit - type formula = F.formula - type term = F.term - type proof = F.proof - type level = unit - let current_level () = () - let assume () _ = Sat - let if_sat () _ = Sat - let backtrack () _ = () - let eval () _ = Unknown - let assign () t = t - let mcsat = false - let iter_assignable () _ _ = () -end diff --git a/src/core/Res.ml b/src/core/Res.ml deleted file mode 100644 index 0114dbb7..00000000 --- a/src/core/Res.ml +++ /dev/null @@ -1,302 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module type S = Res_intf.S -module type FULL = Res_intf.FULL - -module Make(St : Solver_types.S) = struct - - module St = St - - type formula = St.formula - type lemma = St.proof - type clause = St.clause - type atom = St.atom - - exception Insuficient_hyps - exception Resolution_error of string - - (* Log levels *) - let error = 1 - let warn = 3 - let info = 10 - let debug = 80 - - let equal_atoms a b = St.(a.aid) = St.(b.aid) - let compare_atoms a b = Pervasives.compare St.(a.aid) St.(b.aid) - - module Clause = St.Clause - module Atom = St.Atom - - let merge = List.merge compare_atoms - - let _c = ref 0 - let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c) - - (* Compute resolution of 2 clauses *) - let resolve l = - let rec aux resolved acc = function - | [] -> resolved, acc - | [a] -> resolved, a :: acc - | a :: b :: r -> - if equal_atoms a b then - aux resolved (a :: acc) r - else if equal_atoms St.(a.neg) b then - aux (St.(a.var.pa) :: resolved) acc r - else - aux resolved (a :: acc) (b :: r) - in - let resolved, new_clause = aux [] [] l in - resolved, List.rev new_clause - - (* Compute the set of doublons of a clause *) - let list c = List.sort St.Atom.compare (Array.to_list St.(c.atoms)) - - let analyze cl = - let rec aux duplicates free = function - | [] -> duplicates, free - | [ x ] -> duplicates, x :: free - | x :: ((y :: r) as l) -> - if x == y then - count duplicates (x :: free) x [y] r - else - aux duplicates (x :: free) l - and count duplicates free x acc = function - | (y :: r) when x == y -> - count duplicates free x (y :: acc) r - | l -> - aux (acc :: duplicates) free l - in - let doublons, acc = aux [] [] cl in - doublons, List.rev acc - - let to_list c = - let cl = list c in - let doublons, l = analyze cl in - let conflicts, _ = resolve l in - if doublons <> [] then - Log.debug warn "Input clause has redundancies"; - if conflicts <> [] then - Log.debug warn "Input clause is a tautology"; - cl - - (* - let pp_cl fmt l = - let rec aux fmt = function - | [] -> () - | a :: r -> - Format.fprintf fmt "%a@,%a" St.pp_atom a aux r - in - Format.fprintf fmt "@[%a@]" aux l - *) - - (* Comparison of clauses *) - let cmp_cl c d = - let rec aux = function - | [], [] -> 0 - | a :: r, a' :: r' -> - begin match compare_atoms a a' with - | 0 -> aux (r, r') - | x -> x - end - | _ :: _ , [] -> -1 - | [], _ :: _ -> 1 - in - aux (c, d) - - let[@inline] prove conclusion = - assert St.(conclusion.cpremise <> History []); - conclusion - - let rec set_atom_proof a = - let aux acc b = - if equal_atoms a.St.neg b then acc - else set_atom_proof b :: acc - in - assert St.(a.var.v_level >= 0); - match St.(a.var.reason) with - | Some St.Bcp c -> - Log.debugf debug (fun k->k "Analysing: @[%a@ %a@]" St.Atom.debug a St.Clause.debug c); - if Array.length c.St.atoms = 1 then ( - Log.debugf debug (fun k -> k "Old reason: @[%a@]" St.Atom.debug a); - c - ) else ( - assert (a.St.neg.St.is_true); - let r = St.History (c :: (Array.fold_left aux [] c.St.atoms)) in - let c' = St.Clause.make [a.St.neg] r in - a.St.var.St.reason <- Some St.(Bcp c'); - Log.debugf debug - (fun k -> k "New reason: @[%a@ %a@]" St.Atom.debug a St.Clause.debug c'); - c' - ) - | _ -> - Log.debugf error (fun k -> k "Error while proving atom %a" St.Atom.debug a); - raise (Resolution_error "Cannot prove atom") - - let prove_unsat conflict = - if Array.length conflict.St.atoms = 0 then conflict - else ( - Log.debugf info (fun k -> k "Proving unsat from: @[%a@]" St.Clause.debug conflict); - let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.St.atoms in - let res = St.Clause.make [] (St.History (conflict :: l)) in - Log.debugf info (fun k -> k "Proof found: @[%a@]" St.Clause.debug res); - res - ) - - let prove_atom a = - if St.(a.is_true && a.var.v_level = 0) then - Some (set_atom_proof a) - else - None - - (* Interface exposed *) - type proof = clause - and proof_node = { - conclusion : clause; - step : step; - } - and step = - | Hypothesis - | Assumption - | Lemma of lemma - | Duplicate of proof * atom list - | Resolution of proof * proof * atom - - let rec chain_res (c, cl) = function - | d :: r -> - Log.debugf debug - (fun k -> k " Resolving clauses : @[%a@\n%a@]" St.Clause.debug c St.Clause.debug d); - let dl = to_list d in - begin match resolve (merge cl dl) with - | [ a ], l -> - begin match r with - | [] -> (l, c, d, a) - | _ -> - let new_clause = St.Clause.make l (St.History [c; d]) in - chain_res (new_clause, l) r - end - | _ -> - Log.debugf error - (fun k -> k "While resolving clauses:@[%a@\n%a@]" - St.Clause.debug c St.Clause.debug d); - raise (Resolution_error "Clause mismatch") - end - | _ -> - raise (Resolution_error "Bad history") - - let[@inline] conclusion (p:proof) : clause = p - - let expand conclusion = - Log.debugf debug (fun k -> k "Expanding : @[%a@]" St.Clause.debug conclusion); - match conclusion.St.cpremise with - | St.Lemma l -> - {conclusion; step = Lemma l; } - | St.Hyp -> - { conclusion; step = Hypothesis; } - | St.Local -> - { conclusion; step = Assumption; } - | St.History [] -> - Log.debugf error (fun k -> k "Empty history for clause: %a" St.Clause.debug conclusion); - raise (Resolution_error "Empty history") - | St.History [ c ] -> - let duplicates, res = analyze (list c) in - assert (cmp_cl res (list conclusion) = 0); - { conclusion; step = Duplicate (c, List.concat duplicates) } - | St.History ( c :: ([_] as r)) -> - let (l, c', d', a) = chain_res (c, to_list c) r in - assert (cmp_cl l (to_list conclusion) = 0); - { conclusion; step = Resolution (c', d', a); } - | St.History ( c :: r ) -> - let (l, c', d', a) = chain_res (c, to_list c) r in - conclusion.St.cpremise <- St.History [c'; d']; - assert (cmp_cl l (to_list conclusion) = 0); - { conclusion; step = Resolution (c', d', a); } - - (* Proof nodes manipulation *) - let is_leaf = function - | Hypothesis - | Assumption - | Lemma _ -> true - | Duplicate _ - | Resolution _ -> false - - let parents = function - | Hypothesis - | Assumption - | Lemma _ -> [] - | Duplicate (p, _) -> [p] - | Resolution (p, p', _) -> [p; p'] - - let expl = function - | Hypothesis -> "hypothesis" - | Assumption -> "assumption" - | Lemma _ -> "lemma" - | Duplicate _ -> "duplicate" - | Resolution _ -> "resolution" - - (* Compute unsat-core - TODO: replace visited bool by a int unique to each call - of unsat_core, so that the cleanup can be removed ? *) - let unsat_core proof = - let rec aux res acc = function - | [] -> res, acc - | c :: r -> - if not c.St.visited then ( - c.St.visited <- true; - match c.St.cpremise with - | St.Hyp | St.Local | St.Lemma _ -> aux (c :: res) acc r - | St.History h -> - let l = List.fold_left (fun acc c -> - if not c.St.visited then c :: acc else acc) r h in - aux res (c :: acc) l - ) else ( - aux res acc r - ) - in - let res, tmp = aux [] [] [proof] in - List.iter (fun c -> c.St.visited <- false) res; - List.iter (fun c -> c.St.visited <- false) tmp; - res - - module Tbl = Clause.Tbl - - type task = - | Enter of proof - | Leaving of proof - - let spop s = try Some (Stack.pop s) with Stack.Empty -> None - - let rec fold_aux s h f acc = - match spop s with - | None -> acc - | Some (Leaving c) -> - Tbl.add h c true; - fold_aux s h f (f acc (expand c)) - | Some (Enter c) -> - if not (Tbl.mem h c) then begin - Stack.push (Leaving c) s; - let node = expand c in - begin match node.step with - | Duplicate (p1, _) -> - Stack.push (Enter p1) s - | Resolution (p1, p2, _) -> - Stack.push (Enter p2) s; - Stack.push (Enter p1) s - | Hypothesis | Assumption | Lemma _ -> () - end - end; - fold_aux s h f acc - - let fold f acc p = - let h = Tbl.create 42 in - let s = Stack.create () in - Stack.push (Enter p) s; - fold_aux s h f acc - - let check p = fold (fun () _ -> ()) () p - -end - diff --git a/src/core/Res.mli b/src/core/Res.mli deleted file mode 100644 index 06b9c647..00000000 --- a/src/core/Res.mli +++ /dev/null @@ -1,19 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Resolution proofs - - This modules defines functions to create and manipulate resolution proofs. -*) - -module type S = Res_intf.S -(** Interface for a module manipulating resolution proofs. *) - -module type FULL = Res_intf.FULL - -module Make : functor (St : Solver_types.S) -> FULL with module St = St -(** Functor to create a module building proofs from a sat-solver unsat trace. *) - diff --git a/src/core/Res_intf.ml b/src/core/Res_intf.ml deleted file mode 100644 index 2106a3bc..00000000 --- a/src/core/Res_intf.ml +++ /dev/null @@ -1,136 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Interface for proofs *) - -type 'a printer = Format.formatter -> 'a -> unit - -module type S = sig - (** Signature for a module handling proof by resolution from sat solving traces *) - - (** {3 Type declarations} *) - - exception Insuficient_hyps - (** Raised when a complete resolution derivation cannot be found using the current hypotheses. *) - - type formula - type atom - type lemma - type clause - (** Abstract types for atoms, clauses and theory-specific lemmas *) - - type proof - (** Lazy type for proof trees. Proofs are persistent objects, and can be - extended to proof nodes using functions defined later. *) - - and proof_node = { - conclusion : clause; (** The conclusion of the proof *) - step : step; (** The reasoning step used to prove the conclusion *) - } - (** A proof can be expanded into a proof node, which show the first step of the proof. *) - - (** The type of reasoning steps allowed in a proof. *) - and step = - | Hypothesis - (** The conclusion is a user-provided hypothesis *) - | Assumption - (** The conclusion has been locally assumed by the user *) - | Lemma of lemma - (** The conclusion is a tautology provided by the theory, with associated proof *) - | Duplicate of proof * atom list - (** The conclusion is obtained by eliminating multiple occurences of the atom in - the conclusion of the provided proof. *) - | Resolution of proof * proof * atom - (** The conclusion can be deduced by performing a resolution between the conclusions - of the two given proofs. The atom on which to perform the resolution is also given. *) - - (** {3 Proof building functions} *) - - val prove : clause -> proof - (** Given a clause, return a proof of that clause. - @raise Insuficient_hyps if it does not succeed. *) - - val prove_unsat : clause -> proof - (** Given a conflict clause [c], returns a proof of the empty clause. - @raise Insuficient_hyps if it does not succeed. *) - - val prove_atom : atom -> proof option - (** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *) - - (** {3 Proof Nodes} *) - - val parents : step -> proof list - (** Returns the parents of a proof node. *) - - val is_leaf : step -> bool - (** Returns wether the the proof node is a leaf, i.e. an hypothesis, - an assumption, or a lemma. - [true] if and only if {!parents} returns the empty list. *) - - val expl : step -> string - (** Returns a short string description for the proof step; for instance - ["hypothesis"] for a [Hypothesis] - (it currently returns the variant name in lowercase). *) - - - (** {3 Proof Manipulation} *) - - val expand : proof -> proof_node - (** Return the proof step at the root of a given proof. *) - - val conclusion : proof -> clause - (** What is proved at the root of the clause *) - - val fold : ('a -> proof_node -> 'a) -> 'a -> proof -> 'a - (** [fold f acc p], fold [f] over the proof [p] and all its node. It is guaranteed that - [f] is executed exactly once on each proof node in the tree, and that the execution of - [f] on a proof node happens after the execution on the parents of the nodes. *) - - val unsat_core : proof -> clause list - (** Returns the unsat_core of the given proof, i.e the lists of conclusions - of all leafs of the proof. - More efficient than using the [fold] function since it has - access to the internal representation of proofs *) - - (** {3 Misc} *) - - val check : proof -> unit - (** Check the contents of a proof. Mainly for internal use *) - - module Clause : sig - type t = clause - val name : t -> string - val atoms : t -> atom array - val atoms_l : t -> atom list - val pp : t printer - (** A nice looking printer for clauses, which sort the atoms before printing. *) - - module Tbl : Hashtbl.S with type key = t - end - - module Atom : sig - type t = atom - val is_pos : t -> bool - val neg : t -> t - val abs : t -> t - val compare : t -> t -> int - val equal : t -> t -> bool - val lit : t -> formula - val pp : t printer - end - - module Tbl : Hashtbl.S with type key = proof -end - -module type FULL = sig - module St : Solver_types.S - (** Module defining atom and clauses *) - - include S with type atom = St.atom - and type lemma = St.proof - and type clause = St.clause - and type formula = St.formula -end diff --git a/src/core/Solver.ml b/src/core/Solver.ml index 6d788347..5863de2a 100644 --- a/src/core/Solver.ml +++ b/src/core/Solver.ml @@ -6,150 +6,7 @@ Copyright 2016 Simon Cruanes module type S = Solver_intf.S -open Solver_intf +module Make_cdcl_t = Internal.Make_cdcl_t +module Make_mcsat = Internal.Make_mcsat +module Make_pure_sat = Internal.Make_pure_sat -module Make - (St : Solver_types.S) - (Th : Plugin_intf.S with type term = St.term - and type formula = St.formula - and type proof = St.proof) -= struct - - module St = St - - module S = Internal.Make(St)(Th) - - module Proof = S.Proof - - exception UndecidedLit = S.UndecidedLit - - type formula = St.formula - type term = St.term - type atom = St.formula - type clause = St.clause - type theory = Th.t - - type t = S.t - type solver = t - - let create = S.create - - (* Result type *) - type res = - | Sat of (St.term,St.formula) sat_state - | Unsat of (St.clause,Proof.proof) unsat_state - - let pp_all st lvl status = - Log.debugf lvl - (fun k -> k - "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ - @[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." - status - (Vec.pp ~sep:"" St.Trail_elt.debug) (S.trail st) - (Vec.pp ~sep:"" St.Clause.debug) (S.temp st) - (Vec.pp ~sep:"" St.Clause.debug) (S.hyps st) - (Vec.pp ~sep:"" St.Clause.debug) (S.history st) - ) - - let mk_sat (st:S.t) : (_,_) sat_state = - pp_all st 99 "SAT"; - let t = S.trail st in - let iter f f' = - Vec.iter (function - | St.Atom a -> f a.St.lit - | St.Lit l -> f' l.St.term) - t - in - { - eval = S.eval st; - eval_level = S.eval_level st; - iter_trail = iter; - model = (fun () -> S.model st); - } - - let mk_unsat (st:S.t) : (_,_) unsat_state = - pp_all st 99 "UNSAT"; - let unsat_conflict () = - match S.unsat_conflict st with - | None -> assert false - | Some c -> c - in - let get_proof () = - let c = unsat_conflict () in - S.Proof.prove_unsat c - in - { unsat_conflict; get_proof; } - - (* clean local state *) - let[@inline] cleanup_ (st:t) : unit = - if st.S.dirty then ( - S.pop st; (* reset *) - st.S.dirty <- false; - ) - - (* Wrappers around internal functions*) - let[@inline] assume st ?tag cls : unit = - cleanup_ st; - S.assume st ?tag cls - - let[@inline] add_clause st c : unit = - cleanup_ st; - S.add_clause st c - - let solve (st:t) ?(assumptions=[]) () = - cleanup_ st; - try - S.push st; - st.S.dirty <- true; (* to call [pop] before any other action *) - S.local st assumptions; - S.solve st; - Sat (mk_sat st) - with S.Unsat -> - Unsat (mk_unsat st) - - let[@inline] push st = - cleanup_ st; - S.push st - - let[@inline] pop st = - cleanup_ st; - S.pop st - - let unsat_core = S.Proof.unsat_core - - let true_at_level0 st a = - try - let b, lev = S.eval_level st a in - b && lev = 0 - with S.UndecidedLit -> false - - let get_tag cl = St.(cl.tag) - - let[@inline] new_lit st t = - cleanup_ st; - S.new_lit st t - - let[@inline] new_atom st a = - cleanup_ st; - S.new_atom st a - - let export (st:t) : St.clause export = - let hyps = S.hyps st in - let history = S.history st in - let local = S.temp st in - {hyps; history; local} - - module Clause = struct - include St.Clause - - let atoms c = St.Clause.atoms c |> Array.map (fun a -> a.St.lit) - let atoms_l c = St.Clause.atoms_l c |> List.map (fun a -> a.St.lit) - - let make st ?tag l = - let l = List.map (S.mk_atom st) l in - St.Clause.make ?tag l St.Hyp - end - - module Formula = St.Formula - module Term = St.Term -end diff --git a/src/core/Solver.mli b/src/core/Solver.mli index 69441e0d..d50fc2e4 100644 --- a/src/core/Solver.mli +++ b/src/core/Solver.mli @@ -13,16 +13,22 @@ Copyright 2014 Simon Cruanes module type S = Solver_intf.S (** Safe external interface of solvers. *) -module Make - (St : Solver_types.S) - (Th : Plugin_intf.S with type term = St.term - and type formula = St.formula - and type proof = St.proof) - : S with type term = St.term - and type formula = St.formula - and type clause = St.clause - and type Proof.lemma = St.proof +module Make_cdcl_t(Th : Solver_intf.PLUGIN_CDCL_T) + : S with type Term.t = Solver_intf.void + and module Formula = Th.Formula + and type lemma = Th.proof and type theory = Th.t -(** Functor to make a safe external interface. *) + +module Make_mcsat(Th : Solver_intf.PLUGIN_MCSAT) + : S with module Term = Th.Term + and module Formula = Th.Formula + and type lemma = Th.proof + and type theory = Th.t + +module Make_pure_sat(F: Solver_intf.FORMULA) + : S with type Term.t = Solver_intf.void + and module Formula = F + and type lemma = Solver_intf.void + and type theory = unit diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index a28cb248..8b17d69f 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -11,6 +11,8 @@ Copyright 2016 Simon Cruanes functor in {!Solver} or {!Mcsolver}. *) +type 'a printer = Format.formatter -> 'a -> unit + type ('term, 'form) sat_state = { eval: 'form -> bool; (** Returns the valuation of a formula in the current state @@ -45,7 +47,279 @@ type 'clause export = { } (** Export internal state *) -type 'a printer = Format.formatter -> 'a -> unit +type negated = + | Negated (** changed sign *) + | Same_sign (** kept sign *) +(** This type is used during the normalisation of formulas. + See {!val:Expr_intf.S.norm} for more details. *) + +type 'term eval_res = + | Unknown (** The given formula does not have an evaluation *) + | Valued of bool * ('term list) (** The given formula can be evaluated to the given bool. + The list of terms to give is the list of terms that + were effectively used for the evaluation. *) +(** The type of evaluation results for a given formula. + For instance, let's suppose we want to evaluate the formula [x * y = 0], the + following result are correct: + - [Unknown] if neither [x] nor [y] are assigned to a value + - [Valued (true, [x])] if [x] is assigned to [0] + - [Valued (true, [y])] if [y] is assigned to [0] + - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) +*) + +type ('formula, 'proof) th_res = + | Th_sat + (** The current set of assumptions is satisfiable. *) + | Th_unsat of 'formula list * 'proof + (** The current set of assumptions is *NOT* satisfiable, and here is a + theory tautology (with its proof), for which every litteral is false + under the current assumptions. *) +(** Type returned by the theory. Formulas in the unsat clause must come from the + current set of assumptions, i.e must have been encountered in a slice. *) + +type ('term, 'formula) assumption = + | Lit of 'formula (** The given formula is asserted true by the solver *) + | Assign of 'term * 'term (** The first term is assigned to the second *) +(** Asusmptions made by the core SAT solver. *) + +type ('term, 'formula, 'proof) reason = + | Eval of 'term list (** The formula can be evalutaed using the terms in the list *) + | Consequence of 'formula list * 'proof (** [Consequence (l, p)] means that the formulas in [l] imply + the propagated formula [f]. The proof should be a proof of + the clause "[l] implies [f]". *) +(** The type of reasons for propagations of a formula [f]. *) + +type ('term, 'formula, 'proof) slice = { + start : int; (** Start of the slice *) + length : int; (** Length of the slice *) + get : int -> ('term, 'formula) assumption; (** Accessor for the assertions in the slice. + Should only be called on integers [i] s.t. + [start <= i < start + length] *) + push : 'formula list -> 'proof -> unit; (** Add a clause to the solver. *) + propagate : 'formula -> + ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can + evaluate the formula to be true (see the + definition of {!type:eval_res} *) +} +(** The type for a slice of assertions to assume/propagate in the theory. *) + +type ('a, 'b) gadt_eq = GADT_EQ : ('a, 'a) gadt_eq + +type void = (unit,bool) gadt_eq +(** A provably empty type *) + +module type FORMULA = sig + (** formulas *) + + type t + (** The type of atomic formulas over terms. *) + + val equal : t -> t -> bool + (** Equality over formulas. *) + + val hash : t -> int + (** Hashing function for formulas. Should be such that two formulas equal according + to {!val:Expr_intf.S.equal} have the same hash. *) + + val pp : t printer + (** Printing function used among other thing for debugging. *) + + val neg : t -> t + (** Formula negation *) + + val norm : t -> t * negated + (** Returns a 'normalized' form of the formula, possibly negated + (in which case return [Negated]). + [norm] must be so that [a] and [neg a] normalise to the same formula, + but one returns [Negated] and the other [Same_sign]. *) +end + +(** Formulas and Terms required for mcSAT *) +module type EXPR = sig + type proof + (** An abstract type for proofs *) + + module Term : sig + type t + (** The type of terms *) + + val equal : t -> t -> bool + (** Equality over terms. *) + + val hash : t -> int + (** Hashing function for terms. Should be such that two terms equal according + to {!val:Expr_intf.S.equal} have the same hash. *) + + val pp : t printer + (** Printing function used among other for debugging. *) + end + + module Formula : FORMULA +end + +(** Signature for theories to be given to the CDCL(T) solver *) +module type PLUGIN_CDCL_T = sig + type t + (** The plugin state itself *) + + module Formula : FORMULA + + type proof + + type level + (** The type for levels to allow backtracking. *) + + val current_level : t -> level + (** Return the current level of the theory (either the empty/beginning state, or the + last level returned by the [assume] function). *) + + val assume : t -> (void, Formula.t, proof) slice -> (Formula.t, proof) th_res + (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, + and returns the result of the new assumptions. *) + + val if_sat : t -> (void, Formula.t, proof) slice -> (Formula.t, proof) th_res + (** Called at the end of the search in case a model has been found. If no new clause is + pushed and the function returns [Sat], then proof search ends and 'sat' is returned, + else search is resumed. *) + + val backtrack : t -> level -> unit + (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the + same state as when it returned the value [l], *) + +end + +(** Signature for theories to be given to the Model Constructing Solver. *) +module type PLUGIN_MCSAT = sig + type t + (** The plugin state itself *) + + include EXPR + + type level + (** The type for levels to allow backtracking. *) + + val current_level : t -> level + (** Return the current level of the theory (either the empty/beginning state, or the + last level returned by the [assume] function). *) + + val assume : t -> (Term.t, Formula.t, proof) slice -> (Formula.t, proof) th_res + (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, + and returns the result of the new assumptions. *) + + val if_sat : t -> (Term.t, Formula.t, proof) slice -> (Formula.t, proof) th_res + (** Called at the end of the search in case a model has been found. If no new clause is + pushed and the function returns [Sat], then proof search ends and 'sat' is returned, + else search is resumed. *) + + val backtrack : t -> level -> unit + (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the + same state as when it returned the value [l], *) + + val assign : t -> Term.t -> Term.t + (** Returns an assignment value for the given term. *) + + val iter_assignable : t -> (Term.t -> unit) -> Formula.t -> unit + (** An iterator over the subTerm.ts of a Formula.t that should be assigned a value (usually the poure subTerm.ts) *) + + val eval : t -> Formula.t -> Term.t eval_res + (** Returns the evaluation of the Formula.t in the current assignment *) + +end + +module type PROOF = sig + (** Signature for a module handling proof by resolution from sat solving traces *) + + (** {3 Type declarations} *) + + exception Insuficient_hyps + (** Raised when a complete resolution derivation cannot be found using the current hypotheses. *) + + type formula + type atom + type lemma + type clause + (** Abstract types for atoms, clauses and theory-specific lemmas *) + + type t + (** Lazy type for proof trees. Proofs are persistent objects, and can be + extended to proof nodes using functions defined later. *) + + and proof_node = { + conclusion : clause; (** The conclusion of the proof *) + step : step; (** The reasoning step used to prove the conclusion *) + } + (** A proof can be expanded into a proof node, which show the first step of the proof. *) + + (** The type of reasoning steps allowed in a proof. *) + and step = + | Hypothesis + (** The conclusion is a user-provided hypothesis *) + | Assumption + (** The conclusion has been locally assumed by the user *) + | Lemma of lemma + (** The conclusion is a tautology provided by the theory, with associated proof *) + | Duplicate of t * atom list + (** The conclusion is obtained by eliminating multiple occurences of the atom in + the conclusion of the provided proof. *) + | Resolution of t * t * atom + (** The conclusion can be deduced by performing a resolution between the conclusions + of the two given proofs. The atom on which to perform the resolution is also given. *) + + (** {3 Proof building functions} *) + + val prove : clause -> t + (** Given a clause, return a proof of that clause. + @raise Insuficient_hyps if it does not succeed. *) + + val prove_unsat : clause -> t + (** Given a conflict clause [c], returns a proof of the empty clause. + @raise Insuficient_hyps if it does not succeed. *) + + val prove_atom : atom -> t option + (** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *) + + (** {3 Proof Nodes} *) + + val parents : step -> t list + (** Returns the parents of a proof node. *) + + val is_leaf : step -> bool + (** Returns wether the the proof node is a leaf, i.e. an hypothesis, + an assumption, or a lemma. + [true] if and only if {!parents} returns the empty list. *) + + val expl : step -> string + (** Returns a short string description for the proof step; for instance + ["hypothesis"] for a [Hypothesis] + (it currently returns the variant name in lowercase). *) + + + (** {3 Proof Manipulation} *) + + val expand : t -> proof_node + (** Return the proof step at the root of a given proof. *) + + val conclusion : t -> clause + (** What is proved at the root of the clause *) + + val fold : ('a -> proof_node -> 'a) -> 'a -> t -> 'a + (** [fold f acc p], fold [f] over the proof [p] and all its node. It is guaranteed that + [f] is executed exactly once on each proof node in the tree, and that the execution of + [f] on a proof node happens after the execution on the parents of the nodes. *) + + val unsat_core : t -> clause list + (** Returns the unsat_core of the given proof, i.e the lists of conclusions + of all leafs of the proof. + More efficient than using the [fold] function since it has + access to the internal representation of proofs *) + + (** {3 Misc} *) + + val check : t -> unit + (** Check the contents of a proof. Mainly for internal use *) + + module Tbl : Hashtbl.S with type key = t +end (** The external interface implemented by safe solvers, such as the one created by the {!Solver.Make} and {!Mcsolver.Make} functors. *) @@ -54,18 +328,61 @@ module type S = sig These are the internal modules used, you should probably not use them if you're not familiar with the internals of mSAT. *) - type term (** user terms *) + include EXPR - type formula (** user formulas *) + type term = Term.t (** user terms *) + + type formula = Formula.t (** user formulas *) + + type atom + (** The type of atoms given by the module argument for formulas. + An atom is a user-defined atomic formula whose truth value is + picked by Msat. *) type clause type theory - module Proof : Res.S with type clause = clause + type lemma + + type solver + + module Atom : sig + type t = atom + + val equal : t -> t -> bool + val compare : t -> t -> int + val hash : t -> int + val neg : t -> t + val sign : t -> bool + val abs : t -> t + val formula : t -> formula + val pp : t printer + end + + module Clause : sig + type t = clause + + val atoms : t -> atom array + val atoms_l : t -> atom list + val tag : t -> int option + val equal : t -> t -> bool + val name : t -> string + + val pp : t printer + + module Tbl : Hashtbl.S with type key = t + end + + module Proof : PROOF + with type clause = clause + and type atom = atom + and type formula = formula + and type lemma = lemma + and type t = proof (** A module to manipulate proofs. *) - type t + type t = solver (** Main solver type, containing all state for solving. *) val create : ?size:[`Tiny|`Small|`Big] -> theory -> t @@ -76,15 +393,10 @@ module type S = sig (** {2 Types} *) - type atom = formula - (** The type of atoms given by the module argument for formulas. - An atom is a user-defined atomic formula whose truth value is - picked by Msat. *) - (** Result type for the solver *) type res = - | Sat of (term,formula) sat_state (** Returned when the solver reaches SAT, with a model *) - | Unsat of (clause,Proof.proof) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) + | Sat of (term,atom) sat_state (** Returned when the solver reaches SAT, with a model *) + | Unsat of (clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit (** Exception raised by the evaluating functions when a literal @@ -92,11 +404,11 @@ module type S = sig (** {2 Base operations} *) - val assume : t -> ?tag:int -> atom list list -> unit + val assume : t -> ?tag:int -> formula list list -> unit (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) - val add_clause : t -> clause -> unit + val add_clause : t -> ?tag:int -> atom list -> unit (** Lower level addition of clauses *) val solve : t -> ?assumptions:atom list -> unit -> res @@ -110,12 +422,12 @@ module type S = sig be decided on at some point during solving, wether it appears in clauses or not. *) - val new_atom : t -> atom -> unit + val make_atom : t -> formula -> atom (** Add a new atom (i.e propositional formula) to the solver. This formula will be decided on at some point during solving, wether it appears in clauses or not. *) - val unsat_core : Proof.proof -> clause list + val unsat_core : Proof.t -> clause list (** Returns the unsat core of a given proof, ie a subset of all the added clauses that is sufficient to establish unsatisfiability. *) @@ -136,32 +448,5 @@ module type S = sig call to [push] *) val export : t -> clause export - - (** {2 Re-export some functions} *) - - type solver = t - - module Clause : sig - type t = clause - - val atoms : t -> atom array - val atoms_l : t -> atom list - val tag : t -> int option - val equal : t -> t -> bool - - val make : solver -> ?tag:int -> atom list -> t - - val pp : t printer - end - - module Formula : sig - type t = formula - val pp : t printer - end - - module Term : sig - type t = term - val pp : t printer - end end diff --git a/src/core/Solver_types.ml b/src/core/Solver_types.ml deleted file mode 100644 index d585f5dd..00000000 --- a/src/core/Solver_types.ml +++ /dev/null @@ -1,420 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) - -module type S = Solver_types_intf.S - -(* Solver types for McSat Solving *) -(* ************************************************************************ *) - -module McMake (E : Expr_intf.S) = struct - - (* Flag for Mcsat v.s Pure Sat *) - let mcsat = true - - type term = E.Term.t - type formula = E.Formula.t - type proof = E.proof - - type seen = - | Nope - | Both - | Positive - | Negative - - type lit = { - lid : int; - term : term; - mutable l_level : int; - mutable l_idx: int; - mutable l_weight : float; - mutable assigned : term option; - } - - type var = { - vid : int; - pa : atom; - na : atom; - mutable v_fields : int; - mutable v_level : int; - mutable v_idx: int; (** position in heap *) - mutable v_weight : float; (** Weight (for the heap), tracking activity *) - mutable v_assignable: lit list option; - mutable reason : reason option; - } - - and atom = { - aid : int; - var : var; - neg : atom; - lit : formula; - mutable is_true : bool; - mutable watched : clause Vec.t; - } - - and clause = { - name : int; - tag : int option; - atoms : atom array; - mutable cpremise : premise; - mutable activity : float; - mutable attached : bool; - mutable visited : bool; - } - - and reason = - | Decision - | Bcp of clause - | Semantic - - and premise = - | Hyp - | Local - | Lemma of proof - | History of clause list - - type elt = - | E_lit of lit - | E_var of var - - type trail_elt = - | Lit of lit - | Atom of atom - - (* Constructors *) - module MF = Hashtbl.Make(E.Formula) - module MT = Hashtbl.Make(E.Term) - - type t = { - t_map: lit MT.t; - f_map: var MF.t; - vars: elt Vec.t; - mutable cpt_mk_var: int; - mutable cpt_mk_clause: int; - } - - type state = t - - let create_ size_map () : t = { - f_map = MF.create size_map; - t_map = MT.create size_map; - vars = Vec.create(); - cpt_mk_var = 0; - cpt_mk_clause = 0; - } - - let create ?(size=`Big) () : t = - let size_map = match size with - | `Tiny -> 8 - | `Small -> 16 - | `Big -> 4096 - in - create_ size_map () - - let nb_elt st = Vec.size st.vars - let get_elt st i = Vec.get st.vars i - let iter_elt st f = Vec.iter f st.vars - - let name_of_clause c = match c.cpremise with - | Hyp -> "H" ^ string_of_int c.name - | Local -> "L" ^ string_of_int c.name - | Lemma _ -> "T" ^ string_of_int c.name - | History _ -> "C" ^ string_of_int c.name - - module Lit = struct - type t = lit - let[@inline] term l = l.term - let[@inline] level l = l.l_level - let[@inline] assigned l = l.assigned - let[@inline] weight l = l.l_weight - - let make (st:state) (t:term) : t = - try MT.find st.t_map t - with Not_found -> - let res = { - lid = st.cpt_mk_var; - term = t; - l_weight = 1.; - l_idx= -1; - l_level = -1; - assigned = None; - } in - st.cpt_mk_var <- st.cpt_mk_var + 1; - MT.add st.t_map t res; - Vec.push st.vars (E_lit res); - res - - let debug_assign fmt v = - match v.assigned with - | None -> - Format.fprintf fmt "" - | Some t -> - Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level E.Term.pp t - - let pp out v = E.Term.pp out v.term - let debug out v = - Format.fprintf out "%d[%a][lit:@[%a@]]" - (v.lid+1) debug_assign v E.Term.pp v.term - end - - let seen_pos = 0b1 - let seen_neg = 0b10 - - module Var = struct - type t = var - let[@inline] level v = v.v_level - let[@inline] pos v = v.pa - let[@inline] neg v = v.na - let[@inline] reason v = v.reason - let[@inline] assignable v = v.v_assignable - let[@inline] weight v = v.v_weight - - let make (st:state) (t:formula) : var * Expr_intf.negated = - let lit, negated = E.Formula.norm t in - try - MF.find st.f_map lit, negated - with Not_found -> - let cpt_double = st.cpt_mk_var lsl 1 in - let rec var = - { vid = st.cpt_mk_var; - pa = pa; - na = na; - v_fields = 0; - v_level = -1; - v_idx= -1; - v_weight = 0.; - v_assignable = None; - reason = None; - } - and pa = - { var = var; - lit = lit; - watched = Vec.create(); - neg = na; - is_true = false; - aid = cpt_double (* aid = vid*2 *) } - and na = - { var = var; - lit = E.Formula.neg lit; - watched = Vec.create(); - neg = pa; - is_true = false; - aid = cpt_double + 1 (* aid = vid*2+1 *) } in - MF.add st.f_map lit var; - st.cpt_mk_var <- st.cpt_mk_var + 1; - Vec.push st.vars (E_var var); - var, negated - - (* Marking helpers *) - let[@inline] clear v = - v.v_fields <- 0 - - let[@inline] seen_both v = - (seen_pos land v.v_fields <> 0) && - (seen_neg land v.v_fields <> 0) - end - - module Atom = struct - type t = atom - let[@inline] level a = a.var.v_level - let[@inline] var a = a.var - let[@inline] neg a = a.neg - let[@inline] abs a = a.var.pa - let[@inline] lit a = a.lit - let[@inline] equal a b = a == b - let[@inline] is_pos a = a == abs a - let[@inline] compare a b = Pervasives.compare a.aid b.aid - let[@inline] reason a = Var.reason a.var - let[@inline] id a = a.aid - let[@inline] is_true a = a.is_true - let[@inline] is_false a = a.neg.is_true - - let[@inline] seen a = - let pos = equal a (abs a) in - if pos - then (seen_pos land a.var.v_fields <> 0) - else (seen_neg land a.var.v_fields <> 0) - - let[@inline] mark a = - let pos = equal a (abs a) in - if pos then ( - a.var.v_fields <- seen_pos lor a.var.v_fields - ) else ( - a.var.v_fields <- seen_neg lor a.var.v_fields - ) - - let[@inline] make st lit = - let var, negated = Var.make st lit in - match negated with - | Formula_intf.Negated -> var.na - | Formula_intf.Same_sign -> var.pa - - let pp fmt a = E.Formula.pp fmt a.lit - - let pp_a fmt v = - if Array.length v = 0 then ( - Format.fprintf fmt "∅" - ) else ( - pp fmt v.(0); - if (Array.length v) > 1 then begin - for i = 1 to (Array.length v) - 1 do - Format.fprintf fmt " ∨ %a" pp v.(i) - done - end - ) - - (* Complete debug printing *) - let sign a = if a == a.var.pa then "+" else "-" - - let debug_reason fmt = function - | n, _ when n < 0 -> - Format.fprintf fmt "%%" - | n, None -> - Format.fprintf fmt "%d" n - | n, Some Decision -> - Format.fprintf fmt "@@%d" n - | n, Some Bcp c -> - Format.fprintf fmt "->%d/%s" n (name_of_clause c) - | n, Some Semantic -> - Format.fprintf fmt "::%d" n - - let pp_level fmt a = - debug_reason fmt (a.var.v_level, a.var.reason) - - let debug_value fmt a = - if a.is_true then - Format.fprintf fmt "T%a" pp_level a - else if a.neg.is_true then - Format.fprintf fmt "F%a" pp_level a - else - Format.fprintf fmt "" - - let debug out a = - Format.fprintf out "%s%d[%a][atom:@[%a@]]" - (sign a) (a.var.vid+1) debug_value a E.Formula.pp a.lit - - let debug_a out vec = - Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec - end - - (* Elements *) - module Elt = struct - type t = elt - let[@inline] of_lit l = E_lit l - let[@inline] of_var v = E_var v - - let[@inline] id = function - | E_lit l -> l.lid | E_var v -> v.vid - let[@inline] level = function - | E_lit l -> l.l_level | E_var v -> v.v_level - let[@inline] idx = function - | E_lit l -> l.l_idx | E_var v -> v.v_idx - let[@inline] weight = function - | E_lit l -> l.l_weight | E_var v -> v.v_weight - - let[@inline] set_level e lvl = match e with - | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl - let[@inline] set_idx e i = match e with - | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i - let[@inline] set_weight e w = match e with - | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w - end - - module Trail_elt = struct - type t = trail_elt - let[@inline] of_lit l = Lit l - let[@inline] of_atom a = Atom a - - let debug fmt = function - | Lit l -> Lit.debug fmt l - | Atom a -> Atom.debug fmt a - end - - module Clause = struct - type t = clause - - let make = - let n = ref 0 in - fun ?tag ali premise -> - let atoms = Array.of_list ali in - let name = !n in - incr n; - { name; - tag = tag; - atoms = atoms; - visited = false; - attached = false; - activity = 0.; - cpremise = premise} - - let empty = make [] (History []) - let name = name_of_clause - let[@inline] equal c1 c2 = c1==c2 - let[@inline] atoms c = c.atoms - let[@inline] atoms_l c = Array.to_list c.atoms - let[@inline] tag c = c.tag - let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms - - let[@inline] premise c = c.cpremise - let[@inline] set_premise c p = c.cpremise <- p - - let[@inline] visited c = c.visited - let[@inline] set_visited c b = c.visited <- b - - let[@inline] attached c = c.attached - let[@inline] set_attached c b = c.attached <- b - - let[@inline] activity c = c.activity - let[@inline] set_activity c w = c.activity <- w - - module Tbl = Hashtbl.Make(struct - type t = clause - let hash = hash - let equal = equal - end) - - let pp fmt c = - Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms - - let debug_premise out = function - | Hyp -> Format.fprintf out "hyp" - | Local -> Format.fprintf out "local" - | Lemma _ -> Format.fprintf out "th_lemma" - | History v -> - List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v - - let debug out ({atoms=arr; cpremise=cp;_}as c) = - Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" - (name c) Atom.debug_a arr debug_premise cp - - let pp_dimacs fmt {atoms;_} = - let aux fmt a = - Array.iter (fun p -> - Format.fprintf fmt "%s%d " - (if p == p.var.pa then "-" else "") - (p.var.vid+1) - ) a - in - Format.fprintf fmt "%a0" aux atoms - end - - module Term = E.Term - module Formula = E.Formula -end[@@inline] - - -(* Solver types for pure SAT Solving *) -(* ************************************************************************ *) - -module SatMake (E : Formula_intf.S) = struct - include McMake(struct - include E - module Term = E - module Formula = E - end) - - let mcsat = false -end[@@inline] - diff --git a/src/core/Solver_types.mli b/src/core/Solver_types.mli deleted file mode 100644 index fb1e03c3..00000000 --- a/src/core/Solver_types.mli +++ /dev/null @@ -1,26 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) - -(** Internal types (implementation) - - This modules actually implements the internal types used by the solver. - Since mutation is heavily used in the solver, it is really, really, *really* - discouraged to direclty use the functions in this module if you don't know the - inner working of mSAT perfectly as even the simplest - change can have dramatic effects on the solver. -*) - -module type S = Solver_types_intf.S -(** Interface for the internal types. *) - -module McMake (E : Expr_intf.S): - S with type term = E.Term.t and type formula = E.Formula.t and type proof = E.proof -(** Functor to instantiate the types of clauses for a solver. *) - -module SatMake (E : Formula_intf.S): - S with type term = E.t and type formula = E.t and type proof = E.proof -(** Functor to instantiate the types of clauses for a solver. *) - diff --git a/src/core/Solver_types_intf.ml b/src/core/Solver_types_intf.ml deleted file mode 100644 index c5d0ff37..00000000 --- a/src/core/Solver_types_intf.ml +++ /dev/null @@ -1,271 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) - -(** Internal types (interface) - - This modules defines the interface of most of the internal types - used in the core solver. -*) - -type 'a printer = Format.formatter -> 'a -> unit - -module type S = sig - (** The signatures of clauses used in the Solver. *) - - val mcsat : bool - (** TODO:deprecate. *) - - type t - (** State for creating new terms, literals, clauses *) - - val create: ?size:[`Tiny|`Small|`Big] -> unit -> t - - (** {2 Type definitions} *) - - type term - type formula - type proof - (** The types of terms, formulas and proofs. All of these are user-provided. *) - - type seen = - | Nope - | Both - | Positive - | Negative - - (* TODO: hide these types (from the outside of [Msat]); - instead, provide well defined modules [module Lit : sig type t val …] - that define their API in Msat itself (not here) *) - - type lit = { - lid : int; (** Unique identifier *) - term : term; (** Wrapped term *) - mutable l_level : int; (** Decision level of the assignment *) - mutable l_idx: int; (** index in heap *) - mutable l_weight : float; (** Weight (for the heap) *) - mutable assigned : term option; (** Assignment *) - } - (** Wrapper type for literals, i.e. theory terms (for mcsat only). *) - - type var = { - vid : int; (** Unique identifier *) - pa : atom; (** Link for the positive atom *) - na : atom; (** Link for the negative atom *) - mutable v_fields : int; (** bool fields *) - mutable v_level : int; (** Level of decision/propagation *) - mutable v_idx: int; (** rank in variable heap *) - mutable v_weight : float; (** Variable weight (for the heap) *) - mutable v_assignable: lit list option; - (** In mcsat, the list of lits that wraps subterms of the formula wrapped. *) - mutable reason : reason option; - (** The reason for propagation/decision of the literal *) - } - - and atom = { - aid : int; (** Unique identifier *) - var : var; (** Link for the parent variable *) - neg : atom; (** Link for the negation of the atom *) - lit : formula; (** Wrapped formula *) - mutable is_true : bool; (** Is the atom true ? Conversely, the atom - is false iff a.neg.is_true *) - mutable watched : clause Vec.t; (** The vector of clauses that watch this atom *) - } - (** Atoms and variables wrap theory formulas. They exist in the form of - triplet: a variable and two atoms. For a formula [f] in normal form, - the variable v points to the positive atom [a] which wraps [f], while - [a.neg] wraps the theory negation of [f]. *) - - and clause = { - name : int; (** Clause name, mainly for printing, unique. *) - tag : int option; (** User-provided tag for clauses. *) - atoms : atom array; (** The atoms that constitute the clause.*) - mutable cpremise : premise; (** The premise of the clause, i.e. the justification - of why the clause must be satisfied. *) - mutable activity : float; (** Clause activity, used for the heap heuristics. *) - mutable attached : bool; (** Is the clause attached, i.e. does it watch literals. *) - mutable visited : bool; (** Boolean used during propagation and proof generation. *) - } - (** The type of clauses. Each clause generated should be true, i.e. enforced - by the current problem (for more information, see the cpremise field). *) - - and reason = - | Decision (** The atom has been decided by the sat solver *) - | Bcp of clause (** The atom has been propagated by the given clause *) - | Semantic (** The atom has been propagated by the theory at the given level. *) - (** Reasons of propagation/decision of atoms. *) - - and premise = - | Hyp (** The clause is a hypothesis, provided by the user. *) - | Local (** The clause is a 1-atom clause, - where the atom is a local assumption*) - | Lemma of proof (** The clause is a theory-provided tautology, with - the given proof. *) - | History of clause list (** The clause can be obtained by resolution of the clauses - in the list. If the list has a single element [c] , then - the clause can be obtained by simplifying [c] (i.e - eliminating doublons in its atom list). - For a premise [History [a_1 :: ... :: a_n]] ([n > 0]) - the clause is obtained by performing resolution of - [a_1] with [a_2], and then performing a resolution step between - the result and [a_3], etc... - Of course, each of the clause [a_i] also has its own premise. *) - (** Premises for clauses. Indeed each clause generated during a run of the solver - should be satisfied, the premise is the justification of why it should be - satisfied by the solver. *) - - (** {2 Decisions and propagations} *) - type trail_elt = - | Lit of lit - | Atom of atom (**) - (** Either a lit of an atom *) - - (** {2 Elements} *) - - type elt = - | E_lit of lit - | E_var of var (**) - (** Either a lit of a var *) - - val nb_elt : t -> int - val get_elt : t -> int -> elt - val iter_elt : t -> (elt -> unit) -> unit - (** Read access to the vector of variables created *) - - (** {2 Variables, Literals & Clauses } *) - - type state = t - - module Lit : sig - type t = lit - val term : t -> term - val make : state -> term -> t - (** Returns the variable associated with the term *) - - val level : t -> int - val assigned : t -> term option - val weight : t -> float - - val pp : t printer - val debug : t printer - end - - module Var : sig - type t = var - - val pos : t -> atom - val neg : t -> atom - - val level : t -> int - val reason : t -> reason option - val assignable : t -> lit list option - val weight : t -> float - - val make : state -> formula -> t * Formula_intf.negated - (** Returns the variable linked with the given formula, - and whether the atom associated with the formula - is [var.pa] or [var.na] *) - - val seen_both : t -> bool - (** both atoms have been seen? *) - - val clear : t -> unit - (** Clear the 'seen' field of the variable. *) - end - - module Atom : sig - type t = atom - val level : t -> int - val reason : t -> reason option - val lit : t -> formula - val equal : t -> t -> bool - val compare : t -> t -> int - val var : t -> Var.t - val abs : t -> t (** positive atom *) - val neg : t -> t - val id : t -> int - val is_pos : t -> bool (* positive atom? *) - val is_true : t -> bool - val is_false : t -> bool - - val make : state -> formula -> t - (** Returns the atom associated with the given formula *) - - val mark : t -> unit - (** Mark the atom as seen, using the 'seen' field in the variable. *) - - val seen : t -> bool - (** Returns wether the atom has been marked as seen. *) - - val pp : t printer - val pp_a : t array printer - val debug : t printer - val debug_a : t array printer - end - - module Elt : sig - type t = elt - - val of_lit : Lit.t -> t - val of_var : Var.t -> t - - val id : t -> int - val level : t -> int - val idx : t -> int - val weight : t -> float - - val set_level : t -> int -> unit - val set_idx : t -> int -> unit - val set_weight : t -> float -> unit - end - - module Clause : sig - type t = clause - - val name : t -> string - val equal : t -> t -> bool - val hash : t -> int - val atoms : t -> Atom.t array - val atoms_l : t -> Atom.t list - val tag : t -> int option - val premise : t -> premise - - val empty : t - (** The empty clause *) - - val make : ?tag:int -> Atom.t list -> premise -> clause - (** [make_clause name atoms size premise] creates a clause with the given attributes. *) - - val pp : t printer - val pp_dimacs : t printer - val debug : t printer - - module Tbl : Hashtbl.S with type key = t - end - - module Trail_elt : sig - type t = trail_elt - - val of_lit : Lit.t -> t - val of_atom : Atom.t -> t - (** Constructors and destructors *) - val debug : t printer - end - - module Term : sig - type t = term - val equal : t -> t -> bool - val hash : t -> int - val pp : t printer - end - - module Formula : sig - type t = formula - val equal : t -> t -> bool - val hash : t -> int - val pp : t printer - end -end - diff --git a/src/core/Theory_intf.ml b/src/core/Theory_intf.ml deleted file mode 100644 index d377509d..00000000 --- a/src/core/Theory_intf.ml +++ /dev/null @@ -1,83 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) - -(** SMT Theory - - This module defines what a theory must implement in order to - be used in an SMT solver. -*) - -type ('formula, 'proof) res = ('formula, 'proof) Plugin_intf.res = - | Sat - (** The current set of assumptions is satisfiable. *) - | Unsat of 'formula list * 'proof - (** The current set of assumptions is *NOT* satisfiable, and here is a - theory tautology (with its proof), for which every litteral is false - under the current assumptions. *) -(** Type returned by the theory. Formulas in the unsat clause must come from the - current set of assumptions, i.e must have been encountered in a slice. *) - -type ('form, 'proof) slice = { - start : int; (** Start of the slice *) - length : int; (** Length of the slice *) - get : int -> 'form; (** Accesor for the formuals in the slice. - Should only be called on integers [i] s.t. - [start <= i < start + length] *) - push : 'form list -> 'proof -> unit; (** Allows to add to the solver a clause. *) - propagate : 'form -> 'form list -> 'proof -> unit; - (** [propagate lit causes proof] informs the solver to propagate [lit], with the reason - that the clause [causes => lit] is a theory tautology. It is faster than pushing - the associated clause but the clause will not be remembered by the sat solver, - i.e it will not be used by the solver to do boolean propagation. *) -} -(** The type for a slice. Slices are some kind of view of the current - propagation queue. They allow to look at the propagated literals, - and to add new clauses to the solver. *) - -(** Signature for theories to be given to the Solver. *) -module type S = sig - type t - (** The state of the theory itself *) - - type formula - (** The type of formulas. Should be compatble with Formula_intf.S *) - - type proof - (** A custom type for the proofs of lemmas produced by the theory. *) - - type level - (** The type for levels to allow backtracking. *) - - val current_level : t -> level - (** Return the current level of the theory (either the empty/beginning state, or the - last level returned by the [assume] function). *) - - val assume : t -> (formula, proof) slice -> (formula, proof) res - (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, - and returns the result of the new assumptions. *) - - val if_sat : t -> (formula, proof) slice -> (formula, proof) res - (** Called at the end of the search in case a model has been found. If no new clause is - pushed, then 'sat' is returned, else search is resumed. *) - - val backtrack : t -> level -> unit - (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the - same state as when it returned the value [l], *) - -end - -module Dummy(F: Formula_intf.S) - : S with type formula = F.t and type t = unit -= struct - type t = unit - type formula = F.t - type proof = unit - type level = unit - let current_level () = () - let assume () _ = Sat - let if_sat () _ = Sat - let backtrack () _ = () -end diff --git a/src/main/main.ml b/src/main/main.ml index 177b5b32..e2573b4f 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -19,7 +19,7 @@ let size_limit = ref 1000_000_000. module S = Msat_sat module Process = struct - module D = Msat_backend.Dot.Make(S.Proof)(Msat_backend.Dot.Default(S.Proof)) + module D = Msat_backend.Dot.Make(S)(Msat_backend.Dot.Default(S)) let hyps = ref [] @@ -29,7 +29,7 @@ module Process = struct let check_clause c = let l = List.map (function a -> Log.debugf 99 - (fun k -> k "Checking value of %a" S.Formula.pp a); + (fun k -> k "Checking value of %a" S.Atom.pp a); sat.Msat.eval a) c in List.exists (fun x -> x) l in diff --git a/src/sat/Expr_sat.ml b/src/sat/Expr_sat.ml index f41ba5d5..e6de1a13 100644 --- a/src/sat/Expr_sat.ml +++ b/src/sat/Expr_sat.ml @@ -2,9 +2,6 @@ exception Bad_atom (** Exception raised if an atom cannot be created *) -type proof -(** A empty type for proofs *) - type t = int (** Atoms are represented as integers. [-i] begin the negation of [i]. Additionally, since we nee dot be able to create fresh atoms, we @@ -31,9 +28,9 @@ let neg a = - a let norm a = abs a, if a < 0 then - Formula_intf.Negated + Solver_intf.Negated else - Formula_intf.Same_sign + Solver_intf.Same_sign let abs = abs diff --git a/src/sat/Expr_sat.mli b/src/sat/Expr_sat.mli index a48603af..a8be3e3e 100644 --- a/src/sat/Expr_sat.mli +++ b/src/sat/Expr_sat.mli @@ -8,7 +8,7 @@ near optimal efficiency (both in terms of space and time). *) -include Formula_intf.S +include Solver_intf.FORMULA (** This modules implements the requirements for implementing an SAT Solver. *) val make : int -> t diff --git a/src/sat/Msat_sat.ml b/src/sat/Msat_sat.ml index 1fc3d1ee..007f385b 100644 --- a/src/sat/Msat_sat.ml +++ b/src/sat/Msat_sat.ml @@ -4,7 +4,5 @@ Copyright 2016 Guillaume Bury *) module Expr = Expr_sat - -module F = Msat.Make_smt_expr(Expr) -include Msat.Make(F)(Msat.Make_dummy(F)) +include Msat.Make_pure_sat(Expr) diff --git a/src/sat/Msat_sat.mli b/src/sat/Msat_sat.mli index 95695269..b36bda05 100644 --- a/src/sat/Msat_sat.mli +++ b/src/sat/Msat_sat.mli @@ -11,6 +11,6 @@ Copyright 2016 Guillaume Bury module Expr = Expr_sat -include Msat.S with type formula = Expr.t and type theory = unit +include Msat.S with type Formula.t = Expr.t and type theory = unit (** A functor that can generate as many solvers as needed. *) diff --git a/tests/test_api.ml b/tests/test_api.ml index 89e8cafb..bef40497 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -7,6 +7,7 @@ Copyright 2014 Simon Cruanes (* Tests that require the API *) module F = Msat_sat.Expr +module S = Msat_sat let (|>) x f = f x @@ -36,27 +37,7 @@ type solver_res = exception Incorrect_model -module type BASIC_SOLVER = sig - type t - val create : unit -> t - val solve : t -> ?assumptions:F.t list -> unit -> solver_res - val assume : t -> ?tag:int -> F.t list list -> unit -end - -let mk_solver (): (module BASIC_SOLVER) = - let module S = struct - include Msat_sat - let create() = create() - let solve st ?assumptions () = - match solve st ?assumptions() with - | Sat _ -> - R_sat - | Unsat us -> - let p = us.Msat.get_proof () in - Proof.check p; - R_unsat - end - in (module S) +let mk_solver () : S.t = S.create ~size:`Big () exception Error of string @@ -86,20 +67,22 @@ module Test = struct let run (t:t): result = (* Interesting stuff happening *) - let (module S: BASIC_SOLVER) = mk_solver () in - let st = S.create() in + let st = mk_solver() in try List.iter (function | A_assume cs -> S.assume st cs | A_solve (assumptions, expect) -> + let assumptions = List.map (S.make_atom st) assumptions in match S.solve st ~assumptions (), expect with - | R_sat, `Expect_sat - | R_unsat, `Expect_unsat -> () - | R_unsat, `Expect_sat -> + | S.Sat _, `Expect_sat -> () + | S.Unsat us, `Expect_unsat -> + let p = us.Msat.get_proof () in + S.Proof.check p; + | S.Unsat _, `Expect_sat -> error "expect sat, got unsat" - | R_sat, `Expect_unsat -> + | S.Sat _, `Expect_unsat -> error "expect unsat, got sat" ) t.actions; From df9538a91e72ec80a00ac403642904a27fe3309a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 23:27:54 -0600 Subject: [PATCH 059/182] perf: exit early from propagation loop in case of conflict --- src/core/Internal.ml | 67 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 42dfd0f0..86fbbcc7 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1550,7 +1550,7 @@ module Make(Plugin : PLUGIN) if first.neg.is_true then ( (* clause is false *) st.elt_head <- Vec.size st.trail; - raise (Conflict c) + raise_notrace (Conflict c) ) else ( match th_eval st first with | None -> (* clause is unit, keep the same watches, but propagate *) @@ -1558,7 +1558,7 @@ module Make(Plugin : PLUGIN) | Some true -> () | Some false -> st.elt_head <- Vec.size st.trail; - raise (Conflict c) + raise_notrace (Conflict c) ); Watch_kept with Exit -> @@ -1569,28 +1569,21 @@ module Make(Plugin : PLUGIN) clause watching [a] to see if the clause is false, unit, or has other possible watches @param res the optional conflict clause that the propagation might trigger *) - let propagate_atom st a (res:clause option ref) : unit = + let propagate_atom st a : unit = let watched = a.watched in - begin - try - let rec aux i = - if i >= Vec.size watched then () - else ( - let c = Vec.get watched i in - assert c.attached; - let j = match propagate_in_clause st a c i with - | Watch_kept -> i+1 - | Watch_removed -> i (* clause at this index changed *) - in - aux j - ) + let rec aux i = + if i >= Vec.size watched then () + else ( + let c = Vec.get watched i in + assert c.attached; + let j = match propagate_in_clause st a c i with + | Watch_kept -> i+1 + | Watch_removed -> i (* clause at this index changed *) in - aux 0 - with Conflict c -> - assert (!res = None); - res := Some c - end; - () + aux j + ) + in + aux 0 (* Propagation (boolean and theory) *) let create_atom st f = @@ -1684,23 +1677,23 @@ module Make(Plugin : PLUGIN) flush_clauses st; (* Now, check that the situation is sane *) assert (st.elt_head <= Vec.size st.trail); - if st.elt_head = Vec.size st.trail then + if st.elt_head = Vec.size st.trail then ( theory_propagate st - else ( + ) else ( let num_props = ref 0 in - let res = ref None in - while st.elt_head < Vec.size st.trail do - begin match Vec.get st.trail st.elt_head with - | Lit _ -> () - | Atom a -> - incr num_props; - propagate_atom st a res - end; - st.elt_head <- st.elt_head + 1; - done; - match !res with - | None -> theory_propagate st - | _ -> !res + match + while st.elt_head < Vec.size st.trail do + begin match Vec.get st.trail st.elt_head with + | Lit _ -> () + | Atom a -> + incr num_props; + propagate_atom st a + end; + st.elt_head <- st.elt_head + 1; + done; + with + | () -> theory_propagate st + | exception Conflict c -> Some c ) (* remove some learnt clauses From f3488d68dbc8c64a5969c6105b7a14366855c099 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 15:01:09 -0600 Subject: [PATCH 060/182] test: add regression tests and icnf parser for assumptions --- Makefile | 4 +- src/core/Internal.ml | 11 +- src/core/Solver_intf.ml | 3 + src/core/Vec.ml | 1 + src/core/Vec.mli | 2 + src/sat/Expr_sat.ml | 2 + src/sat/Expr_sat.mli | 2 + tests/Makefile | 8 + tests/dune | 4 + tests/icnf-solve/Icnf_solve.ml | 131 + tests/icnf-solve/dune | 6 + tests/icnf-solve/lexer.mll | 28 + tests/regression/.regression1.icnf.ref | 473 + tests/regression/.regression2.icnf.ref | 249 + tests/regression/regression1.icnf | 11437 +++++++++++++++++++++++ tests/regression/regression2.icnf | 4484 +++++++++ 16 files changed, 16841 insertions(+), 4 deletions(-) create mode 100644 tests/Makefile create mode 100644 tests/icnf-solve/Icnf_solve.ml create mode 100644 tests/icnf-solve/dune create mode 100644 tests/icnf-solve/lexer.mll create mode 100644 tests/regression/.regression1.icnf.ref create mode 100644 tests/regression/.regression2.icnf.ref create mode 100644 tests/regression/regression1.icnf create mode 100644 tests/regression/regression2.icnf diff --git a/Makefile b/Makefile index 3391828d..b529fa09 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,10 @@ build-dev: @dune build $(OPTS) @install test: - @echo "run API tests…" + @echo "run tests…" @dune runtest + +test-full: test @echo "run benchmarks…" @/usr/bin/time -f "%e" ./tests/run sat diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 86fbbcc7..8cc01a1f 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -331,10 +331,9 @@ module Make(Plugin : PLUGIN) module Clause = struct type t = clause - let make = + let make_a = let n = ref 0 in - fun ?tag ali premise -> - let atoms = Array.of_list ali in + fun ?tag atoms premise -> let name = !n in incr n; { name; @@ -345,6 +344,8 @@ module Make(Plugin : PLUGIN) activity = 0.; cpremise = premise} + let make ?tag l premise = make_a ?tag (Array.of_list l) premise + let empty = make [] (History []) let name = name_of_clause let[@inline] equal c1 c2 = c1==c2 @@ -2009,6 +2010,10 @@ module Make(Plugin : PLUGIN) cleanup_ st; assume st ?tag cls + let[@inline] add_clause_a st c : unit = + let c = Clause.make_a c Hyp in + add_clause st c + let[@inline] add_clause st ?tag c : unit = cleanup_ st; let c = Clause.make ?tag c Hyp in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 8b17d69f..13dff750 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -411,6 +411,9 @@ module type S = sig val add_clause : t -> ?tag:int -> atom list -> unit (** Lower level addition of clauses *) + val add_clause_a : t -> atom array -> unit + (** Lower level addition of clauses *) + val solve : t -> ?assumptions:atom list -> unit -> res (** Try and solves the current set of clauses. @param assumptions additional atomic assumptions to be temporarily added. diff --git a/src/core/Vec.ml b/src/core/Vec.ml index 6223dfd9..0f2fb07b 100644 --- a/src/core/Vec.ml +++ b/src/core/Vec.ml @@ -94,6 +94,7 @@ let exists p t = Sequence.exists p @@ to_seq t let for_all p t = Sequence.for_all p @@ to_seq t let fold f acc a = Sequence.fold f acc @@ to_seq a let to_list a = Sequence.to_list @@ to_seq a +let to_array a = Array.sub a.data 0 a.sz let of_list l : _ t = match l with diff --git a/src/core/Vec.mli b/src/core/Vec.mli index b35e0f04..74919698 100644 --- a/src/core/Vec.mli +++ b/src/core/Vec.mli @@ -12,6 +12,8 @@ val create : unit -> 'a t val to_list : 'a t -> 'a list (** Returns the list of elements of the vector *) +val to_array : 'a t -> 'a array + val of_list : 'a list -> 'a t val to_seq : 'a t -> 'a Sequence.t diff --git a/src/sat/Expr_sat.ml b/src/sat/Expr_sat.ml index e6de1a13..cdac09d4 100644 --- a/src/sat/Expr_sat.ml +++ b/src/sat/Expr_sat.ml @@ -23,6 +23,8 @@ let _make i = end else raise Bad_atom +let to_int i = i + (** *) let neg a = - a diff --git a/src/sat/Expr_sat.mli b/src/sat/Expr_sat.mli index a8be3e3e..c4803bc9 100644 --- a/src/sat/Expr_sat.mli +++ b/src/sat/Expr_sat.mli @@ -14,6 +14,8 @@ include Solver_intf.FORMULA val make : int -> t (** Make a proposition from an integer. *) +val to_int : t -> int + val fresh : unit -> t (** Make a fresh atom *) diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..2dd2304b --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,8 @@ + +test-icnf: + @for i in regression/*.icnf ; do \ + echo "test problem $$i"; \ + ./icnf-solve/icnf_solve.exe $$i > regression/.`basename $$i`.out 2>/dev/null ; \ + diff regression/.`basename $$i`.out regression/.`basename $$i`.ref \ + || ( echo "mismatch for $$i" ; exit 1) ; \ + done diff --git a/tests/dune b/tests/dune index 6e357a4c..7c8ca088 100644 --- a/tests/dune +++ b/tests/dune @@ -12,4 +12,8 @@ (deps test_api.exe) (action (run %{deps}))) +(alias + (name runtest) + (deps ./icnf-solve/icnf_solve.exe Makefile (source_tree regression)) + (action (run make test-icnf))) diff --git a/tests/icnf-solve/Icnf_solve.ml b/tests/icnf-solve/Icnf_solve.ml new file mode 100644 index 00000000..afc8b61d --- /dev/null +++ b/tests/icnf-solve/Icnf_solve.ml @@ -0,0 +1,131 @@ + +module Vec = Msat.Vec + +module Parse : sig + type 'a event = + | Add_clause of 'a array + | Solve of 'a array + + type 'a t + + val make : file:string -> (int -> 'a) -> 'a t + + val next : 'a t -> 'a event (** @raise End_of_file when done *) +end = struct + module L = Lexer + + type 'a event = + | Add_clause of 'a array + | Solve of 'a array + + type 'a t = { + mk: int -> 'a; + vec: 'a Vec.t; + lex: Lexing.lexbuf; + } + + let make ~file mk : _ t = + let ic = open_in file in + let lex = Lexing.from_channel ic in + at_exit (fun () -> close_in_noerr ic); + {lex; vec=Vec.create(); mk; } + + let rec next (self:_ t) : _ event = + match L.token self.lex with + | L.EOF -> raise End_of_file + | L.A -> + let c = read_ints self in + Solve c + | L.I 0 -> + Add_clause [| |] + | L.I x -> + let c = read_ints ~first:(self.mk x) self in + Add_clause c + and read_ints ?first self : _ array = + Vec.clear self.vec; (* reuse local vec *) + CCOpt.iter (Vec.push self.vec) first; + let rec aux() = + match L.token self.lex with + | L.I 0 -> Vec.to_array self.vec (* done *) + | L.I n -> + let x = self.mk n in + Vec.push self.vec x; + aux() + | L.A -> failwith "unexpected \"a\"" + | L.EOF -> failwith "unexpected end of file" + in + aux() +end + +module Solver = struct + module F = Msat_sat.Expr + module S = Msat_sat + type t = S.t + + let make () = S.create() + let mklit s i = S.make_atom s (let v = F.make (abs i) in if i>0 then v else F.neg v) + let add_clause s c = S.add_clause_a s c; true + let to_int a : int = F.to_int @@ S.Atom.formula a + let solve s ass = + let ass = Array.to_list ass in + match S.solve ~assumptions:ass s () with + | S.Sat _ -> true + | S.Unsat _ -> false +end + +let solve_with_solver ~debug file : unit = + Printf.eprintf "c process %S\n%!" file; + let s = Solver.make () in + let pp_arr out a = + Array.iter (fun lit -> Printf.fprintf out "%d " (Solver.to_int lit)) a; + in + let p = Parse.make ~file (Solver.mklit s) in + let rec process_problem () = + match Parse.next p with + | Parse.Add_clause c -> + if debug then ( + Printf.printf "add_clause %a\n%!" pp_arr c; + ); + let r = Solver.add_clause s c in + if r then process_problem() + else ( + Printf.printf "UNSAT\n%!"; + skip_problem () + ) + | Parse.Solve assumptions -> + if debug then ( + Printf.printf "c solve %a\n%!" pp_arr assumptions; + ); + let r = Solver.solve s assumptions in + Printf.printf "%s\n%!" (if r then "SAT" else "UNSAT"); + (* next problem! *) + process_problem() + | exception End_of_file -> + done_ () + and skip_problem() = + match Parse.next p with + | Parse.Add_clause _ -> skip_problem() + | Parse.Solve _ -> process_problem () + | exception End_of_file -> done_ () + and done_ () = + Printf.eprintf "c done for %S\n%!" file; + () + in + process_problem () + +let solve_with_file ~debug file : unit = + try solve_with_solver ~debug file + with e -> + Printf.printf "error while solving %S:\n%s" + file (Printexc.to_string e); + exit 1 + +let () = + let files = ref [] in + let debug = ref false in + let opts = [ + "-d", Arg.Set debug, " debug"; + ] |> Arg.align in + Arg.parse opts (fun f -> files := f :: !files) "icnf_solve [options] "; + List.iter (fun f -> solve_with_file ~debug:!debug f) !files; + () diff --git a/tests/icnf-solve/dune b/tests/icnf-solve/dune new file mode 100644 index 00000000..e28262fc --- /dev/null +++ b/tests/icnf-solve/dune @@ -0,0 +1,6 @@ +(executable + (name icnf_solve) + (modes native) + (libraries containers msat msat_sat)) + +(ocamllex (modules lexer)) diff --git a/tests/icnf-solve/lexer.mll b/tests/icnf-solve/lexer.mll new file mode 100644 index 00000000..8e214618 --- /dev/null +++ b/tests/icnf-solve/lexer.mll @@ -0,0 +1,28 @@ + +{ + type token = A | I of int | EOF +} + +let space = [' ' '\t'] +let nat = (['0'-'9'])+ +let int = ('-' nat) | nat + +rule token = parse + | '\n' { Lexing.new_line lexbuf; token lexbuf } + | space { token lexbuf } + | 'c' { skip_line lexbuf } + | 'p' { skip_line lexbuf } + | int { let i = int_of_string (Lexing.lexeme lexbuf) in I i } + | 'a' { A } + | eof { EOF } + | _ as c + { + let msg = Printf.sprintf "lexer fails on char %c\n" c in + failwith msg + } + +and skip_line = parse + | '\n' { Lexing.new_line lexbuf; token lexbuf } + | eof { EOF } + | _ { skip_line lexbuf } + diff --git a/tests/regression/.regression1.icnf.ref b/tests/regression/.regression1.icnf.ref new file mode 100644 index 00000000..bac62d8b --- /dev/null +++ b/tests/regression/.regression1.icnf.ref @@ -0,0 +1,473 @@ +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT diff --git a/tests/regression/.regression2.icnf.ref b/tests/regression/.regression2.icnf.ref new file mode 100644 index 00000000..a6ae70d3 --- /dev/null +++ b/tests/regression/.regression2.icnf.ref @@ -0,0 +1,249 @@ +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +SAT +SAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT +SAT +SAT +SAT +SAT +SAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +UNSAT +SAT +SAT diff --git a/tests/regression/regression1.icnf b/tests/regression/regression1.icnf new file mode 100644 index 00000000..e33a6f11 --- /dev/null +++ b/tests/regression/regression1.icnf @@ -0,0 +1,11437 @@ +c regression test for incremental batsat: last case should be "SAT", not "UNSAT" +p cnf 10 10 +1 0 +2 0 +3 0 +-4 0 +-5 0 +-6 0 +-7 0 +-8 0 +-9 0 +-10 0 +-11 0 +-12 0 +-13 0 +-14 0 +-15 0 +-16 0 +-17 0 +-18 0 +-19 0 +-20 0 +-21 0 +-22 0 +-23 0 +24 0 +25 0 +-26 0 +27 0 +-28 0 +29 0 +-30 0 +31 0 +-32 0 +33 0 +-34 0 +-35 0 +-36 0 +-37 0 +-38 0 +-39 0 +2 0 +-40 0 +-41 0 +-42 0 +-43 0 +-44 0 +-45 0 +-46 0 +-47 0 +-48 0 +-49 0 +-50 0 +-51 0 +-52 0 +-53 0 +-54 0 +-55 0 +a 0 +56 -57 58 23 0 +-56 -41 0 +-56 -42 0 +-56 -43 0 +-56 -44 0 +-56 -45 0 +-56 -46 0 +-56 -47 0 +-56 -48 0 +-56 -49 0 +-56 -59 0 +-56 -60 0 +-56 57 0 +-56 -58 0 +-56 -23 0 +61 -62 -58 0 +-61 -41 0 +-61 -42 0 +-61 -43 0 +-61 -44 0 +-61 -45 0 +-61 -46 0 +-61 -47 0 +-61 -48 0 +-61 -49 0 +-61 -63 0 +-61 62 0 +-61 58 0 +56 61 0 +-40 0 +64 -65 66 67 0 +-64 -42 0 +-64 -43 0 +-64 -44 0 +-64 -45 0 +-64 -46 0 +-64 -47 0 +-64 -48 0 +-64 -49 0 +-64 -68 0 +-64 -69 0 +-64 65 0 +-64 -66 0 +-64 -67 0 +70 -71 -66 0 +-70 -42 0 +-70 -43 0 +-70 -44 0 +-70 -45 0 +-70 -46 0 +-70 -47 0 +-70 -48 0 +-70 -49 0 +-70 -72 0 +-70 71 0 +-70 66 0 +64 70 0 +-41 0 +73 -74 75 76 0 +-73 -43 0 +-73 -44 0 +-73 -45 0 +-73 -46 0 +-73 -47 0 +-73 -48 0 +-73 -49 0 +-73 -77 0 +-73 -78 0 +-73 74 0 +-73 -75 0 +-73 -76 0 +79 -80 -75 0 +-79 -43 0 +-79 -44 0 +-79 -45 0 +-79 -46 0 +-79 -47 0 +-79 -48 0 +-79 -49 0 +-79 -81 0 +-79 80 0 +-79 75 0 +73 79 0 +-42 0 +82 -83 84 85 0 +-82 -44 0 +-82 -45 0 +-82 -46 0 +-82 -47 0 +-82 -48 0 +-82 -49 0 +-82 -86 0 +-82 -87 0 +-82 83 0 +-82 -84 0 +-82 -85 0 +88 -89 -84 0 +-88 -44 0 +-88 -45 0 +-88 -46 0 +-88 -47 0 +-88 -48 0 +-88 -49 0 +-88 -90 0 +-88 89 0 +-88 84 0 +82 88 0 +-43 0 +91 -92 93 94 0 +-91 -45 0 +-91 -46 0 +-91 -47 0 +-91 -48 0 +-91 -49 0 +-91 -95 0 +-91 -96 0 +-91 92 0 +-91 -93 0 +-91 -94 0 +97 -98 -93 0 +-97 -45 0 +-97 -46 0 +-97 -47 0 +-97 -48 0 +-97 -49 0 +-97 -99 0 +-97 98 0 +-97 93 0 +91 97 0 +-44 0 +100 -101 93 102 0 +-100 -46 0 +-100 -47 0 +-100 -48 0 +-100 -49 0 +-100 -103 0 +-100 -104 0 +-100 101 0 +-100 -93 0 +-100 -102 0 +105 -106 -93 0 +-105 -46 0 +-105 -47 0 +-105 -48 0 +-105 -49 0 +-105 -107 0 +-105 106 0 +-105 93 0 +100 105 0 +-45 0 +108 -109 84 110 0 +-108 -47 0 +-108 -48 0 +-108 -49 0 +-108 -111 0 +-108 -112 0 +-108 109 0 +-108 -84 0 +-108 -110 0 +113 -114 -84 0 +-113 -47 0 +-113 -48 0 +-113 -49 0 +-113 -115 0 +-113 114 0 +-113 84 0 +108 113 0 +-46 0 +116 -117 75 118 0 +-116 -48 0 +-116 -49 0 +-116 -119 0 +-116 -120 0 +-116 117 0 +-116 -75 0 +-116 -118 0 +121 -122 -75 0 +-121 -48 0 +-121 -49 0 +-121 -123 0 +-121 122 0 +-121 75 0 +116 121 0 +-47 0 +124 -125 66 126 0 +-124 -49 0 +-124 -127 0 +-124 -128 0 +-124 125 0 +-124 -66 0 +-124 -126 0 +129 -130 -66 0 +-129 -49 0 +-129 -131 0 +-129 130 0 +-129 66 0 +124 129 0 +-48 0 +132 -133 58 134 0 +-132 -135 0 +-132 -136 0 +-132 133 0 +-132 -58 0 +-132 -134 0 +137 -138 -58 0 +-137 -139 0 +-137 138 0 +-137 58 0 +132 137 0 +-49 0 +140 -58 141 25 0 +-140 58 0 +-140 -141 0 +-140 -25 0 +142 -58 -141 -25 0 +-142 58 0 +-142 141 0 +-142 25 0 +143 58 -141 25 0 +-143 -58 0 +-143 141 0 +-143 -25 0 +144 58 141 -25 -145 0 +-144 -146 0 +-144 -58 0 +-144 -141 0 +-144 25 0 +-144 145 0 +147 58 141 25 145 0 +-147 -146 0 +-147 -58 0 +-147 -141 0 +-147 -25 0 +-147 -145 0 +140 142 143 144 147 0 +-50 0 +148 -66 149 27 0 +-148 66 0 +-148 -149 0 +-148 -27 0 +150 -66 -149 -27 0 +-150 66 0 +-150 149 0 +-150 27 0 +151 66 -149 27 0 +-151 -66 0 +-151 149 0 +-151 -27 0 +152 66 149 -27 -153 0 +-152 -154 0 +-152 -66 0 +-152 -149 0 +-152 27 0 +-152 153 0 +155 66 149 27 153 0 +-155 -154 0 +-155 -66 0 +-155 -149 0 +-155 -27 0 +-155 -153 0 +148 150 151 152 155 0 +-51 0 +156 -75 157 29 0 +-156 75 0 +-156 -157 0 +-156 -29 0 +158 -75 -157 -29 0 +-158 75 0 +-158 157 0 +-158 29 0 +159 75 -157 29 0 +-159 -75 0 +-159 157 0 +-159 -29 0 +160 75 157 -29 -161 0 +-160 -162 0 +-160 -75 0 +-160 -157 0 +-160 29 0 +-160 161 0 +163 75 157 29 161 0 +-163 -162 0 +-163 -75 0 +-163 -157 0 +-163 -29 0 +-163 -161 0 +156 158 159 160 163 0 +-52 0 +164 -84 165 31 0 +-164 84 0 +-164 -165 0 +-164 -31 0 +166 -84 -165 -31 0 +-166 84 0 +-166 165 0 +-166 31 0 +167 84 -165 31 0 +-167 -84 0 +-167 165 0 +-167 -31 0 +168 84 165 -31 -169 0 +-168 -170 0 +-168 -84 0 +-168 -165 0 +-168 31 0 +-168 169 0 +171 84 165 31 169 0 +-171 -170 0 +-171 -84 0 +-171 -165 0 +-171 -31 0 +-171 -169 0 +164 166 167 168 171 0 +-53 0 +172 -93 173 33 0 +-172 93 0 +-172 -173 0 +-172 -33 0 +174 -93 -173 -33 0 +-174 93 0 +-174 173 0 +-174 33 0 +175 93 -173 33 0 +-175 -93 0 +-175 173 0 +-175 -33 0 +176 93 173 -33 -177 0 +-176 -178 0 +-176 -93 0 +-176 -173 0 +-176 33 0 +-176 177 0 +179 93 173 33 177 0 +-179 -178 0 +-179 -93 0 +-179 -173 0 +-179 -33 0 +-179 -177 0 +172 174 175 176 179 0 +-54 0 +a 0 +a 59 60 63 68 69 72 77 78 81 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 0 +180 -67 23 62 0 +-180 67 0 +-180 -23 0 +-180 -62 0 +181 -67 -23 -62 0 +-181 67 0 +-181 23 0 +-181 62 0 +182 67 -23 62 0 +-182 -67 0 +-182 23 0 +-182 -62 0 +183 67 23 -62 -184 0 +-183 -185 0 +-183 -67 0 +-183 -23 0 +-183 62 0 +-183 184 0 +186 67 23 62 184 0 +-186 -185 0 +-186 -67 0 +-186 -23 0 +-186 -62 0 +-186 -184 0 +180 181 182 183 186 0 +-63 0 +a 0 +a 59 60 68 69 72 77 78 81 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 0 +187 -76 67 71 0 +-187 76 0 +-187 -67 0 +-187 -71 0 +188 -76 -67 -71 0 +-188 76 0 +-188 67 0 +-188 71 0 +189 76 -67 71 0 +-189 -76 0 +-189 67 0 +-189 -71 0 +190 76 67 -71 -191 0 +-190 -192 0 +-190 -76 0 +-190 -67 0 +-190 71 0 +-190 191 0 +193 76 67 71 191 0 +-193 -192 0 +-193 -76 0 +-193 -67 0 +-193 -71 0 +-193 -191 0 +187 188 189 190 193 0 +-72 0 +a 0 +a 59 60 68 69 77 78 81 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 0 +194 -85 76 80 0 +-194 85 0 +-194 -76 0 +-194 -80 0 +195 -85 -76 -80 0 +-195 85 0 +-195 76 0 +-195 80 0 +196 85 -76 80 0 +-196 -85 0 +-196 76 0 +-196 -80 0 +197 85 76 -80 -198 0 +-197 -199 0 +-197 -85 0 +-197 -76 0 +-197 80 0 +-197 198 0 +200 85 76 80 198 0 +-200 -199 0 +-200 -85 0 +-200 -76 0 +-200 -80 0 +-200 -198 0 +194 195 196 197 200 0 +-81 0 +a 0 +a 59 60 68 69 77 78 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 0 +201 -22 202 57 0 +-201 22 0 +-201 -202 0 +-201 -57 0 +203 -22 -202 -57 0 +-203 22 0 +-203 202 0 +-203 57 0 +204 22 -202 57 0 +-204 -22 0 +-204 202 0 +-204 -57 0 +205 22 202 -57 -206 0 +-205 -207 0 +-205 -22 0 +-205 -202 0 +-205 57 0 +-205 206 0 +208 22 202 57 206 0 +-208 -207 0 +-208 -22 0 +-208 -202 0 +-208 -57 0 +-208 -206 0 +201 203 204 205 208 0 +-59 0 +a 0 +a 60 68 69 77 78 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 0 +209 -210 211 65 0 +-209 210 0 +-209 -211 0 +-209 -65 0 +212 -210 -211 -65 0 +-212 210 0 +-212 211 0 +-212 65 0 +213 210 -211 65 0 +-213 -210 0 +-213 211 0 +-213 -65 0 +214 210 211 -65 -215 0 +-214 -216 0 +-214 -210 0 +-214 -211 0 +-214 65 0 +-214 215 0 +217 210 211 65 215 0 +-217 -216 0 +-217 -210 0 +-217 -211 0 +-217 -65 0 +-217 -215 0 +209 212 213 214 217 0 +-68 0 +a 0 +a 60 69 77 78 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 216 0 +218 -219 220 74 0 +-218 219 0 +-218 -220 0 +-218 -74 0 +221 -219 -220 -74 0 +-221 219 0 +-221 220 0 +-221 74 0 +222 219 -220 74 0 +-222 -219 0 +-222 220 0 +-222 -74 0 +223 219 220 -74 -224 0 +-223 -225 0 +-223 -219 0 +-223 -220 0 +-223 74 0 +-223 224 0 +226 219 220 74 224 0 +-226 -225 0 +-226 -219 0 +-226 -220 0 +-226 -74 0 +-226 -224 0 +218 221 222 223 226 0 +-77 0 +a 0 +a 60 69 78 86 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 216 225 0 +227 -228 229 83 0 +-227 228 0 +-227 -229 0 +-227 -83 0 +230 -228 -229 -83 0 +-230 228 0 +-230 229 0 +-230 83 0 +231 228 -229 83 0 +-231 -228 0 +-231 229 0 +-231 -83 0 +232 228 229 -83 -233 0 +-232 -234 0 +-232 -228 0 +-232 -229 0 +-232 83 0 +-232 233 0 +235 228 229 83 233 0 +-235 -234 0 +-235 -228 0 +-235 -229 0 +-235 -83 0 +-235 -233 0 +227 230 231 232 235 0 +-86 0 +a 0 +a 60 69 78 87 90 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 216 225 234 0 +236 -94 85 89 0 +-236 94 0 +-236 -85 0 +-236 -89 0 +237 -94 -85 -89 0 +-237 94 0 +-237 85 0 +-237 89 0 +238 94 -85 89 0 +-238 -94 0 +-238 85 0 +-238 -89 0 +239 94 85 -89 -240 0 +-239 -241 0 +-239 -94 0 +-239 -85 0 +-239 89 0 +-239 240 0 +242 94 85 89 240 0 +-242 -241 0 +-242 -94 0 +-242 -85 0 +-242 -89 0 +-242 -240 0 +236 237 238 239 242 0 +-90 0 +a 0 +a 60 69 78 87 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 216 225 234 241 0 +243 -244 245 229 0 +-243 -48 0 +-243 -49 0 +-243 -246 0 +-243 -247 0 +-243 -44 0 +-243 -45 0 +-243 -46 0 +-243 -47 0 +-243 244 0 +-243 -245 0 +-243 -229 0 +248 -249 -245 0 +-248 -48 0 +-248 -49 0 +-248 -44 0 +-248 -45 0 +-248 -46 0 +-248 -47 0 +-248 -250 0 +-248 249 0 +-248 245 0 +243 248 0 +-87 0 +a 0 +a 60 69 78 95 96 99 103 104 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 178 185 192 199 207 216 225 234 241 246 247 250 0 +251 -102 94 98 0 +-251 102 0 +-251 -94 0 +-251 -98 0 +252 -102 -94 -98 0 +-252 102 0 +-252 94 0 +-252 98 0 +253 102 -94 98 0 +-253 -102 0 +-253 94 0 +-253 -98 0 +254 102 94 -98 -255 0 +-254 -256 0 +-254 -102 0 +-254 -94 0 +-254 98 0 +-254 255 0 +257 102 94 98 255 0 +-257 -256 0 +-257 -102 0 +-257 -94 0 +-257 -98 0 +-257 -255 0 +251 252 253 254 257 0 +-99 0 +a 0 +258 -259 260 92 0 +-258 259 0 +-258 -260 0 +-258 -92 0 +261 -259 -260 -92 0 +-261 259 0 +-261 260 0 +-261 92 0 +262 259 -260 92 0 +-262 -259 0 +-262 260 0 +-262 -92 0 +263 259 260 -92 -264 0 +-263 -265 0 +-263 -259 0 +-263 -260 0 +-263 92 0 +-263 264 0 +266 259 260 92 264 0 +-266 -265 0 +-266 -259 0 +-266 -260 0 +-266 -92 0 +-266 -264 0 +258 261 262 263 266 0 +-95 0 +267 -268 269 260 0 +-267 -48 0 +-267 -49 0 +-267 -45 0 +-267 -46 0 +-267 -47 0 +-267 -270 0 +-267 -271 0 +-267 268 0 +-267 -269 0 +-267 -260 0 +272 -273 -269 0 +-272 -48 0 +-272 -49 0 +-272 -45 0 +-272 -46 0 +-272 -47 0 +-272 -274 0 +-272 273 0 +-272 269 0 +267 272 0 +-96 0 +275 -276 277 101 0 +-275 276 0 +-275 -277 0 +-275 -101 0 +278 -276 -277 -101 0 +-278 276 0 +-278 277 0 +-278 101 0 +279 276 -277 101 0 +-279 -276 0 +-279 277 0 +-279 -101 0 +280 276 277 -101 -281 0 +-280 -282 0 +-280 -276 0 +-280 -277 0 +-280 101 0 +-280 281 0 +283 276 277 101 281 0 +-283 -282 0 +-283 -276 0 +-283 -277 0 +-283 -101 0 +-283 -281 0 +275 278 279 280 283 0 +-103 0 +284 -285 269 277 0 +-284 -48 0 +-284 -49 0 +-284 -46 0 +-284 -47 0 +-284 -286 0 +-284 -287 0 +-284 285 0 +-284 -269 0 +-284 -277 0 +288 -289 -269 0 +-288 -48 0 +-288 -49 0 +-288 -46 0 +-288 -47 0 +-288 -290 0 +-288 289 0 +-288 269 0 +284 288 0 +-104 0 +291 -269 292 177 0 +-291 269 0 +-291 -292 0 +-291 -177 0 +293 -269 -292 -177 0 +-293 269 0 +-293 292 0 +-293 177 0 +294 269 -292 177 0 +-294 -269 0 +-294 292 0 +-294 -177 0 +295 269 292 -177 -296 0 +-295 -297 0 +-295 -269 0 +-295 -292 0 +-295 177 0 +-295 296 0 +298 269 292 177 296 0 +-298 -297 0 +-298 -269 0 +-298 -292 0 +-298 -177 0 +-298 -296 0 +291 293 294 295 298 0 +-178 0 +a 0 +a 60 69 78 107 111 112 115 119 120 123 127 128 131 135 136 139 146 154 162 170 185 192 199 207 216 225 234 241 246 247 250 256 265 270 271 274 282 286 287 290 297 0 +299 -118 110 114 0 +-299 118 0 +-299 -110 0 +-299 -114 0 +300 -118 -110 -114 0 +-300 118 0 +-300 110 0 +-300 114 0 +301 118 -110 114 0 +-301 -118 0 +-301 110 0 +-301 -114 0 +302 118 110 -114 -303 0 +-302 -304 0 +-302 -118 0 +-302 -110 0 +-302 114 0 +-302 303 0 +305 118 110 114 303 0 +-305 -304 0 +-305 -118 0 +-305 -110 0 +-305 -114 0 +-305 -303 0 +299 300 301 302 305 0 +-115 0 +a 0 +a 60 69 78 107 111 112 119 120 123 127 128 131 135 136 139 146 154 162 170 185 192 199 207 216 225 234 241 246 247 250 256 265 270 271 274 282 286 287 290 297 304 0 +306 -126 118 122 0 +-306 126 0 +-306 -118 0 +-306 -122 0 +307 -126 -118 -122 0 +-307 126 0 +-307 118 0 +-307 122 0 +308 126 -118 122 0 +-308 -126 0 +-308 118 0 +-308 -122 0 +309 126 118 -122 -310 0 +-309 -311 0 +-309 -126 0 +-309 -118 0 +-309 122 0 +-309 310 0 +312 126 118 122 310 0 +-312 -311 0 +-312 -126 0 +-312 -118 0 +-312 -122 0 +-312 -310 0 +306 307 308 309 312 0 +-123 0 +a 0 +313 -228 259 240 0 +-313 228 0 +-313 -259 0 +-313 -240 0 +314 -228 -259 -240 0 +-314 228 0 +-314 259 0 +-314 240 0 +315 228 -259 240 0 +-315 -228 0 +-315 259 0 +-315 -240 0 +316 228 259 -240 -317 0 +-316 -318 0 +-316 -228 0 +-316 -259 0 +-316 240 0 +-316 317 0 +319 228 259 240 317 0 +-319 -318 0 +-319 -228 0 +-319 -259 0 +-319 -240 0 +-319 -317 0 +313 314 315 316 319 0 +-241 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 131 135 136 139 146 154 162 170 185 192 199 207 216 225 234 246 247 250 256 265 270 271 274 282 286 287 290 297 304 311 318 0 +320 -134 126 130 0 +-320 134 0 +-320 -126 0 +-320 -130 0 +321 -134 -126 -130 0 +-321 134 0 +-321 126 0 +-321 130 0 +322 134 -126 130 0 +-322 -134 0 +-322 126 0 +-322 -130 0 +323 134 126 -130 -324 0 +-323 -325 0 +-323 -134 0 +-323 -126 0 +-323 130 0 +-323 324 0 +326 134 126 130 324 0 +-326 -325 0 +-326 -134 0 +-326 -126 0 +-326 -130 0 +-326 -324 0 +320 321 322 323 326 0 +-131 0 +a 0 +327 -219 228 198 0 +-327 219 0 +-327 -228 0 +-327 -198 0 +328 -219 -228 -198 0 +-328 219 0 +-328 228 0 +-328 198 0 +329 219 -228 198 0 +-329 -219 0 +-329 228 0 +-329 -198 0 +330 219 228 -198 -331 0 +-330 -332 0 +-330 -219 0 +-330 -228 0 +-330 198 0 +-330 331 0 +333 219 228 198 331 0 +-333 -332 0 +-333 -219 0 +-333 -228 0 +-333 -198 0 +-333 -331 0 +327 328 329 330 333 0 +-199 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 139 146 154 162 170 185 192 207 216 225 234 246 247 250 256 265 270 271 274 282 286 287 290 297 304 311 318 325 332 0 +334 -1 134 138 0 +-334 1 0 +-334 -134 0 +-334 -138 0 +335 -1 -134 -138 0 +-335 1 0 +-335 134 0 +-335 138 0 +334 335 0 +-139 0 +a 0 +336 -210 219 191 0 +-336 210 0 +-336 -219 0 +-336 -191 0 +337 -210 -219 -191 0 +-337 210 0 +-337 219 0 +-337 191 0 +338 210 -219 191 0 +-338 -210 0 +-338 219 0 +-338 -191 0 +339 210 219 -191 -340 0 +-339 -341 0 +-339 -210 0 +-339 -219 0 +-339 191 0 +-339 340 0 +342 210 219 191 340 0 +-342 -341 0 +-342 -210 0 +-342 -219 0 +-342 -191 0 +-342 -340 0 +336 337 338 339 342 0 +-192 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 185 207 216 225 234 246 247 250 256 265 270 271 274 282 286 287 290 297 304 311 318 325 332 341 0 +343 -22 210 184 0 +-343 22 0 +-343 -210 0 +-343 -184 0 +344 -22 -210 -184 0 +-344 22 0 +-344 210 0 +-344 184 0 +345 22 -210 184 0 +-345 -22 0 +-345 210 0 +-345 -184 0 +346 22 210 -184 -347 0 +-346 -348 0 +-346 -22 0 +-346 -210 0 +-346 184 0 +-346 347 0 +349 22 210 184 347 0 +-349 -348 0 +-349 -22 0 +-349 -210 0 +-349 -184 0 +-349 -347 0 +343 344 345 346 349 0 +-185 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 234 246 247 250 256 265 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 0 +350 -94 229 249 0 +-350 94 0 +-350 -229 0 +-350 -249 0 +351 -94 -229 -249 0 +-351 94 0 +-351 229 0 +-351 249 0 +352 94 -229 249 0 +-352 -94 0 +-352 229 0 +-352 -249 0 +353 94 229 -249 -354 0 +-353 -355 0 +-353 -94 0 +-353 -229 0 +-353 249 0 +-353 354 0 +356 94 229 249 354 0 +-356 -355 0 +-356 -94 0 +-356 -229 0 +-356 -249 0 +-356 -354 0 +350 351 352 353 356 0 +-250 0 +a 0 +357 -259 358 354 0 +-357 259 0 +-357 -358 0 +-357 -354 0 +359 -259 -358 -354 0 +-359 259 0 +-359 358 0 +-359 354 0 +360 259 -358 354 0 +-360 -259 0 +-360 358 0 +-360 -354 0 +361 259 358 -354 -362 0 +-361 -363 0 +-361 -259 0 +-361 -358 0 +-361 354 0 +-361 362 0 +364 259 358 354 362 0 +-364 -363 0 +-364 -259 0 +-364 -358 0 +-364 -354 0 +-364 -362 0 +357 359 360 361 364 0 +-355 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 234 246 247 256 265 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 363 0 +365 -358 366 233 0 +-365 358 0 +-365 -366 0 +-365 -233 0 +367 -358 -366 -233 0 +-367 358 0 +-367 366 0 +-367 233 0 +368 358 -366 233 0 +-368 -358 0 +-368 366 0 +-368 -233 0 +369 358 366 -233 -370 0 +-369 -371 0 +-369 -358 0 +-369 -366 0 +-369 233 0 +-369 370 0 +372 358 366 233 370 0 +-372 -371 0 +-372 -358 0 +-372 -366 0 +-372 -233 0 +-372 -370 0 +365 367 368 369 372 0 +-234 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 256 265 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 363 371 0 +373 -259 276 255 0 +-373 259 0 +-373 -276 0 +-373 -255 0 +374 -259 -276 -255 0 +-374 259 0 +-374 276 0 +-374 255 0 +375 259 -276 255 0 +-375 -259 0 +-375 276 0 +-375 -255 0 +376 259 276 -255 -377 0 +-376 -378 0 +-376 -259 0 +-376 -276 0 +-376 255 0 +-376 377 0 +379 259 276 255 377 0 +-379 -378 0 +-379 -259 0 +-379 -276 0 +-379 -255 0 +-379 -377 0 +373 374 375 376 379 0 +-256 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 265 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 363 371 378 0 +380 -381 382 264 0 +-380 381 0 +-380 -382 0 +-380 -264 0 +383 -381 -382 -264 0 +-383 381 0 +-383 382 0 +-383 264 0 +384 381 -382 264 0 +-384 -381 0 +-384 382 0 +-384 -264 0 +385 381 382 -264 -386 0 +-385 -387 0 +-385 -381 0 +-385 -382 0 +-385 264 0 +-385 386 0 +388 381 382 264 386 0 +-388 -387 0 +-388 -381 0 +-388 -382 0 +-388 -264 0 +-388 -386 0 +380 383 384 385 388 0 +-265 0 +a 0 +a 60 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 363 371 378 387 0 +389 -390 391 202 0 +-389 -48 0 +-389 -49 0 +-389 -392 0 +-389 -393 0 +-389 -41 0 +-389 -42 0 +-389 -43 0 +-389 -44 0 +-389 -45 0 +-389 -46 0 +-389 -47 0 +-389 390 0 +-389 -391 0 +-389 -202 0 +394 -395 -391 0 +-394 -48 0 +-394 -49 0 +-394 -41 0 +-394 -42 0 +-394 -43 0 +-394 -44 0 +-394 -45 0 +-394 -46 0 +-394 -47 0 +-394 -396 0 +-394 395 0 +-394 391 0 +389 394 0 +-60 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 270 271 274 282 286 287 290 297 304 311 318 325 332 341 348 363 371 378 387 392 393 396 0 +397 -102 260 273 0 +-397 102 0 +-397 -260 0 +-397 -273 0 +398 -102 -260 -273 0 +-398 102 0 +-398 260 0 +-398 273 0 +399 102 -260 273 0 +-399 -102 0 +-399 260 0 +-399 -273 0 +400 102 260 -273 -401 0 +-400 -402 0 +-400 -102 0 +-400 -260 0 +-400 273 0 +-400 401 0 +403 102 260 273 401 0 +-403 -402 0 +-403 -102 0 +-403 -260 0 +-403 -273 0 +-403 -401 0 +397 398 399 400 403 0 +-274 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 270 271 282 286 287 290 297 304 311 318 325 332 341 348 363 371 378 387 392 393 396 402 0 +404 -110 277 289 0 +-404 110 0 +-404 -277 0 +-404 -289 0 +405 -110 -277 -289 0 +-405 110 0 +-405 277 0 +-405 289 0 +406 110 -277 289 0 +-406 -110 0 +-406 277 0 +-406 -289 0 +407 110 277 -289 -408 0 +-407 -409 0 +-407 -110 0 +-407 -277 0 +-407 289 0 +-407 408 0 +410 110 277 289 408 0 +-410 -409 0 +-410 -110 0 +-410 -277 0 +-410 -289 0 +-410 -408 0 +404 405 406 407 410 0 +-290 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 270 271 282 286 287 297 304 311 318 325 332 341 348 363 371 378 387 392 393 396 402 409 0 +411 -381 412 268 0 +-411 381 0 +-411 -412 0 +-411 -268 0 +413 -381 -412 -268 0 +-413 381 0 +-413 412 0 +-413 268 0 +414 381 -412 268 0 +-414 -381 0 +-414 412 0 +-414 -268 0 +415 381 412 -268 -416 0 +-415 -417 0 +-415 -381 0 +-415 -412 0 +-415 268 0 +-415 416 0 +418 381 412 268 416 0 +-418 -417 0 +-418 -381 0 +-418 -412 0 +-418 -268 0 +-418 -416 0 +411 413 414 415 418 0 +-270 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 318 325 332 341 348 363 371 378 387 392 393 396 402 409 417 0 +419 -366 382 317 0 +-419 366 0 +-419 -382 0 +-419 -317 0 +420 -366 -382 -317 0 +-420 366 0 +-420 382 0 +-420 317 0 +421 366 -382 317 0 +-421 -366 0 +-421 382 0 +-421 -317 0 +422 366 382 -317 -423 0 +-422 -424 0 +-422 -366 0 +-422 -382 0 +-422 317 0 +-422 423 0 +425 366 382 317 423 0 +-425 -424 0 +-425 -366 0 +-425 -382 0 +-425 -317 0 +-425 -423 0 +419 420 421 422 425 0 +-318 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 332 341 348 363 371 378 387 392 393 396 402 409 417 424 0 +426 -427 366 331 0 +-426 427 0 +-426 -366 0 +-426 -331 0 +428 -427 -366 -331 0 +-428 427 0 +-428 366 0 +-428 331 0 +429 427 -366 331 0 +-429 -427 0 +-429 366 0 +-429 -331 0 +430 427 366 -331 -431 0 +-430 -432 0 +-430 -427 0 +-430 -366 0 +-430 331 0 +-430 431 0 +433 427 366 331 431 0 +-433 -432 0 +-433 -427 0 +-433 -366 0 +-433 -331 0 +-433 -431 0 +426 428 429 430 433 0 +-332 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 341 348 363 371 378 387 392 393 396 402 409 417 424 432 0 +434 -435 427 340 0 +-434 435 0 +-434 -427 0 +-434 -340 0 +436 -435 -427 -340 0 +-436 435 0 +-436 427 0 +-436 340 0 +437 435 -427 340 0 +-437 -435 0 +-437 427 0 +-437 -340 0 +438 435 427 -340 -439 0 +-438 -440 0 +-438 -435 0 +-438 -427 0 +-438 340 0 +-438 439 0 +441 435 427 340 439 0 +-441 -440 0 +-441 -435 0 +-441 -427 0 +-441 -340 0 +-441 -439 0 +434 436 437 438 441 0 +-341 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 348 363 371 378 387 392 393 396 402 409 417 424 432 440 0 +442 -21 435 347 0 +-442 21 0 +-442 -435 0 +-442 -347 0 +443 -21 -435 -347 0 +-443 21 0 +-443 435 0 +-443 347 0 +444 21 -435 347 0 +-444 -21 0 +-444 435 0 +-444 -347 0 +445 21 435 -347 -446 0 +-445 -447 0 +-445 -21 0 +-445 -435 0 +-445 347 0 +-445 446 0 +448 21 435 347 446 0 +-448 -447 0 +-448 -21 0 +-448 -435 0 +-448 -347 0 +-448 -446 0 +442 443 444 445 448 0 +-348 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 363 371 378 387 392 393 396 402 409 417 424 432 440 447 0 +449 -382 450 362 0 +-449 382 0 +-449 -450 0 +-449 -362 0 +451 -382 -450 -362 0 +-451 382 0 +-451 450 0 +-451 362 0 +452 382 -450 362 0 +-452 -382 0 +-452 450 0 +-452 -362 0 +453 382 450 -362 -454 0 +-453 -455 0 +-453 -382 0 +-453 -450 0 +-453 362 0 +-453 454 0 +456 382 450 362 454 0 +-456 -455 0 +-456 -382 0 +-456 -450 0 +-456 -362 0 +-456 -454 0 +449 451 452 453 456 0 +-363 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 371 378 387 392 393 396 402 409 417 424 432 440 447 455 0 +457 -450 458 370 0 +-457 450 0 +-457 -458 0 +-457 -370 0 +459 -450 -458 -370 0 +-459 450 0 +-459 458 0 +-459 370 0 +460 450 -458 370 0 +-460 -450 0 +-460 458 0 +-460 -370 0 +461 450 458 -370 -462 0 +-461 -463 0 +-461 -450 0 +-461 -458 0 +-461 370 0 +-461 462 0 +464 450 458 370 462 0 +-464 -463 0 +-464 -450 0 +-464 -458 0 +-464 -370 0 +-464 -462 0 +457 459 460 461 464 0 +-371 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 378 387 392 393 396 402 409 417 424 432 440 447 455 463 0 +465 -466 467 386 0 +-465 466 0 +-465 -467 0 +-465 -386 0 +468 -466 -467 -386 0 +-468 466 0 +-468 467 0 +-468 386 0 +469 466 -467 386 0 +-469 -466 0 +-469 467 0 +-469 -386 0 +470 466 467 -386 -471 0 +-470 -472 0 +-470 -466 0 +-470 -467 0 +-470 386 0 +-470 471 0 +473 466 467 386 471 0 +-473 -472 0 +-473 -466 0 +-473 -467 0 +-473 -386 0 +-473 -471 0 +465 468 469 470 473 0 +-387 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 378 392 393 396 402 409 417 424 432 440 447 455 463 472 0 +474 -67 202 395 0 +-474 67 0 +-474 -202 0 +-474 -395 0 +475 -67 -202 -395 0 +-475 67 0 +-475 202 0 +-475 395 0 +476 67 -202 395 0 +-476 -67 0 +-476 202 0 +-476 -395 0 +477 67 202 -395 -478 0 +-477 -479 0 +-477 -67 0 +-477 -202 0 +-477 395 0 +-477 478 0 +480 67 202 395 478 0 +-480 -479 0 +-480 -67 0 +-480 -202 0 +-480 -395 0 +-480 -478 0 +474 475 476 477 480 0 +-396 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 207 216 225 246 247 271 282 286 287 297 304 311 325 378 392 393 402 409 417 424 432 440 447 455 463 472 479 0 +481 -21 482 206 0 +-481 21 0 +-481 -482 0 +-481 -206 0 +483 -21 -482 -206 0 +-483 21 0 +-483 482 0 +-483 206 0 +484 21 -482 206 0 +-484 -21 0 +-484 482 0 +-484 -206 0 +485 21 482 -206 -486 0 +-485 -487 0 +-485 -21 0 +-485 -482 0 +-485 206 0 +-485 486 0 +488 21 482 206 486 0 +-488 -487 0 +-488 -21 0 +-488 -482 0 +-488 -206 0 +-488 -486 0 +481 483 484 485 488 0 +-207 0 +a 0 +489 -482 490 390 0 +-489 482 0 +-489 -490 0 +-489 -390 0 +491 -482 -490 -390 0 +-491 482 0 +-491 490 0 +-491 390 0 +492 482 -490 390 0 +-492 -482 0 +-492 490 0 +-492 -390 0 +493 482 490 -390 -494 0 +-493 -495 0 +-493 -482 0 +-493 -490 0 +-493 390 0 +-493 494 0 +496 482 490 390 494 0 +-496 -495 0 +-496 -482 0 +-496 -490 0 +-496 -390 0 +-496 -494 0 +489 491 492 493 496 0 +-392 0 +497 -498 499 490 0 +-497 -48 0 +-497 -49 0 +-497 -500 0 +-497 -501 0 +-497 -41 0 +-497 -42 0 +-497 -43 0 +-497 -44 0 +-497 -45 0 +-497 -46 0 +-497 -47 0 +-497 498 0 +-497 -499 0 +-497 -490 0 +502 -503 -499 0 +-502 -48 0 +-502 -49 0 +-502 -41 0 +-502 -42 0 +-502 -43 0 +-502 -44 0 +-502 -45 0 +-502 -46 0 +-502 -47 0 +-502 -504 0 +-502 503 0 +-502 499 0 +497 502 0 +-393 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 271 282 286 287 297 304 311 325 378 402 409 417 424 432 440 447 455 463 472 479 487 495 500 501 504 0 +505 -506 507 412 0 +-505 -48 0 +-505 -49 0 +-505 -45 0 +-505 -46 0 +-505 -47 0 +-505 -508 0 +-505 -509 0 +-505 506 0 +-505 -507 0 +-505 -412 0 +510 -511 -507 0 +-510 -48 0 +-510 -49 0 +-510 -45 0 +-510 -46 0 +-510 -47 0 +-510 -512 0 +-510 511 0 +-510 507 0 +505 510 0 +-271 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 282 286 287 297 304 311 325 378 402 409 417 424 432 440 447 455 463 472 479 487 495 500 501 504 508 509 512 0 +513 -276 381 401 0 +-513 276 0 +-513 -381 0 +-513 -401 0 +514 -276 -381 -401 0 +-514 276 0 +-514 381 0 +-514 401 0 +515 276 -381 401 0 +-515 -276 0 +-515 381 0 +-515 -401 0 +516 276 381 -401 -517 0 +-516 -518 0 +-516 -276 0 +-516 -381 0 +-516 401 0 +-516 517 0 +519 276 381 401 517 0 +-519 -518 0 +-519 -276 0 +-519 -381 0 +-519 -401 0 +-519 -517 0 +513 514 515 516 519 0 +-402 0 +a 0 +520 -521 522 281 0 +-520 521 0 +-520 -522 0 +-520 -281 0 +523 -521 -522 -281 0 +-523 521 0 +-523 522 0 +-523 281 0 +524 521 -522 281 0 +-524 -521 0 +-524 522 0 +-524 -281 0 +525 521 522 -281 -526 0 +-525 -527 0 +-525 -521 0 +-525 -522 0 +-525 281 0 +-525 526 0 +528 521 522 281 526 0 +-528 -527 0 +-528 -521 0 +-528 -522 0 +-528 -281 0 +-528 -526 0 +520 523 524 525 528 0 +-282 0 +529 -521 530 285 0 +-529 521 0 +-529 -530 0 +-529 -285 0 +531 -521 -530 -285 0 +-531 521 0 +-531 530 0 +-531 285 0 +532 521 -530 285 0 +-532 -521 0 +-532 530 0 +-532 -285 0 +533 521 530 -285 -534 0 +-533 -535 0 +-533 -521 0 +-533 -530 0 +-533 285 0 +-533 534 0 +536 521 530 285 534 0 +-536 -535 0 +-536 -521 0 +-536 -530 0 +-536 -285 0 +-536 -534 0 +529 531 532 533 536 0 +-286 0 +537 -538 507 530 0 +-537 -48 0 +-537 -49 0 +-537 -46 0 +-537 -47 0 +-537 -539 0 +-537 -540 0 +-537 538 0 +-537 -507 0 +-537 -530 0 +541 -542 -507 0 +-541 -48 0 +-541 -49 0 +-541 -46 0 +-541 -47 0 +-541 -543 0 +-541 542 0 +-541 507 0 +537 541 0 +-287 0 +544 -507 545 296 0 +-544 507 0 +-544 -545 0 +-544 -296 0 +546 -507 -545 -296 0 +-546 507 0 +-546 545 0 +-546 296 0 +547 507 -545 296 0 +-547 -507 0 +-547 545 0 +-547 -296 0 +548 507 545 -296 -549 0 +-548 -550 0 +-548 -507 0 +-548 -545 0 +-548 296 0 +-548 549 0 +551 507 545 296 549 0 +-551 -550 0 +-551 -507 0 +-551 -545 0 +-551 -296 0 +-551 -549 0 +544 546 547 548 551 0 +-297 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 304 311 325 378 409 417 424 432 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 0 +552 -382 522 377 0 +-552 382 0 +-552 -522 0 +-552 -377 0 +553 -382 -522 -377 0 +-553 382 0 +-553 522 0 +-553 377 0 +554 382 -522 377 0 +-554 -382 0 +-554 522 0 +-554 -377 0 +555 382 522 -377 -556 0 +-555 -557 0 +-555 -382 0 +-555 -522 0 +-555 377 0 +-555 556 0 +558 382 522 377 556 0 +-558 -557 0 +-558 -382 0 +-558 -522 0 +-558 -377 0 +-558 -556 0 +552 553 554 555 558 0 +-378 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 304 311 325 409 417 424 432 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 0 +559 -467 560 416 0 +-559 467 0 +-559 -560 0 +-559 -416 0 +561 -467 -560 -416 0 +-561 467 0 +-561 560 0 +-561 416 0 +562 467 -560 416 0 +-562 -467 0 +-562 560 0 +-562 -416 0 +563 467 560 -416 -564 0 +-563 -565 0 +-563 -467 0 +-563 -560 0 +-563 416 0 +-563 564 0 +566 467 560 416 564 0 +-566 -565 0 +-566 -467 0 +-566 -560 0 +-566 -416 0 +-566 -564 0 +559 561 562 563 566 0 +-417 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 304 311 325 409 424 432 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 0 +567 -458 466 423 0 +-567 458 0 +-567 -466 0 +-567 -423 0 +568 -458 -466 -423 0 +-568 458 0 +-568 466 0 +-568 423 0 +569 458 -466 423 0 +-569 -458 0 +-569 466 0 +-569 -423 0 +570 458 466 -423 -571 0 +-570 -572 0 +-570 -458 0 +-570 -466 0 +-570 423 0 +-570 571 0 +573 458 466 423 571 0 +-573 -572 0 +-573 -458 0 +-573 -466 0 +-573 -423 0 +-573 -571 0 +567 568 569 570 573 0 +-424 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 304 311 325 409 432 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 0 +574 -458 575 431 0 +-574 458 0 +-574 -575 0 +-574 -431 0 +576 -458 -575 -431 0 +-576 458 0 +-576 575 0 +-576 431 0 +577 458 -575 431 0 +-577 -458 0 +-577 575 0 +-577 -431 0 +578 458 575 -431 -579 0 +-578 -580 0 +-578 -458 0 +-578 -575 0 +-578 431 0 +-578 579 0 +581 458 575 431 579 0 +-581 -580 0 +-581 -458 0 +-581 -575 0 +-581 -431 0 +-581 -579 0 +574 576 577 578 581 0 +-432 0 +a 0 +a 69 78 107 111 112 119 120 127 128 135 136 146 154 162 170 216 225 246 247 304 311 325 409 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 0 +582 -583 584 133 0 +-582 583 0 +-582 -584 0 +-582 -133 0 +585 -583 -584 -133 0 +-585 583 0 +-585 584 0 +-585 133 0 +586 583 -584 133 0 +-586 -583 0 +-586 584 0 +-586 -133 0 +587 583 584 -133 -588 0 +-587 -589 0 +-587 -583 0 +-587 -584 0 +-587 133 0 +-587 588 0 +590 583 584 133 588 0 +-590 -589 0 +-590 -583 0 +-590 -584 0 +-590 -133 0 +-590 -588 0 +582 585 586 587 590 0 +-135 0 +a 0 +a 69 78 107 111 112 119 120 127 128 136 146 154 162 170 216 225 246 247 304 311 325 409 440 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 0 +591 -575 592 439 0 +-591 575 0 +-591 -592 0 +-591 -439 0 +593 -575 -592 -439 0 +-593 575 0 +-593 592 0 +-593 439 0 +594 575 -592 439 0 +-594 -575 0 +-594 592 0 +-594 -439 0 +595 575 592 -439 -596 0 +-595 -597 0 +-595 -575 0 +-595 -592 0 +-595 439 0 +-595 596 0 +598 575 592 439 596 0 +-598 -597 0 +-598 -575 0 +-598 -592 0 +-598 -439 0 +-598 -596 0 +591 593 594 595 598 0 +-440 0 +a 0 +a 69 78 107 111 112 119 120 127 128 136 146 154 162 170 216 225 246 247 304 311 325 409 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 0 +599 -600 601 211 0 +-599 -48 0 +-599 -49 0 +-599 -42 0 +-599 -43 0 +-599 -44 0 +-599 -45 0 +-599 -46 0 +-599 -47 0 +-599 -602 0 +-599 -603 0 +-599 600 0 +-599 -601 0 +-599 -211 0 +604 -605 -601 0 +-604 -48 0 +-604 -49 0 +-604 -42 0 +-604 -43 0 +-604 -44 0 +-604 -45 0 +-604 -46 0 +-604 -47 0 +-604 -606 0 +-604 605 0 +-604 601 0 +599 604 0 +-69 0 +a 0 +a 78 107 111 112 119 120 127 128 136 146 154 162 170 216 225 246 247 304 311 325 409 447 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 0 +607 -20 592 446 0 +-607 20 0 +-607 -592 0 +-607 -446 0 +608 -20 -592 -446 0 +-608 20 0 +-608 592 0 +-608 446 0 +609 20 -592 446 0 +-609 -20 0 +-609 592 0 +-609 -446 0 +610 20 592 -446 -611 0 +-610 -612 0 +-610 -20 0 +-610 -592 0 +-610 446 0 +-610 611 0 +613 20 592 446 611 0 +-613 -612 0 +-613 -20 0 +-613 -592 0 +-613 -446 0 +-613 -611 0 +607 608 609 610 613 0 +-447 0 +a 0 +a 78 107 111 112 119 120 127 128 136 146 154 162 170 216 225 246 247 304 311 325 409 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 0 +614 -615 391 584 0 +-614 -616 0 +-614 -617 0 +-614 615 0 +-614 -391 0 +-614 -584 0 +618 -619 -391 0 +-618 -620 0 +-618 619 0 +-618 391 0 +614 618 0 +-136 0 +a 0 +621 -622 623 615 0 +-621 622 0 +-621 -623 0 +-621 -615 0 +624 -622 -623 -615 0 +-624 622 0 +-624 623 0 +-624 615 0 +625 622 -623 615 0 +-625 -622 0 +-625 623 0 +-625 -615 0 +626 622 623 -615 -627 0 +-626 -628 0 +-626 -622 0 +-626 -623 0 +-626 615 0 +-626 627 0 +629 622 623 615 627 0 +-629 -628 0 +-629 -622 0 +-629 -623 0 +-629 -615 0 +-629 -627 0 +621 624 625 626 629 0 +-616 0 +630 -631 499 623 0 +-630 -632 0 +-630 -633 0 +-630 631 0 +-630 -499 0 +-630 -623 0 +634 -635 -499 0 +-634 -636 0 +-634 635 0 +-634 499 0 +630 634 0 +-617 0 +a 0 +a 78 107 111 112 119 120 127 128 146 154 162 170 216 225 246 247 304 311 325 409 455 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 0 +637 -466 638 454 0 +-637 466 0 +-637 -638 0 +-637 -454 0 +639 -466 -638 -454 0 +-639 466 0 +-639 638 0 +-639 454 0 +640 466 -638 454 0 +-640 -466 0 +-640 638 0 +-640 -454 0 +641 466 638 -454 -642 0 +-641 -643 0 +-641 -466 0 +-641 -638 0 +-641 454 0 +-641 642 0 +644 466 638 454 642 0 +-644 -643 0 +-644 -466 0 +-644 -638 0 +-644 -454 0 +-644 -642 0 +637 639 640 641 644 0 +-455 0 +a 0 +a 78 107 111 112 119 120 127 128 146 154 162 170 216 225 246 247 304 311 325 409 463 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 0 +645 -638 646 462 0 +-645 638 0 +-645 -646 0 +-645 -462 0 +647 -638 -646 -462 0 +-647 638 0 +-647 646 0 +-647 462 0 +648 638 -646 462 0 +-648 -638 0 +-648 646 0 +-648 -462 0 +649 638 646 -462 -650 0 +-649 -651 0 +-649 -638 0 +-649 -646 0 +-649 462 0 +-649 650 0 +652 638 646 462 650 0 +-652 -651 0 +-652 -638 0 +-652 -646 0 +-652 -462 0 +-652 -650 0 +645 647 648 649 652 0 +-463 0 +a 0 +a 78 107 111 112 119 120 127 128 146 154 162 170 216 225 246 247 304 311 325 409 472 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 651 0 +653 -654 655 471 0 +-653 654 0 +-653 -655 0 +-653 -471 0 +656 -654 -655 -471 0 +-656 654 0 +-656 655 0 +-656 471 0 +657 654 -655 471 0 +-657 -654 0 +-657 655 0 +-657 -471 0 +658 654 655 -471 -659 0 +-658 -660 0 +-658 -654 0 +-658 -655 0 +-658 471 0 +-658 659 0 +661 654 655 471 659 0 +-661 -660 0 +-661 -654 0 +-661 -655 0 +-661 -471 0 +-661 -659 0 +653 656 657 658 661 0 +-472 0 +a 0 +a 78 107 111 112 119 120 127 128 146 154 162 170 216 225 246 247 304 311 325 409 479 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 651 660 0 +662 -210 482 478 0 +-662 210 0 +-662 -482 0 +-662 -478 0 +663 -210 -482 -478 0 +-663 210 0 +-663 482 0 +-663 478 0 +664 210 -482 478 0 +-664 -210 0 +-664 482 0 +-664 -478 0 +665 210 482 -478 -666 0 +-665 -667 0 +-665 -210 0 +-665 -482 0 +-665 478 0 +-665 666 0 +668 210 482 478 666 0 +-668 -667 0 +-668 -210 0 +-668 -482 0 +-668 -478 0 +-668 -666 0 +662 663 664 665 668 0 +-479 0 +a 0 +a 78 107 111 112 119 120 127 128 146 154 162 170 216 225 246 247 304 311 325 409 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 651 660 667 0 +669 -391 670 145 0 +-669 391 0 +-669 -670 0 +-669 -145 0 +671 -391 -670 -145 0 +-671 391 0 +-671 670 0 +-671 145 0 +672 391 -670 145 0 +-672 -391 0 +-672 670 0 +-672 -145 0 +673 391 670 -145 -674 0 +-673 -675 0 +-673 -391 0 +-673 -670 0 +-673 145 0 +-673 674 0 +676 391 670 145 674 0 +-676 -675 0 +-676 -391 0 +-676 -670 0 +-676 -145 0 +-676 -674 0 +669 671 672 673 676 0 +-146 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 487 495 500 501 504 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 651 660 667 675 0 +677 -67 490 503 0 +-677 67 0 +-677 -490 0 +-677 -503 0 +678 -67 -490 -503 0 +-678 67 0 +-678 490 0 +-678 503 0 +679 67 -490 503 0 +-679 -67 0 +-679 490 0 +-679 -503 0 +680 67 490 -503 -681 0 +-680 -682 0 +-680 -67 0 +-680 -490 0 +-680 503 0 +-680 681 0 +683 67 490 503 681 0 +-683 -682 0 +-683 -67 0 +-683 -490 0 +-683 -503 0 +-683 -681 0 +677 678 679 680 683 0 +-504 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 487 495 500 501 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 628 632 633 636 643 651 660 667 675 682 0 +684 -20 685 486 0 +-684 20 0 +-684 -685 0 +-684 -486 0 +686 -20 -685 -486 0 +-686 20 0 +-686 685 0 +-686 486 0 +687 20 -685 486 0 +-687 -20 0 +-687 685 0 +-687 -486 0 +688 20 685 -486 -689 0 +-688 -690 0 +-688 -20 0 +-688 -685 0 +-688 486 0 +-688 689 0 +691 20 685 486 689 0 +-691 -690 0 +-691 -20 0 +-691 -685 0 +-691 -486 0 +-691 -689 0 +684 686 687 688 691 0 +-487 0 +a 0 +692 -685 693 494 0 +-692 685 0 +-692 -693 0 +-692 -494 0 +694 -685 -693 -494 0 +-694 685 0 +-694 693 0 +-694 494 0 +695 685 -693 494 0 +-695 -685 0 +-695 693 0 +-695 -494 0 +696 685 693 -494 -697 0 +-696 -698 0 +-696 -685 0 +-696 -693 0 +-696 494 0 +-696 697 0 +699 685 693 494 697 0 +-699 -698 0 +-699 -685 0 +-699 -693 0 +-699 -494 0 +-699 -697 0 +692 694 695 696 699 0 +-495 0 +700 -693 701 498 0 +-700 693 0 +-700 -701 0 +-700 -498 0 +702 -693 -701 -498 0 +-702 693 0 +-702 701 0 +-702 498 0 +703 693 -701 498 0 +-703 -693 0 +-703 701 0 +-703 -498 0 +704 693 701 -498 -705 0 +-704 -706 0 +-704 -693 0 +-704 -701 0 +-704 498 0 +-704 705 0 +707 693 701 498 705 0 +-707 -706 0 +-707 -693 0 +-707 -701 0 +-707 -498 0 +-707 -705 0 +700 702 703 704 707 0 +-500 0 +708 -709 710 701 0 +-708 -48 0 +-708 -49 0 +-708 -711 0 +-708 -712 0 +-708 -41 0 +-708 -42 0 +-708 -43 0 +-708 -44 0 +-708 -45 0 +-708 -46 0 +-708 -47 0 +-708 709 0 +-708 -710 0 +-708 -701 0 +713 -714 -710 0 +-713 -48 0 +-713 -49 0 +-713 -41 0 +-713 -42 0 +-713 -43 0 +-713 -44 0 +-713 -45 0 +-713 -46 0 +-713 -47 0 +-713 -715 0 +-713 714 0 +-713 710 0 +708 713 0 +-501 0 +716 -717 718 627 0 +-716 717 0 +-716 -718 0 +-716 -627 0 +719 -717 -718 -627 0 +-719 717 0 +-719 718 0 +-719 627 0 +720 717 -718 627 0 +-720 -717 0 +-720 718 0 +-720 -627 0 +721 717 718 -627 -722 0 +-721 -723 0 +-721 -717 0 +-721 -718 0 +-721 627 0 +-721 722 0 +724 717 718 627 722 0 +-724 -723 0 +-724 -717 0 +-724 -718 0 +-724 -627 0 +-724 -722 0 +716 719 720 721 724 0 +-628 0 +725 -717 726 631 0 +-725 717 0 +-725 -726 0 +-725 -631 0 +727 -717 -726 -631 0 +-727 717 0 +-727 726 0 +-727 631 0 +728 717 -726 631 0 +-728 -717 0 +-728 726 0 +-728 -631 0 +729 717 726 -631 -730 0 +-729 -731 0 +-729 -717 0 +-729 -726 0 +-729 631 0 +-729 730 0 +732 717 726 631 730 0 +-732 -731 0 +-732 -717 0 +-732 -726 0 +-732 -631 0 +-732 -730 0 +725 727 728 729 732 0 +-632 0 +733 -734 710 726 0 +-733 -735 0 +-733 -736 0 +-733 734 0 +-733 -710 0 +-733 -726 0 +737 -738 -710 0 +-737 -739 0 +-737 738 0 +-737 710 0 +733 737 0 +-633 0 +740 -210 693 681 0 +-740 210 0 +-740 -693 0 +-740 -681 0 +741 -210 -693 -681 0 +-741 210 0 +-741 693 0 +-741 681 0 +742 210 -693 681 0 +-742 -210 0 +-742 693 0 +-742 -681 0 +743 210 693 -681 -744 0 +-743 -745 0 +-743 -210 0 +-743 -693 0 +-743 681 0 +-743 744 0 +746 210 693 681 744 0 +-746 -745 0 +-746 -210 0 +-746 -693 0 +-746 -681 0 +-746 -744 0 +740 741 742 743 746 0 +-682 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 508 509 512 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 0 +747 -102 412 511 0 +-747 102 0 +-747 -412 0 +-747 -511 0 +748 -102 -412 -511 0 +-748 102 0 +-748 412 0 +-748 511 0 +749 102 -412 511 0 +-749 -102 0 +-749 412 0 +-749 -511 0 +750 102 412 -511 -751 0 +-750 -752 0 +-750 -102 0 +-750 -412 0 +-750 511 0 +-750 751 0 +753 102 412 511 751 0 +-753 -752 0 +-753 -102 0 +-753 -412 0 +-753 -511 0 +-753 -751 0 +747 748 749 750 753 0 +-512 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 508 509 518 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 0 +754 -522 467 517 0 +-754 522 0 +-754 -467 0 +-754 -517 0 +755 -522 -467 -517 0 +-755 522 0 +-755 467 0 +-755 517 0 +756 522 -467 517 0 +-756 -522 0 +-756 467 0 +-756 -517 0 +757 522 467 -517 -758 0 +-757 -759 0 +-757 -522 0 +-757 -467 0 +-757 517 0 +-757 758 0 +760 522 467 517 758 0 +-760 -759 0 +-760 -522 0 +-760 -467 0 +-760 -517 0 +-760 -758 0 +754 755 756 757 760 0 +-518 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 508 509 527 535 539 540 543 550 557 565 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 0 +761 -110 530 542 0 +-761 110 0 +-761 -530 0 +-761 -542 0 +762 -110 -530 -542 0 +-762 110 0 +-762 530 0 +-762 542 0 +763 110 -530 542 0 +-763 -110 0 +-763 530 0 +-763 -542 0 +764 110 530 -542 -765 0 +-764 -766 0 +-764 -110 0 +-764 -530 0 +-764 542 0 +-764 765 0 +767 110 530 542 765 0 +-767 -766 0 +-767 -110 0 +-767 -530 0 +-767 -542 0 +-767 -765 0 +761 762 763 764 767 0 +-543 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 508 509 527 535 539 540 550 557 565 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 0 +768 -560 769 506 0 +-768 560 0 +-768 -769 0 +-768 -506 0 +770 -560 -769 -506 0 +-770 560 0 +-770 769 0 +-770 506 0 +771 560 -769 506 0 +-771 -560 0 +-771 769 0 +-771 -506 0 +772 560 769 -506 -773 0 +-772 -774 0 +-772 -560 0 +-772 -769 0 +-772 506 0 +-772 773 0 +775 560 769 506 773 0 +-775 -774 0 +-775 -560 0 +-775 -769 0 +-775 -506 0 +-775 -773 0 +768 770 771 772 775 0 +-508 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 509 527 535 539 540 550 557 565 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 0 +776 -654 777 564 0 +-776 654 0 +-776 -777 0 +-776 -564 0 +778 -654 -777 -564 0 +-778 654 0 +-778 777 0 +-778 564 0 +779 654 -777 564 0 +-779 -654 0 +-779 777 0 +-779 -564 0 +780 654 777 -564 -781 0 +-780 -782 0 +-780 -654 0 +-780 -777 0 +-780 564 0 +-780 781 0 +783 654 777 564 781 0 +-783 -782 0 +-783 -654 0 +-783 -777 0 +-783 -564 0 +-783 -781 0 +776 778 779 780 783 0 +-565 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 509 527 535 539 540 550 557 572 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 0 +784 -646 655 571 0 +-784 646 0 +-784 -655 0 +-784 -571 0 +785 -646 -655 -571 0 +-785 646 0 +-785 655 0 +-785 571 0 +786 646 -655 571 0 +-786 -646 0 +-786 655 0 +-786 -571 0 +787 646 655 -571 -788 0 +-787 -789 0 +-787 -646 0 +-787 -655 0 +-787 571 0 +-787 788 0 +790 646 655 571 788 0 +-790 -789 0 +-790 -646 0 +-790 -655 0 +-790 -571 0 +-790 -788 0 +784 785 786 787 790 0 +-572 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 509 527 535 539 540 550 557 580 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 0 +791 -646 792 579 0 +-791 646 0 +-791 -792 0 +-791 -579 0 +793 -646 -792 -579 0 +-793 646 0 +-793 792 0 +-793 579 0 +794 646 -792 579 0 +-794 -646 0 +-794 792 0 +-794 -579 0 +795 646 792 -579 -796 0 +-795 -797 0 +-795 -646 0 +-795 -792 0 +-795 579 0 +-795 796 0 +798 646 792 579 796 0 +-798 -797 0 +-798 -646 0 +-798 -792 0 +-798 -579 0 +-798 -796 0 +791 793 794 795 798 0 +-580 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 509 527 535 539 540 550 557 589 597 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 0 +799 -792 800 596 0 +-799 792 0 +-799 -800 0 +-799 -596 0 +801 -792 -800 -596 0 +-801 792 0 +-801 800 0 +-801 596 0 +802 792 -800 596 0 +-802 -792 0 +-802 800 0 +-802 -596 0 +803 792 800 -596 -804 0 +-803 -805 0 +-803 -792 0 +-803 -800 0 +-803 596 0 +-803 804 0 +806 792 800 596 804 0 +-806 -805 0 +-806 -792 0 +-806 -800 0 +-806 -596 0 +-806 -804 0 +799 801 802 803 806 0 +-597 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 509 527 535 539 540 550 557 589 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 0 +807 -808 809 769 0 +-807 -48 0 +-807 -49 0 +-807 -810 0 +-807 -811 0 +-807 -45 0 +-807 -46 0 +-807 -47 0 +-807 808 0 +-807 -809 0 +-807 -769 0 +812 -813 -809 0 +-812 -48 0 +-812 -49 0 +-812 -45 0 +-812 -46 0 +-812 -47 0 +-812 -814 0 +-812 813 0 +-812 809 0 +807 812 0 +-509 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 527 535 539 540 550 557 589 602 603 606 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 0 +815 -76 211 605 0 +-815 76 0 +-815 -211 0 +-815 -605 0 +816 -76 -211 -605 0 +-816 76 0 +-816 211 0 +-816 605 0 +817 76 -211 605 0 +-817 -76 0 +-817 211 0 +-817 -605 0 +818 76 211 -605 -819 0 +-818 -820 0 +-818 -76 0 +-818 -211 0 +-818 605 0 +-818 819 0 +821 76 211 605 819 0 +-821 -820 0 +-821 -76 0 +-821 -211 0 +-821 -605 0 +-821 -819 0 +815 816 817 818 821 0 +-606 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 216 225 246 247 304 311 325 409 527 535 539 540 550 557 589 602 603 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 820 0 +822 -823 435 215 0 +-822 823 0 +-822 -435 0 +-822 -215 0 +824 -823 -435 -215 0 +-824 823 0 +-824 435 0 +-824 215 0 +825 823 -435 215 0 +-825 -823 0 +-825 435 0 +-825 -215 0 +826 823 435 -215 -827 0 +-826 -828 0 +-826 -823 0 +-826 -435 0 +-826 215 0 +-826 827 0 +829 823 435 215 827 0 +-829 -828 0 +-829 -823 0 +-829 -435 0 +-829 -215 0 +-829 -827 0 +822 824 825 826 829 0 +-216 0 +a 0 +830 -823 831 600 0 +-830 823 0 +-830 -831 0 +-830 -600 0 +832 -823 -831 -600 0 +-832 823 0 +-832 831 0 +-832 600 0 +833 823 -831 600 0 +-833 -823 0 +-833 831 0 +-833 -600 0 +834 823 831 -600 -835 0 +-834 -836 0 +-834 -823 0 +-834 -831 0 +-834 600 0 +-834 835 0 +837 823 831 600 835 0 +-837 -836 0 +-837 -823 0 +-837 -831 0 +-837 -600 0 +-837 -835 0 +830 832 833 834 837 0 +-602 0 +838 -839 840 831 0 +-838 -48 0 +-838 -49 0 +-838 -45 0 +-838 -46 0 +-838 -47 0 +-838 -841 0 +-838 -842 0 +-838 -42 0 +-838 -43 0 +-838 -44 0 +-838 839 0 +-838 -840 0 +-838 -831 0 +843 -844 -840 0 +-843 -48 0 +-843 -49 0 +-843 -45 0 +-843 -46 0 +-843 -47 0 +-843 -42 0 +-843 -43 0 +-843 -44 0 +-843 -845 0 +-843 844 0 +-843 840 0 +838 843 0 +-603 0 +846 -219 823 819 0 +-846 219 0 +-846 -823 0 +-846 -819 0 +847 -219 -823 -819 0 +-847 219 0 +-847 823 0 +-847 819 0 +848 219 -823 819 0 +-848 -219 0 +-848 823 0 +-848 -819 0 +849 219 823 -819 -850 0 +-849 -851 0 +-849 -219 0 +-849 -823 0 +-849 819 0 +-849 850 0 +852 219 823 819 850 0 +-852 -851 0 +-852 -219 0 +-852 -823 0 +-852 -819 0 +-852 -850 0 +846 847 848 849 852 0 +-820 0 +a 0 +a 78 107 111 112 119 120 127 128 154 162 170 225 246 247 304 311 325 409 527 535 539 540 550 557 589 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 0 +853 -854 855 220 0 +-853 -48 0 +-853 -49 0 +-853 -45 0 +-853 -46 0 +-853 -47 0 +-853 -43 0 +-853 -44 0 +-853 -856 0 +-853 -857 0 +-853 854 0 +-853 -855 0 +-853 -220 0 +858 -859 -855 0 +-858 -48 0 +-858 -49 0 +-858 -45 0 +-858 -46 0 +-858 -47 0 +-858 -43 0 +-858 -44 0 +-858 -860 0 +-858 859 0 +-858 855 0 +853 858 0 +-78 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 304 311 325 409 527 535 539 540 550 557 589 612 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 0 +861 -19 800 611 0 +-861 19 0 +-861 -800 0 +-861 -611 0 +862 -19 -800 -611 0 +-862 19 0 +-862 800 0 +-862 611 0 +863 19 -800 611 0 +-863 -19 0 +-863 800 0 +-863 -611 0 +864 19 800 -611 -865 0 +-864 -866 0 +-864 -19 0 +-864 -800 0 +-864 611 0 +-864 865 0 +867 19 800 611 865 0 +-867 -866 0 +-867 -19 0 +-867 -800 0 +-867 -611 0 +-867 -865 0 +861 862 863 864 867 0 +-612 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 304 311 325 409 527 535 539 540 550 557 589 620 636 643 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 0 +868 -655 869 642 0 +-868 655 0 +-868 -869 0 +-868 -642 0 +870 -655 -869 -642 0 +-870 655 0 +-870 869 0 +-870 642 0 +871 655 -869 642 0 +-871 -655 0 +-871 869 0 +-871 -642 0 +872 655 869 -642 -873 0 +-872 -874 0 +-872 -655 0 +-872 -869 0 +-872 642 0 +-872 873 0 +875 655 869 642 873 0 +-875 -874 0 +-875 -655 0 +-875 -869 0 +-875 -642 0 +-875 -873 0 +868 870 871 872 875 0 +-643 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 304 311 325 409 527 535 539 540 550 557 589 620 636 651 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 0 +876 -869 877 650 0 +-876 869 0 +-876 -877 0 +-876 -650 0 +878 -869 -877 -650 0 +-878 869 0 +-878 877 0 +-878 650 0 +879 869 -877 650 0 +-879 -869 0 +-879 877 0 +-879 -650 0 +880 869 877 -650 -881 0 +-880 -882 0 +-880 -869 0 +-880 -877 0 +-880 650 0 +-880 881 0 +883 869 877 650 881 0 +-883 -882 0 +-883 -869 0 +-883 -877 0 +-883 -650 0 +-883 -881 0 +876 878 879 880 883 0 +-651 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 304 311 325 409 527 535 539 540 550 557 589 620 636 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 0 +884 -885 886 303 0 +-884 885 0 +-884 -886 0 +-884 -303 0 +887 -885 -886 -303 0 +-887 885 0 +-887 886 0 +-887 303 0 +888 885 -886 303 0 +-888 -885 0 +-888 886 0 +-888 -303 0 +889 885 886 -303 -890 0 +-889 -891 0 +-889 -885 0 +-889 -886 0 +-889 303 0 +-889 890 0 +892 885 886 303 890 0 +-892 -891 0 +-892 -885 0 +-892 -886 0 +-892 -303 0 +-892 -890 0 +884 887 888 889 892 0 +-304 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 311 325 409 527 535 539 540 550 557 589 620 636 660 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 0 +893 -894 895 659 0 +-893 894 0 +-893 -895 0 +-893 -659 0 +896 -894 -895 -659 0 +-896 894 0 +-896 895 0 +-896 659 0 +897 894 -895 659 0 +-897 -894 0 +-897 895 0 +-897 -659 0 +898 894 895 -659 -899 0 +-898 -900 0 +-898 -894 0 +-898 -895 0 +-898 659 0 +-898 899 0 +901 894 895 659 899 0 +-901 -900 0 +-901 -894 0 +-901 -895 0 +-901 -659 0 +-901 -899 0 +893 896 897 898 901 0 +-660 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 311 325 409 527 535 539 540 550 557 589 620 636 667 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 0 +902 -886 903 310 0 +-902 886 0 +-902 -903 0 +-902 -310 0 +904 -886 -903 -310 0 +-904 886 0 +-904 903 0 +-904 310 0 +905 886 -903 310 0 +-905 -886 0 +-905 903 0 +-905 -310 0 +906 886 903 -310 -907 0 +-906 -908 0 +-906 -886 0 +-906 -903 0 +-906 310 0 +-906 907 0 +909 886 903 310 907 0 +-909 -908 0 +-909 -886 0 +-909 -903 0 +-909 -310 0 +-909 -907 0 +902 904 905 906 909 0 +-311 0 +a 0 +910 -435 685 666 0 +-910 435 0 +-910 -685 0 +-910 -666 0 +911 -435 -685 -666 0 +-911 435 0 +-911 685 0 +-911 666 0 +912 435 -685 666 0 +-912 -435 0 +-912 685 0 +-912 -666 0 +913 435 685 -666 -914 0 +-913 -915 0 +-913 -435 0 +-913 -685 0 +-913 666 0 +-913 914 0 +916 435 685 666 914 0 +-916 -915 0 +-916 -435 0 +-916 -685 0 +-916 -666 0 +-916 -914 0 +910 911 912 913 916 0 +-667 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 325 409 527 535 539 540 550 557 589 620 636 675 690 698 706 711 712 715 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 0 +917 -67 701 714 0 +-917 67 0 +-917 -701 0 +-917 -714 0 +918 -67 -701 -714 0 +-918 67 0 +-918 701 0 +-918 714 0 +919 67 -701 714 0 +-919 -67 0 +-919 701 0 +-919 -714 0 +920 67 701 -714 -921 0 +-920 -922 0 +-920 -67 0 +-920 -701 0 +-920 714 0 +-920 921 0 +923 67 701 714 921 0 +-923 -922 0 +-923 -67 0 +-923 -701 0 +-923 -714 0 +-923 -921 0 +917 918 919 920 923 0 +-715 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 325 409 527 535 539 540 550 557 589 620 636 675 690 698 706 711 712 723 731 735 736 739 745 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 0 +924 -903 583 324 0 +-924 903 0 +-924 -583 0 +-924 -324 0 +925 -903 -583 -324 0 +-925 903 0 +-925 583 0 +-925 324 0 +926 903 -583 324 0 +-926 -903 0 +-926 583 0 +-926 -324 0 +927 903 583 -324 -928 0 +-927 -929 0 +-927 -903 0 +-927 -583 0 +-927 324 0 +-927 928 0 +930 903 583 324 928 0 +-930 -929 0 +-930 -903 0 +-930 -583 0 +-930 -324 0 +-930 -928 0 +924 925 926 927 930 0 +-325 0 +a 0 +931 -19 932 689 0 +-931 19 0 +-931 -932 0 +-931 -689 0 +933 -19 -932 -689 0 +-933 19 0 +-933 932 0 +-933 689 0 +934 19 -932 689 0 +-934 -19 0 +-934 932 0 +-934 -689 0 +935 19 932 -689 -936 0 +-935 -937 0 +-935 -19 0 +-935 -932 0 +-935 689 0 +-935 936 0 +938 19 932 689 936 0 +-938 -937 0 +-938 -19 0 +-938 -932 0 +-938 -689 0 +-938 -936 0 +931 933 934 935 938 0 +-690 0 +939 -932 940 697 0 +-939 932 0 +-939 -940 0 +-939 -697 0 +941 -932 -940 -697 0 +-941 932 0 +-941 940 0 +-941 697 0 +942 932 -940 697 0 +-942 -932 0 +-942 940 0 +-942 -697 0 +943 932 940 -697 -944 0 +-943 -945 0 +-943 -932 0 +-943 -940 0 +-943 697 0 +-943 944 0 +946 932 940 697 944 0 +-946 -945 0 +-946 -932 0 +-946 -940 0 +-946 -697 0 +-946 -944 0 +939 941 942 943 946 0 +-698 0 +947 -940 948 705 0 +-947 940 0 +-947 -948 0 +-947 -705 0 +949 -940 -948 -705 0 +-949 940 0 +-949 948 0 +-949 705 0 +950 940 -948 705 0 +-950 -940 0 +-950 948 0 +-950 -705 0 +951 940 948 -705 -952 0 +-951 -953 0 +-951 -940 0 +-951 -948 0 +-951 705 0 +-951 952 0 +954 940 948 705 952 0 +-954 -953 0 +-954 -940 0 +-954 -948 0 +-954 -705 0 +-954 -952 0 +947 949 950 951 954 0 +-706 0 +955 -948 956 709 0 +-955 948 0 +-955 -956 0 +-955 -709 0 +957 -948 -956 -709 0 +-957 948 0 +-957 956 0 +-957 709 0 +958 948 -956 709 0 +-958 -948 0 +-958 956 0 +-958 -709 0 +959 948 956 -709 -960 0 +-959 -961 0 +-959 -948 0 +-959 -956 0 +-959 709 0 +-959 960 0 +962 948 956 709 960 0 +-962 -961 0 +-962 -948 0 +-962 -956 0 +-962 -709 0 +-962 -960 0 +955 957 958 959 962 0 +-711 0 +963 -964 965 956 0 +-963 -48 0 +-963 -49 0 +-963 -966 0 +-963 -967 0 +-963 -41 0 +-963 -42 0 +-963 -43 0 +-963 -44 0 +-963 -45 0 +-963 -46 0 +-963 -47 0 +-963 964 0 +-963 -965 0 +-963 -956 0 +968 -969 -965 0 +-968 -48 0 +-968 -49 0 +-968 -41 0 +-968 -42 0 +-968 -43 0 +-968 -44 0 +-968 -45 0 +-968 -46 0 +-968 -47 0 +-968 -970 0 +-968 969 0 +-968 965 0 +963 968 0 +-712 0 +971 -972 973 722 0 +-971 972 0 +-971 -973 0 +-971 -722 0 +974 -972 -973 -722 0 +-974 972 0 +-974 973 0 +-974 722 0 +975 972 -973 722 0 +-975 -972 0 +-975 973 0 +-975 -722 0 +976 972 973 -722 -977 0 +-976 -978 0 +-976 -972 0 +-976 -973 0 +-976 722 0 +-976 977 0 +979 972 973 722 977 0 +-979 -978 0 +-979 -972 0 +-979 -973 0 +-979 -722 0 +-979 -977 0 +971 974 975 976 979 0 +-723 0 +980 -973 981 730 0 +-980 973 0 +-980 -981 0 +-980 -730 0 +982 -973 -981 -730 0 +-982 973 0 +-982 981 0 +-982 730 0 +983 973 -981 730 0 +-983 -973 0 +-983 981 0 +-983 -730 0 +984 973 981 -730 -985 0 +-984 -986 0 +-984 -973 0 +-984 -981 0 +-984 730 0 +-984 985 0 +987 973 981 730 985 0 +-987 -986 0 +-987 -973 0 +-987 -981 0 +-987 -730 0 +-987 -985 0 +980 982 983 984 987 0 +-731 0 +988 -981 989 734 0 +-988 981 0 +-988 -989 0 +-988 -734 0 +990 -981 -989 -734 0 +-990 981 0 +-990 989 0 +-990 734 0 +991 981 -989 734 0 +-991 -981 0 +-991 989 0 +-991 -734 0 +992 981 989 -734 -993 0 +-992 -994 0 +-992 -981 0 +-992 -989 0 +-992 734 0 +-992 993 0 +995 981 989 734 993 0 +-995 -994 0 +-995 -981 0 +-995 -989 0 +-995 -734 0 +-995 -993 0 +988 990 991 992 995 0 +-735 0 +996 -997 965 989 0 +-996 -998 0 +-996 -999 0 +-996 997 0 +-996 -965 0 +-996 -989 0 +1000 -1001 -965 0 +-1000 -1002 0 +-1000 1001 0 +-1000 965 0 +996 1000 0 +-736 0 +1003 -435 940 744 0 +-1003 435 0 +-1003 -940 0 +-1003 -744 0 +1004 -435 -940 -744 0 +-1004 435 0 +-1004 940 0 +-1004 744 0 +1005 435 -940 744 0 +-1005 -435 0 +-1005 940 0 +-1005 -744 0 +1006 435 940 -744 -1007 0 +-1006 -1008 0 +-1006 -435 0 +-1006 -940 0 +-1006 744 0 +-1006 1007 0 +1009 435 940 744 1007 0 +-1009 -1008 0 +-1009 -435 0 +-1009 -940 0 +-1009 -744 0 +-1009 -1007 0 +1003 1004 1005 1006 1009 0 +-745 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 752 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 0 +1010 -276 560 751 0 +-1010 276 0 +-1010 -560 0 +-1010 -751 0 +1011 -276 -560 -751 0 +-1011 276 0 +-1011 560 0 +-1011 751 0 +1012 276 -560 751 0 +-1012 -276 0 +-1012 560 0 +-1012 -751 0 +1013 276 560 -751 -1014 0 +-1013 -1015 0 +-1013 -276 0 +-1013 -560 0 +-1013 751 0 +-1013 1014 0 +1016 276 560 751 1014 0 +-1016 -1015 0 +-1016 -276 0 +-1016 -560 0 +-1016 -751 0 +-1016 -1014 0 +1010 1011 1012 1013 1016 0 +-752 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 782 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 0 +1017 -895 1018 781 0 +-1017 895 0 +-1017 -1018 0 +-1017 -781 0 +1019 -895 -1018 -781 0 +-1019 895 0 +-1019 1018 0 +-1019 781 0 +1020 895 -1018 781 0 +-1020 -895 0 +-1020 1018 0 +-1020 -781 0 +1021 895 1018 -781 -1022 0 +-1021 -1023 0 +-1021 -895 0 +-1021 -1018 0 +-1021 781 0 +-1021 1022 0 +1024 895 1018 781 1022 0 +-1024 -1023 0 +-1024 -895 0 +-1024 -1018 0 +-1024 -781 0 +-1024 -1022 0 +1017 1019 1020 1021 1024 0 +-782 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 789 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 0 +1025 -877 894 788 0 +-1025 877 0 +-1025 -894 0 +-1025 -788 0 +1026 -877 -894 -788 0 +-1026 877 0 +-1026 894 0 +-1026 788 0 +1027 877 -894 788 0 +-1027 -877 0 +-1027 894 0 +-1027 -788 0 +1028 877 894 -788 -1029 0 +-1028 -1030 0 +-1028 -877 0 +-1028 -894 0 +-1028 788 0 +-1028 1029 0 +1031 877 894 788 1029 0 +-1031 -1030 0 +-1031 -877 0 +-1031 -894 0 +-1031 -788 0 +-1031 -1029 0 +1025 1026 1027 1028 1031 0 +-789 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 797 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 0 +1032 -877 1033 796 0 +-1032 877 0 +-1032 -1033 0 +-1032 -796 0 +1034 -877 -1033 -796 0 +-1034 877 0 +-1034 1033 0 +-1034 796 0 +1035 877 -1033 796 0 +-1035 -877 0 +-1035 1033 0 +-1035 -796 0 +1036 877 1033 -796 -1037 0 +-1036 -1038 0 +-1036 -877 0 +-1036 -1033 0 +-1036 796 0 +-1036 1037 0 +1039 877 1033 796 1037 0 +-1039 -1038 0 +-1039 -877 0 +-1039 -1033 0 +-1039 -796 0 +-1039 -1037 0 +1032 1034 1035 1036 1039 0 +-797 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 805 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 0 +1040 -1033 1041 804 0 +-1040 1033 0 +-1040 -1041 0 +-1040 -804 0 +1042 -1033 -1041 -804 0 +-1042 1033 0 +-1042 1041 0 +-1042 804 0 +1043 1033 -1041 804 0 +-1043 -1033 0 +-1043 1041 0 +-1043 -804 0 +1044 1033 1041 -804 -1045 0 +-1044 -1046 0 +-1044 -1033 0 +-1044 -1041 0 +-1044 804 0 +-1044 1045 0 +1047 1033 1041 804 1045 0 +-1047 -1046 0 +-1047 -1033 0 +-1047 -1041 0 +-1047 -804 0 +-1047 -1045 0 +1040 1042 1043 1044 1047 0 +-805 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 810 811 814 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 0 +1048 -102 769 813 0 +-1048 102 0 +-1048 -769 0 +-1048 -813 0 +1049 -102 -769 -813 0 +-1049 102 0 +-1049 769 0 +-1049 813 0 +1050 102 -769 813 0 +-1050 -102 0 +-1050 769 0 +-1050 -813 0 +1051 102 769 -813 -1052 0 +-1051 -1053 0 +-1051 -102 0 +-1051 -769 0 +-1051 813 0 +-1051 1052 0 +1054 102 769 813 1052 0 +-1054 -1053 0 +-1054 -102 0 +-1054 -769 0 +-1054 -813 0 +-1054 -1052 0 +1048 1049 1050 1051 1054 0 +-814 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 774 810 811 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 0 +1055 -777 1056 773 0 +-1055 777 0 +-1055 -1056 0 +-1055 -773 0 +1057 -777 -1056 -773 0 +-1057 777 0 +-1057 1056 0 +-1057 773 0 +1058 777 -1056 773 0 +-1058 -777 0 +-1058 1056 0 +-1058 -773 0 +1059 777 1056 -773 -1060 0 +-1059 -1061 0 +-1059 -777 0 +-1059 -1056 0 +-1059 773 0 +-1059 1060 0 +1062 777 1056 773 1060 0 +-1062 -1061 0 +-1062 -777 0 +-1062 -1056 0 +-1062 -773 0 +-1062 -1060 0 +1055 1057 1058 1059 1062 0 +-774 0 +a 0 +1063 -1056 1064 808 0 +-1063 1056 0 +-1063 -1064 0 +-1063 -808 0 +1065 -1056 -1064 -808 0 +-1065 1056 0 +-1065 1064 0 +-1065 808 0 +1066 1056 -1064 808 0 +-1066 -1056 0 +-1066 1064 0 +-1066 -808 0 +1067 1056 1064 -808 -1068 0 +-1067 -1069 0 +-1067 -1056 0 +-1067 -1064 0 +-1067 808 0 +-1067 1068 0 +1070 1056 1064 808 1068 0 +-1070 -1069 0 +-1070 -1056 0 +-1070 -1064 0 +-1070 -808 0 +-1070 -1068 0 +1063 1065 1066 1067 1070 0 +-810 0 +1071 -1072 1073 1064 0 +-1071 -48 0 +-1071 -49 0 +-1071 -1074 0 +-1071 -1075 0 +-1071 -45 0 +-1071 -46 0 +-1071 -47 0 +-1071 1072 0 +-1071 -1073 0 +-1071 -1064 0 +1076 -1077 -1073 0 +-1076 -48 0 +-1076 -49 0 +-1076 -45 0 +-1076 -46 0 +-1076 -47 0 +-1076 -1078 0 +-1076 1077 0 +-1076 1073 0 +1071 1076 0 +-811 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 409 527 535 539 540 550 557 589 620 636 675 739 759 766 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 0 +1079 -885 521 408 0 +-1079 885 0 +-1079 -521 0 +-1079 -408 0 +1080 -885 -521 -408 0 +-1080 885 0 +-1080 521 0 +-1080 408 0 +1081 885 -521 408 0 +-1081 -885 0 +-1081 521 0 +-1081 -408 0 +1082 885 521 -408 -1083 0 +-1082 -1084 0 +-1082 -885 0 +-1082 -521 0 +-1082 408 0 +-1082 1083 0 +1085 885 521 408 1083 0 +-1085 -1084 0 +-1085 -885 0 +-1085 -521 0 +-1085 -408 0 +-1085 -1083 0 +1079 1080 1081 1082 1085 0 +-409 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 828 836 841 842 845 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 0 +1086 -76 831 844 0 +-1086 76 0 +-1086 -831 0 +-1086 -844 0 +1087 -76 -831 -844 0 +-1087 76 0 +-1087 831 0 +-1087 844 0 +1088 76 -831 844 0 +-1088 -76 0 +-1088 831 0 +-1088 -844 0 +1089 76 831 -844 -1090 0 +-1089 -1091 0 +-1089 -76 0 +-1089 -831 0 +-1089 844 0 +-1089 1090 0 +1092 76 831 844 1090 0 +-1092 -1091 0 +-1092 -76 0 +-1092 -831 0 +-1092 -844 0 +-1092 -1090 0 +1086 1087 1088 1089 1092 0 +-845 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 828 836 841 842 851 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1091 0 +1093 -592 1094 827 0 +-1093 592 0 +-1093 -1094 0 +-1093 -827 0 +1095 -592 -1094 -827 0 +-1095 592 0 +-1095 1094 0 +-1095 827 0 +1096 592 -1094 827 0 +-1096 -592 0 +-1096 1094 0 +-1096 -827 0 +1097 592 1094 -827 -1098 0 +-1097 -1099 0 +-1097 -592 0 +-1097 -1094 0 +-1097 827 0 +-1097 1098 0 +1100 592 1094 827 1098 0 +-1100 -1099 0 +-1100 -592 0 +-1100 -1094 0 +-1100 -827 0 +-1100 -1098 0 +1093 1095 1096 1097 1100 0 +-828 0 +a 0 +1101 -1094 1102 835 0 +-1101 1094 0 +-1101 -1102 0 +-1101 -835 0 +1103 -1094 -1102 -835 0 +-1103 1094 0 +-1103 1102 0 +-1103 835 0 +1104 1094 -1102 835 0 +-1104 -1094 0 +-1104 1102 0 +-1104 -835 0 +1105 1094 1102 -835 -1106 0 +-1105 -1107 0 +-1105 -1094 0 +-1105 -1102 0 +-1105 835 0 +-1105 1106 0 +1108 1094 1102 835 1106 0 +-1108 -1107 0 +-1108 -1094 0 +-1108 -1102 0 +-1108 -835 0 +-1108 -1106 0 +1101 1103 1104 1105 1108 0 +-836 0 +1109 -1102 1110 839 0 +-1109 1102 0 +-1109 -1110 0 +-1109 -839 0 +1111 -1102 -1110 -839 0 +-1111 1102 0 +-1111 1110 0 +-1111 839 0 +1112 1102 -1110 839 0 +-1112 -1102 0 +-1112 1110 0 +-1112 -839 0 +1113 1102 1110 -839 -1114 0 +-1113 -1115 0 +-1113 -1102 0 +-1113 -1110 0 +-1113 839 0 +-1113 1114 0 +1116 1102 1110 839 1114 0 +-1116 -1115 0 +-1116 -1102 0 +-1116 -1110 0 +-1116 -839 0 +-1116 -1114 0 +1109 1111 1112 1113 1116 0 +-841 0 +1117 -1118 1119 1110 0 +-1117 -48 0 +-1117 -49 0 +-1117 -45 0 +-1117 -46 0 +-1117 -47 0 +-1117 -1120 0 +-1117 -1121 0 +-1117 -42 0 +-1117 -43 0 +-1117 -44 0 +-1117 1118 0 +-1117 -1119 0 +-1117 -1110 0 +1122 -1123 -1119 0 +-1122 -48 0 +-1122 -49 0 +-1122 -45 0 +-1122 -46 0 +-1122 -47 0 +-1122 -42 0 +-1122 -43 0 +-1122 -44 0 +-1122 -1124 0 +-1122 1123 0 +-1122 1119 0 +1117 1122 0 +-842 0 +1125 -427 1094 850 0 +-1125 427 0 +-1125 -1094 0 +-1125 -850 0 +1126 -427 -1094 -850 0 +-1126 427 0 +-1126 1094 0 +-1126 850 0 +1127 427 -1094 850 0 +-1127 -427 0 +-1127 1094 0 +-1127 -850 0 +1128 427 1094 -850 -1129 0 +-1128 -1130 0 +-1128 -427 0 +-1128 -1094 0 +-1128 850 0 +-1128 1129 0 +1131 427 1094 850 1129 0 +-1131 -1130 0 +-1131 -427 0 +-1131 -1094 0 +-1131 -850 0 +-1131 -1129 0 +1125 1126 1127 1128 1131 0 +-851 0 +1132 -219 1102 1090 0 +-1132 219 0 +-1132 -1102 0 +-1132 -1090 0 +1133 -219 -1102 -1090 0 +-1133 219 0 +-1133 1102 0 +-1133 1090 0 +1134 219 -1102 1090 0 +-1134 -219 0 +-1134 1102 0 +-1134 -1090 0 +1135 219 1102 -1090 -1136 0 +-1135 -1137 0 +-1135 -219 0 +-1135 -1102 0 +-1135 1090 0 +-1135 1136 0 +1138 219 1102 1090 1136 0 +-1138 -1137 0 +-1138 -219 0 +-1138 -1102 0 +-1138 -1090 0 +-1138 -1136 0 +1132 1133 1134 1135 1138 0 +-1091 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 856 857 860 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 0 +1139 -85 220 859 0 +-1139 85 0 +-1139 -220 0 +-1139 -859 0 +1140 -85 -220 -859 0 +-1140 85 0 +-1140 220 0 +-1140 859 0 +1141 85 -220 859 0 +-1141 -85 0 +-1141 220 0 +-1141 -859 0 +1142 85 220 -859 -1143 0 +-1142 -1144 0 +-1142 -85 0 +-1142 -220 0 +-1142 859 0 +-1142 1143 0 +1145 85 220 859 1143 0 +-1145 -1144 0 +-1145 -85 0 +-1145 -220 0 +-1145 -859 0 +-1145 -1143 0 +1139 1140 1141 1142 1145 0 +-860 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 225 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 856 857 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 0 +1146 -1147 427 224 0 +-1146 1147 0 +-1146 -427 0 +-1146 -224 0 +1148 -1147 -427 -224 0 +-1148 1147 0 +-1148 427 0 +-1148 224 0 +1149 1147 -427 224 0 +-1149 -1147 0 +-1149 427 0 +-1149 -224 0 +1150 1147 427 -224 -1151 0 +-1150 -1152 0 +-1150 -1147 0 +-1150 -427 0 +-1150 224 0 +-1150 1151 0 +1153 1147 427 224 1151 0 +-1153 -1152 0 +-1153 -1147 0 +-1153 -427 0 +-1153 -224 0 +-1153 -1151 0 +1146 1148 1149 1150 1153 0 +-225 0 +a 0 +1154 -1147 1155 854 0 +-1154 1147 0 +-1154 -1155 0 +-1154 -854 0 +1156 -1147 -1155 -854 0 +-1156 1147 0 +-1156 1155 0 +-1156 854 0 +1157 1147 -1155 854 0 +-1157 -1147 0 +-1157 1155 0 +-1157 -854 0 +1158 1147 1155 -854 -1159 0 +-1158 -1160 0 +-1158 -1147 0 +-1158 -1155 0 +-1158 854 0 +-1158 1159 0 +1161 1147 1155 854 1159 0 +-1161 -1160 0 +-1161 -1147 0 +-1161 -1155 0 +-1161 -854 0 +-1161 -1159 0 +1154 1156 1157 1158 1161 0 +-856 0 +1162 -1163 1164 1155 0 +-1162 -48 0 +-1162 -49 0 +-1162 -45 0 +-1162 -46 0 +-1162 -47 0 +-1162 -43 0 +-1162 -44 0 +-1162 -1165 0 +-1162 -1166 0 +-1162 1163 0 +-1162 -1164 0 +-1162 -1155 0 +1167 -1168 -1164 0 +-1167 -48 0 +-1167 -49 0 +-1167 -45 0 +-1167 -46 0 +-1167 -47 0 +-1167 -43 0 +-1167 -44 0 +-1167 -1169 0 +-1167 1168 0 +-1167 1164 0 +1162 1167 0 +-857 0 +a 0 +a 107 111 112 119 120 127 128 154 162 170 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 0 +1170 -110 102 106 0 +-1170 110 0 +-1170 -102 0 +-1170 -106 0 +1171 -110 -102 -106 0 +-1171 110 0 +-1171 102 0 +-1171 106 0 +1172 110 -102 106 0 +-1172 -110 0 +-1172 102 0 +-1172 -106 0 +1173 110 102 -106 -1174 0 +-1173 -1175 0 +-1173 -110 0 +-1173 -102 0 +-1173 106 0 +-1173 1174 0 +1176 110 102 106 1174 0 +-1176 -1175 0 +-1176 -110 0 +-1176 -102 0 +-1176 -106 0 +-1176 -1174 0 +1170 1171 1172 1173 1176 0 +-107 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 866 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 0 +1177 -18 1041 865 0 +-1177 18 0 +-1177 -1041 0 +-1177 -865 0 +1178 -18 -1041 -865 0 +-1178 18 0 +-1178 1041 0 +-1178 865 0 +1179 18 -1041 865 0 +-1179 -18 0 +-1179 1041 0 +-1179 -865 0 +1180 18 1041 -865 -1181 0 +-1180 -1182 0 +-1180 -18 0 +-1180 -1041 0 +-1180 865 0 +-1180 1181 0 +1183 18 1041 865 1181 0 +-1183 -1182 0 +-1183 -18 0 +-1183 -1041 0 +-1183 -865 0 +-1183 -1181 0 +1177 1178 1179 1180 1183 0 +-866 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 874 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 0 +1184 -894 1185 873 0 +-1184 894 0 +-1184 -1185 0 +-1184 -873 0 +1186 -894 -1185 -873 0 +-1186 894 0 +-1186 1185 0 +-1186 873 0 +1187 894 -1185 873 0 +-1187 -894 0 +-1187 1185 0 +-1187 -873 0 +1188 894 1185 -873 -1189 0 +-1188 -1190 0 +-1188 -894 0 +-1188 -1185 0 +-1188 873 0 +-1188 1189 0 +1191 894 1185 873 1189 0 +-1191 -1190 0 +-1191 -894 0 +-1191 -1185 0 +-1191 -873 0 +-1191 -1189 0 +1184 1186 1187 1188 1191 0 +-874 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 882 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 0 +1192 -1185 1193 881 0 +-1192 1185 0 +-1192 -1193 0 +-1192 -881 0 +1194 -1185 -1193 -881 0 +-1194 1185 0 +-1194 1193 0 +-1194 881 0 +1195 1185 -1193 881 0 +-1195 -1185 0 +-1195 1193 0 +-1195 -881 0 +1196 1185 1193 -881 -1197 0 +-1196 -1198 0 +-1196 -1185 0 +-1196 -1193 0 +-1196 881 0 +-1196 1197 0 +1199 1185 1193 881 1197 0 +-1199 -1198 0 +-1199 -1185 0 +-1199 -1193 0 +-1199 -881 0 +-1199 -1197 0 +1192 1194 1195 1196 1199 0 +-882 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 527 535 539 540 550 557 589 620 636 675 739 759 766 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 0 +1200 -1201 1202 526 0 +-1200 1201 0 +-1200 -1202 0 +-1200 -526 0 +1203 -1201 -1202 -526 0 +-1203 1201 0 +-1203 1202 0 +-1203 526 0 +1204 1201 -1202 526 0 +-1204 -1201 0 +-1204 1202 0 +-1204 -526 0 +1205 1201 1202 -526 -1206 0 +-1205 -1207 0 +-1205 -1201 0 +-1205 -1202 0 +-1205 526 0 +-1205 1206 0 +1208 1201 1202 526 1206 0 +-1208 -1207 0 +-1208 -1201 0 +-1208 -1202 0 +-1208 -526 0 +-1208 -1206 0 +1200 1203 1204 1205 1208 0 +-527 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 535 539 540 550 557 589 620 636 675 739 759 766 891 900 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1207 0 +1209 -1210 1211 899 0 +-1209 1210 0 +-1209 -1211 0 +-1209 -899 0 +1212 -1210 -1211 -899 0 +-1212 1210 0 +-1212 1211 0 +-1212 899 0 +1213 1210 -1211 899 0 +-1213 -1210 0 +-1213 1211 0 +-1213 -899 0 +1214 1210 1211 -899 -1215 0 +-1214 -1216 0 +-1214 -1210 0 +-1214 -1211 0 +-1214 899 0 +-1214 1215 0 +1217 1210 1211 899 1215 0 +-1217 -1216 0 +-1217 -1210 0 +-1217 -1211 0 +-1217 -899 0 +-1217 -1215 0 +1209 1212 1213 1214 1217 0 +-900 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 535 539 540 550 557 589 620 636 675 739 759 766 891 908 915 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1207 1216 0 +1218 -592 932 914 0 +-1218 592 0 +-1218 -932 0 +-1218 -914 0 +1219 -592 -932 -914 0 +-1219 592 0 +-1219 932 0 +-1219 914 0 +1220 592 -932 914 0 +-1220 -592 0 +-1220 932 0 +-1220 -914 0 +1221 592 932 -914 -1222 0 +-1221 -1223 0 +-1221 -592 0 +-1221 -932 0 +-1221 914 0 +-1221 1222 0 +1224 592 932 914 1222 0 +-1224 -1223 0 +-1224 -592 0 +-1224 -932 0 +-1224 -914 0 +-1224 -1222 0 +1218 1219 1220 1221 1224 0 +-915 0 +a 0 +1225 -1202 1226 534 0 +-1225 1202 0 +-1225 -1226 0 +-1225 -534 0 +1227 -1202 -1226 -534 0 +-1227 1202 0 +-1227 1226 0 +-1227 534 0 +1228 1202 -1226 534 0 +-1228 -1202 0 +-1228 1226 0 +-1228 -534 0 +1229 1202 1226 -534 -1230 0 +-1229 -1231 0 +-1229 -1202 0 +-1229 -1226 0 +-1229 534 0 +-1229 1230 0 +1232 1202 1226 534 1230 0 +-1232 -1231 0 +-1232 -1202 0 +-1232 -1226 0 +-1232 -534 0 +-1232 -1230 0 +1225 1227 1228 1229 1232 0 +-535 0 +1233 -1226 1234 538 0 +-1233 1226 0 +-1233 -1234 0 +-1233 -538 0 +1235 -1226 -1234 -538 0 +-1235 1226 0 +-1235 1234 0 +-1235 538 0 +1236 1226 -1234 538 0 +-1236 -1226 0 +-1236 1234 0 +-1236 -538 0 +1237 1226 1234 -538 -1238 0 +-1237 -1239 0 +-1237 -1226 0 +-1237 -1234 0 +-1237 538 0 +-1237 1238 0 +1240 1226 1234 538 1238 0 +-1240 -1239 0 +-1240 -1226 0 +-1240 -1234 0 +-1240 -538 0 +-1240 -1238 0 +1233 1235 1236 1237 1240 0 +-539 0 +1241 -1242 809 1234 0 +-1241 -48 0 +-1241 -49 0 +-1241 -1243 0 +-1241 -1244 0 +-1241 -46 0 +-1241 -47 0 +-1241 1242 0 +-1241 -809 0 +-1241 -1234 0 +1245 -1246 -809 0 +-1245 -48 0 +-1245 -49 0 +-1245 -46 0 +-1245 -47 0 +-1245 -1247 0 +-1245 1246 0 +-1245 809 0 +1241 1245 0 +-540 0 +1248 -809 1249 549 0 +-1248 809 0 +-1248 -1249 0 +-1248 -549 0 +1250 -809 -1249 -549 0 +-1250 809 0 +-1250 1249 0 +-1250 549 0 +1251 809 -1249 549 0 +-1251 -809 0 +-1251 1249 0 +-1251 -549 0 +1252 809 1249 -549 -1253 0 +-1252 -1254 0 +-1252 -809 0 +-1252 -1249 0 +-1252 549 0 +-1252 1253 0 +1255 809 1249 549 1253 0 +-1255 -1254 0 +-1255 -809 0 +-1255 -1249 0 +-1255 -549 0 +-1255 -1253 0 +1248 1250 1251 1252 1255 0 +-550 0 +1256 -654 1201 758 0 +-1256 654 0 +-1256 -1201 0 +-1256 -758 0 +1257 -654 -1201 -758 0 +-1257 654 0 +-1257 1201 0 +-1257 758 0 +1258 654 -1201 758 0 +-1258 -654 0 +-1258 1201 0 +-1258 -758 0 +1259 654 1201 -758 -1260 0 +-1259 -1261 0 +-1259 -654 0 +-1259 -1201 0 +-1259 758 0 +-1259 1260 0 +1262 654 1201 758 1260 0 +-1262 -1261 0 +-1262 -654 0 +-1262 -1201 0 +-1262 -758 0 +-1262 -1260 0 +1256 1257 1258 1259 1262 0 +-759 0 +a 0 +1263 -1264 1265 1206 0 +-1263 1264 0 +-1263 -1265 0 +-1263 -1206 0 +1266 -1264 -1265 -1206 0 +-1266 1264 0 +-1266 1265 0 +-1266 1206 0 +1267 1264 -1265 1206 0 +-1267 -1264 0 +-1267 1265 0 +-1267 -1206 0 +1268 1264 1265 -1206 -1269 0 +-1268 -1270 0 +-1268 -1264 0 +-1268 -1265 0 +-1268 1206 0 +-1268 1269 0 +1271 1264 1265 1206 1269 0 +-1271 -1270 0 +-1271 -1264 0 +-1271 -1265 0 +-1271 -1206 0 +-1271 -1269 0 +1263 1266 1267 1268 1271 0 +-1207 0 +1272 -1264 1273 1230 0 +-1272 1264 0 +-1272 -1273 0 +-1272 -1230 0 +1274 -1264 -1273 -1230 0 +-1274 1264 0 +-1274 1273 0 +-1274 1230 0 +1275 1264 -1273 1230 0 +-1275 -1264 0 +-1275 1273 0 +-1275 -1230 0 +1276 1264 1273 -1230 -1277 0 +-1276 -1278 0 +-1276 -1264 0 +-1276 -1273 0 +-1276 1230 0 +-1276 1277 0 +1279 1264 1273 1230 1277 0 +-1279 -1278 0 +-1279 -1264 0 +-1279 -1273 0 +-1279 -1230 0 +-1279 -1277 0 +1272 1274 1275 1276 1279 0 +-1231 0 +1280 -1273 1281 1238 0 +-1280 1273 0 +-1280 -1281 0 +-1280 -1238 0 +1282 -1273 -1281 -1238 0 +-1282 1273 0 +-1282 1281 0 +-1282 1238 0 +1283 1273 -1281 1238 0 +-1283 -1273 0 +-1283 1281 0 +-1283 -1238 0 +1284 1273 1281 -1238 -1285 0 +-1284 -1286 0 +-1284 -1273 0 +-1284 -1281 0 +-1284 1238 0 +-1284 1285 0 +1287 1273 1281 1238 1285 0 +-1287 -1286 0 +-1287 -1273 0 +-1287 -1281 0 +-1287 -1238 0 +-1287 -1285 0 +1280 1282 1283 1284 1287 0 +-1239 0 +1288 -1281 1289 1242 0 +-1288 1281 0 +-1288 -1289 0 +-1288 -1242 0 +1290 -1281 -1289 -1242 0 +-1290 1281 0 +-1290 1289 0 +-1290 1242 0 +1291 1281 -1289 1242 0 +-1291 -1281 0 +-1291 1289 0 +-1291 -1242 0 +1292 1281 1289 -1242 -1293 0 +-1292 -1294 0 +-1292 -1281 0 +-1292 -1289 0 +-1292 1242 0 +-1292 1293 0 +1295 1281 1289 1242 1293 0 +-1295 -1294 0 +-1295 -1281 0 +-1295 -1289 0 +-1295 -1242 0 +-1295 -1293 0 +1288 1290 1291 1292 1295 0 +-1243 0 +1296 -1297 1073 1289 0 +-1296 -48 0 +-1296 -49 0 +-1296 -46 0 +-1296 -47 0 +-1296 -1298 0 +-1296 -1299 0 +-1296 1297 0 +-1296 -1073 0 +-1296 -1289 0 +1300 -1301 -1073 0 +-1300 -48 0 +-1300 -49 0 +-1300 -46 0 +-1300 -47 0 +-1300 -1302 0 +-1300 1301 0 +-1300 1073 0 +1296 1300 0 +-1244 0 +1303 -1073 1304 1253 0 +-1303 1073 0 +-1303 -1304 0 +-1303 -1253 0 +1305 -1073 -1304 -1253 0 +-1305 1073 0 +-1305 1304 0 +-1305 1253 0 +1306 1073 -1304 1253 0 +-1306 -1073 0 +-1306 1304 0 +-1306 -1253 0 +1307 1073 1304 -1253 -1308 0 +-1307 -1309 0 +-1307 -1073 0 +-1307 -1304 0 +-1307 1253 0 +-1307 1308 0 +1310 1073 1304 1253 1308 0 +-1310 -1309 0 +-1310 -1073 0 +-1310 -1304 0 +-1310 -1253 0 +-1310 -1308 0 +1303 1305 1306 1307 1310 0 +-1254 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 557 589 620 636 675 739 766 891 908 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 0 +1311 -466 1201 556 0 +-1311 466 0 +-1311 -1201 0 +-1311 -556 0 +1312 -466 -1201 -556 0 +-1312 466 0 +-1312 1201 0 +-1312 556 0 +1313 466 -1201 556 0 +-1313 -466 0 +-1313 1201 0 +-1313 -556 0 +1314 466 1201 -556 -1315 0 +-1314 -1316 0 +-1314 -466 0 +-1314 -1201 0 +-1314 556 0 +-1314 1315 0 +1317 466 1201 556 1315 0 +-1317 -1316 0 +-1317 -466 0 +-1317 -1201 0 +-1317 -556 0 +-1317 -1315 0 +1311 1312 1313 1314 1317 0 +-557 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 922 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 0 +1318 -210 948 921 0 +-1318 210 0 +-1318 -948 0 +-1318 -921 0 +1319 -210 -948 -921 0 +-1319 210 0 +-1319 948 0 +-1319 921 0 +1320 210 -948 921 0 +-1320 -210 0 +-1320 948 0 +-1320 -921 0 +1321 210 948 -921 -1322 0 +-1321 -1323 0 +-1321 -210 0 +-1321 -948 0 +-1321 921 0 +-1321 1322 0 +1324 210 948 921 1322 0 +-1324 -1323 0 +-1324 -210 0 +-1324 -948 0 +-1324 -921 0 +-1324 -1322 0 +1318 1319 1320 1321 1324 0 +-922 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 937 945 953 961 966 967 970 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 0 +1325 -67 956 969 0 +-1325 67 0 +-1325 -956 0 +-1325 -969 0 +1326 -67 -956 -969 0 +-1326 67 0 +-1326 956 0 +-1326 969 0 +1327 67 -956 969 0 +-1327 -67 0 +-1327 956 0 +-1327 -969 0 +1328 67 956 -969 -1329 0 +-1328 -1330 0 +-1328 -67 0 +-1328 -956 0 +-1328 969 0 +-1328 1329 0 +1331 67 956 969 1329 0 +-1331 -1330 0 +-1331 -67 0 +-1331 -956 0 +-1331 -969 0 +-1331 -1329 0 +1325 1326 1327 1328 1331 0 +-970 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 937 945 953 961 966 967 978 986 994 998 999 1002 1008 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 0 +1332 -18 1333 936 0 +-1332 18 0 +-1332 -1333 0 +-1332 -936 0 +1334 -18 -1333 -936 0 +-1334 18 0 +-1334 1333 0 +-1334 936 0 +1335 18 -1333 936 0 +-1335 -18 0 +-1335 1333 0 +-1335 -936 0 +1336 18 1333 -936 -1337 0 +-1336 -1338 0 +-1336 -18 0 +-1336 -1333 0 +-1336 936 0 +-1336 1337 0 +1339 18 1333 936 1337 0 +-1339 -1338 0 +-1339 -18 0 +-1339 -1333 0 +-1339 -936 0 +-1339 -1337 0 +1332 1334 1335 1336 1339 0 +-937 0 +a 0 +1340 -1333 1341 944 0 +-1340 1333 0 +-1340 -1341 0 +-1340 -944 0 +1342 -1333 -1341 -944 0 +-1342 1333 0 +-1342 1341 0 +-1342 944 0 +1343 1333 -1341 944 0 +-1343 -1333 0 +-1343 1341 0 +-1343 -944 0 +1344 1333 1341 -944 -1345 0 +-1344 -1346 0 +-1344 -1333 0 +-1344 -1341 0 +-1344 944 0 +-1344 1345 0 +1347 1333 1341 944 1345 0 +-1347 -1346 0 +-1347 -1333 0 +-1347 -1341 0 +-1347 -944 0 +-1347 -1345 0 +1340 1342 1343 1344 1347 0 +-945 0 +1348 -1341 1349 952 0 +-1348 1341 0 +-1348 -1349 0 +-1348 -952 0 +1350 -1341 -1349 -952 0 +-1350 1341 0 +-1350 1349 0 +-1350 952 0 +1351 1341 -1349 952 0 +-1351 -1341 0 +-1351 1349 0 +-1351 -952 0 +1352 1341 1349 -952 -1353 0 +-1352 -1354 0 +-1352 -1341 0 +-1352 -1349 0 +-1352 952 0 +-1352 1353 0 +1355 1341 1349 952 1353 0 +-1355 -1354 0 +-1355 -1341 0 +-1355 -1349 0 +-1355 -952 0 +-1355 -1353 0 +1348 1350 1351 1352 1355 0 +-953 0 +1356 -1349 1357 960 0 +-1356 1349 0 +-1356 -1357 0 +-1356 -960 0 +1358 -1349 -1357 -960 0 +-1358 1349 0 +-1358 1357 0 +-1358 960 0 +1359 1349 -1357 960 0 +-1359 -1349 0 +-1359 1357 0 +-1359 -960 0 +1360 1349 1357 -960 -1361 0 +-1360 -1362 0 +-1360 -1349 0 +-1360 -1357 0 +-1360 960 0 +-1360 1361 0 +1363 1349 1357 960 1361 0 +-1363 -1362 0 +-1363 -1349 0 +-1363 -1357 0 +-1363 -960 0 +-1363 -1361 0 +1356 1358 1359 1360 1363 0 +-961 0 +1364 -1357 1365 964 0 +-1364 1357 0 +-1364 -1365 0 +-1364 -964 0 +1366 -1357 -1365 -964 0 +-1366 1357 0 +-1366 1365 0 +-1366 964 0 +1367 1357 -1365 964 0 +-1367 -1357 0 +-1367 1365 0 +-1367 -964 0 +1368 1357 1365 -964 -1369 0 +-1368 -1370 0 +-1368 -1357 0 +-1368 -1365 0 +-1368 964 0 +-1368 1369 0 +1371 1357 1365 964 1369 0 +-1371 -1370 0 +-1371 -1357 0 +-1371 -1365 0 +-1371 -964 0 +-1371 -1369 0 +1364 1366 1367 1368 1371 0 +-966 0 +1372 -1373 1374 1365 0 +-1372 -48 0 +-1372 -49 0 +-1372 -46 0 +-1372 -47 0 +-1372 -1375 0 +-1372 -1376 0 +-1372 -41 0 +-1372 -42 0 +-1372 -43 0 +-1372 -44 0 +-1372 -45 0 +-1372 1373 0 +-1372 -1374 0 +-1372 -1365 0 +1377 -1378 -1374 0 +-1377 -48 0 +-1377 -49 0 +-1377 -46 0 +-1377 -47 0 +-1377 -41 0 +-1377 -42 0 +-1377 -43 0 +-1377 -44 0 +-1377 -45 0 +-1377 -1379 0 +-1377 1378 0 +-1377 1374 0 +1372 1377 0 +-967 0 +1380 -1381 1382 977 0 +-1380 1381 0 +-1380 -1382 0 +-1380 -977 0 +1383 -1381 -1382 -977 0 +-1383 1381 0 +-1383 1382 0 +-1383 977 0 +1384 1381 -1382 977 0 +-1384 -1381 0 +-1384 1382 0 +-1384 -977 0 +1385 1381 1382 -977 -1386 0 +-1385 -1387 0 +-1385 -1381 0 +-1385 -1382 0 +-1385 977 0 +-1385 1386 0 +1388 1381 1382 977 1386 0 +-1388 -1387 0 +-1388 -1381 0 +-1388 -1382 0 +-1388 -977 0 +-1388 -1386 0 +1380 1383 1384 1385 1388 0 +-978 0 +1389 -1381 1390 985 0 +-1389 1381 0 +-1389 -1390 0 +-1389 -985 0 +1391 -1381 -1390 -985 0 +-1391 1381 0 +-1391 1390 0 +-1391 985 0 +1392 1381 -1390 985 0 +-1392 -1381 0 +-1392 1390 0 +-1392 -985 0 +1393 1381 1390 -985 -1394 0 +-1393 -1395 0 +-1393 -1381 0 +-1393 -1390 0 +-1393 985 0 +-1393 1394 0 +1396 1381 1390 985 1394 0 +-1396 -1395 0 +-1396 -1381 0 +-1396 -1390 0 +-1396 -985 0 +-1396 -1394 0 +1389 1391 1392 1393 1396 0 +-986 0 +1397 -1390 1398 993 0 +-1397 1390 0 +-1397 -1398 0 +-1397 -993 0 +1399 -1390 -1398 -993 0 +-1399 1390 0 +-1399 1398 0 +-1399 993 0 +1400 1390 -1398 993 0 +-1400 -1390 0 +-1400 1398 0 +-1400 -993 0 +1401 1390 1398 -993 -1402 0 +-1401 -1403 0 +-1401 -1390 0 +-1401 -1398 0 +-1401 993 0 +-1401 1402 0 +1404 1390 1398 993 1402 0 +-1404 -1403 0 +-1404 -1390 0 +-1404 -1398 0 +-1404 -993 0 +-1404 -1402 0 +1397 1399 1400 1401 1404 0 +-994 0 +1405 -1398 1406 997 0 +-1405 1398 0 +-1405 -1406 0 +-1405 -997 0 +1407 -1398 -1406 -997 0 +-1407 1398 0 +-1407 1406 0 +-1407 997 0 +1408 1398 -1406 997 0 +-1408 -1398 0 +-1408 1406 0 +-1408 -997 0 +1409 1398 1406 -997 -1410 0 +-1409 -1411 0 +-1409 -1398 0 +-1409 -1406 0 +-1409 997 0 +-1409 1410 0 +1412 1398 1406 997 1410 0 +-1412 -1411 0 +-1412 -1398 0 +-1412 -1406 0 +-1412 -997 0 +-1412 -1410 0 +1405 1407 1408 1409 1412 0 +-998 0 +1413 -1414 1374 1406 0 +-1413 -1415 0 +-1413 -1416 0 +-1413 1414 0 +-1413 -1374 0 +-1413 -1406 0 +1417 -1418 -1374 0 +-1417 -1419 0 +-1417 1418 0 +-1417 1374 0 +1413 1417 0 +-999 0 +1420 -592 1341 1007 0 +-1420 592 0 +-1420 -1341 0 +-1420 -1007 0 +1421 -592 -1341 -1007 0 +-1421 592 0 +-1421 1341 0 +-1421 1007 0 +1422 592 -1341 1007 0 +-1422 -592 0 +-1422 1341 0 +-1422 -1007 0 +1423 592 1341 -1007 -1424 0 +-1423 -1425 0 +-1423 -592 0 +-1423 -1341 0 +-1423 1007 0 +-1423 1424 0 +1426 592 1341 1007 1424 0 +-1426 -1425 0 +-1426 -592 0 +-1426 -1341 0 +-1426 -1007 0 +-1426 -1424 0 +1420 1421 1422 1423 1426 0 +-1008 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 1002 1015 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 0 +1427 -522 777 1014 0 +-1427 522 0 +-1427 -777 0 +-1427 -1014 0 +1428 -522 -777 -1014 0 +-1428 522 0 +-1428 777 0 +-1428 1014 0 +1429 522 -777 1014 0 +-1429 -522 0 +-1429 777 0 +-1429 -1014 0 +1430 522 777 -1014 -1431 0 +-1430 -1432 0 +-1430 -522 0 +-1430 -777 0 +-1430 1014 0 +-1430 1431 0 +1433 522 777 1014 1431 0 +-1433 -1432 0 +-1433 -522 0 +-1433 -777 0 +-1433 -1014 0 +-1433 -1431 0 +1427 1428 1429 1430 1433 0 +-1015 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 1002 1023 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 0 +1434 -1210 1435 1022 0 +-1434 1210 0 +-1434 -1435 0 +-1434 -1022 0 +1436 -1210 -1435 -1022 0 +-1436 1210 0 +-1436 1435 0 +-1436 1022 0 +1437 1210 -1435 1022 0 +-1437 -1210 0 +-1437 1435 0 +-1437 -1022 0 +1438 1210 1435 -1022 -1439 0 +-1438 -1440 0 +-1438 -1210 0 +-1438 -1435 0 +-1438 1022 0 +-1438 1439 0 +1441 1210 1435 1022 1439 0 +-1441 -1440 0 +-1441 -1210 0 +-1441 -1435 0 +-1441 -1022 0 +-1441 -1439 0 +1434 1436 1437 1438 1441 0 +-1023 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 1002 1030 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 0 +1442 -1193 1211 1029 0 +-1442 1193 0 +-1442 -1211 0 +-1442 -1029 0 +1443 -1193 -1211 -1029 0 +-1443 1193 0 +-1443 1211 0 +-1443 1029 0 +1444 1193 -1211 1029 0 +-1444 -1193 0 +-1444 1211 0 +-1444 -1029 0 +1445 1193 1211 -1029 -1446 0 +-1445 -1447 0 +-1445 -1193 0 +-1445 -1211 0 +-1445 1029 0 +-1445 1446 0 +1448 1193 1211 1029 1446 0 +-1448 -1447 0 +-1448 -1193 0 +-1448 -1211 0 +-1448 -1029 0 +-1448 -1446 0 +1442 1443 1444 1445 1448 0 +-1030 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 1002 1038 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 0 +1449 -1193 1450 1037 0 +-1449 1193 0 +-1449 -1450 0 +-1449 -1037 0 +1451 -1193 -1450 -1037 0 +-1451 1193 0 +-1451 1450 0 +-1451 1037 0 +1452 1193 -1450 1037 0 +-1452 -1193 0 +-1452 1450 0 +-1452 -1037 0 +1453 1193 1450 -1037 -1454 0 +-1453 -1455 0 +-1453 -1193 0 +-1453 -1450 0 +-1453 1037 0 +-1453 1454 0 +1456 1193 1450 1037 1454 0 +-1456 -1455 0 +-1456 -1193 0 +-1456 -1450 0 +-1456 -1037 0 +-1456 -1454 0 +1449 1451 1452 1453 1456 0 +-1038 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 589 620 636 675 739 766 891 908 929 1002 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 0 +1457 -622 1458 588 0 +-1457 622 0 +-1457 -1458 0 +-1457 -588 0 +1459 -622 -1458 -588 0 +-1459 622 0 +-1459 1458 0 +-1459 588 0 +1460 622 -1458 588 0 +-1460 -622 0 +-1460 1458 0 +-1460 -588 0 +1461 622 1458 -588 -1462 0 +-1461 -1463 0 +-1461 -622 0 +-1461 -1458 0 +-1461 588 0 +-1461 1462 0 +1464 622 1458 588 1462 0 +-1464 -1463 0 +-1464 -622 0 +-1464 -1458 0 +-1464 -588 0 +-1464 -1462 0 +1457 1459 1460 1461 1464 0 +-589 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1046 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 0 +1465 -1450 1466 1045 0 +-1465 1450 0 +-1465 -1466 0 +-1465 -1045 0 +1467 -1450 -1466 -1045 0 +-1467 1450 0 +-1467 1466 0 +-1467 1045 0 +1468 1450 -1466 1045 0 +-1468 -1450 0 +-1468 1466 0 +-1468 -1045 0 +1469 1450 1466 -1045 -1470 0 +-1469 -1471 0 +-1469 -1450 0 +-1469 -1466 0 +-1469 1045 0 +-1469 1470 0 +1472 1450 1466 1045 1470 0 +-1472 -1471 0 +-1472 -1450 0 +-1472 -1466 0 +-1472 -1045 0 +-1472 -1470 0 +1465 1467 1468 1469 1472 0 +-1046 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1053 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 0 +1473 -276 1056 1052 0 +-1473 276 0 +-1473 -1056 0 +-1473 -1052 0 +1474 -276 -1056 -1052 0 +-1474 276 0 +-1474 1056 0 +-1474 1052 0 +1475 276 -1056 1052 0 +-1475 -276 0 +-1475 1056 0 +-1475 -1052 0 +1476 276 1056 -1052 -1477 0 +-1476 -1478 0 +-1476 -276 0 +-1476 -1056 0 +-1476 1052 0 +-1476 1477 0 +1479 276 1056 1052 1477 0 +-1479 -1478 0 +-1479 -276 0 +-1479 -1056 0 +-1479 -1052 0 +-1479 -1477 0 +1473 1474 1475 1476 1479 0 +-1053 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1061 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 0 +1480 -1018 1481 1060 0 +-1480 1018 0 +-1480 -1481 0 +-1480 -1060 0 +1482 -1018 -1481 -1060 0 +-1482 1018 0 +-1482 1481 0 +-1482 1060 0 +1483 1018 -1481 1060 0 +-1483 -1018 0 +-1483 1481 0 +-1483 -1060 0 +1484 1018 1481 -1060 -1485 0 +-1484 -1486 0 +-1484 -1018 0 +-1484 -1481 0 +-1484 1060 0 +-1484 1485 0 +1487 1018 1481 1060 1485 0 +-1487 -1486 0 +-1487 -1018 0 +-1487 -1481 0 +-1487 -1060 0 +-1487 -1485 0 +1480 1482 1483 1484 1487 0 +-1061 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1069 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 0 +1488 -1481 1489 1068 0 +-1488 1481 0 +-1488 -1489 0 +-1488 -1068 0 +1490 -1481 -1489 -1068 0 +-1490 1481 0 +-1490 1489 0 +-1490 1068 0 +1491 1481 -1489 1068 0 +-1491 -1481 0 +-1491 1489 0 +-1491 -1068 0 +1492 1481 1489 -1068 -1493 0 +-1492 -1494 0 +-1492 -1481 0 +-1492 -1489 0 +-1492 1068 0 +-1492 1493 0 +1495 1481 1489 1068 1493 0 +-1495 -1494 0 +-1495 -1481 0 +-1495 -1489 0 +-1495 -1068 0 +-1495 -1493 0 +1488 1490 1491 1492 1495 0 +-1069 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1074 1075 1078 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 0 +1496 -102 1064 1077 0 +-1496 102 0 +-1496 -1064 0 +-1496 -1077 0 +1497 -102 -1064 -1077 0 +-1497 102 0 +-1497 1064 0 +-1497 1077 0 +1498 102 -1064 1077 0 +-1498 -102 0 +-1498 1064 0 +-1498 -1077 0 +1499 102 1064 -1077 -1500 0 +-1499 -1501 0 +-1499 -102 0 +-1499 -1064 0 +-1499 1077 0 +-1499 1500 0 +1502 102 1064 1077 1500 0 +-1502 -1501 0 +-1502 -102 0 +-1502 -1064 0 +-1502 -1077 0 +-1502 -1500 0 +1496 1497 1498 1499 1502 0 +-1078 0 +a 0 +a 111 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1074 1075 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 0 +1503 -885 1504 109 0 +-1503 885 0 +-1503 -1504 0 +-1503 -109 0 +1505 -885 -1504 -109 0 +-1505 885 0 +-1505 1504 0 +-1505 109 0 +1506 885 -1504 109 0 +-1506 -885 0 +-1506 1504 0 +-1506 -109 0 +1507 885 1504 -109 -1508 0 +-1507 -1509 0 +-1507 -885 0 +-1507 -1504 0 +-1507 109 0 +-1507 1508 0 +1510 885 1504 109 1508 0 +-1510 -1509 0 +-1510 -885 0 +-1510 -1504 0 +-1510 -109 0 +-1510 -1508 0 +1503 1505 1506 1507 1510 0 +-111 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 675 739 766 891 908 929 1002 1074 1075 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 0 +1511 -499 1512 674 0 +-1511 499 0 +-1511 -1512 0 +-1511 -674 0 +1513 -499 -1512 -674 0 +-1513 499 0 +-1513 1512 0 +-1513 674 0 +1514 499 -1512 674 0 +-1514 -499 0 +-1514 1512 0 +-1514 -674 0 +1515 499 1512 -674 -1516 0 +-1515 -1517 0 +-1515 -499 0 +-1515 -1512 0 +-1515 674 0 +-1515 1516 0 +1518 499 1512 674 1516 0 +-1518 -1517 0 +-1518 -499 0 +-1518 -1512 0 +-1518 -674 0 +-1518 -1516 0 +1511 1513 1514 1515 1518 0 +-675 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1074 1075 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 0 +1519 -1489 1520 1072 0 +-1519 1489 0 +-1519 -1520 0 +-1519 -1072 0 +1521 -1489 -1520 -1072 0 +-1521 1489 0 +-1521 1520 0 +-1521 1072 0 +1522 1489 -1520 1072 0 +-1522 -1489 0 +-1522 1520 0 +-1522 -1072 0 +1523 1489 1520 -1072 -1524 0 +-1523 -1525 0 +-1523 -1489 0 +-1523 -1520 0 +-1523 1072 0 +-1523 1524 0 +1526 1489 1520 1072 1524 0 +-1526 -1525 0 +-1526 -1489 0 +-1526 -1520 0 +-1526 -1072 0 +-1526 -1524 0 +1519 1521 1522 1523 1526 0 +-1074 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1099 1107 1115 1120 1121 1124 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 0 +1527 -76 1110 1123 0 +-1527 76 0 +-1527 -1110 0 +-1527 -1123 0 +1528 -76 -1110 -1123 0 +-1528 76 0 +-1528 1110 0 +-1528 1123 0 +1529 76 -1110 1123 0 +-1529 -76 0 +-1529 1110 0 +-1529 -1123 0 +1530 76 1110 -1123 -1531 0 +-1530 -1532 0 +-1530 -76 0 +-1530 -1110 0 +-1530 1123 0 +-1530 1531 0 +1533 76 1110 1123 1531 0 +-1533 -1532 0 +-1533 -76 0 +-1533 -1110 0 +-1533 -1123 0 +-1533 -1531 0 +1527 1528 1529 1530 1533 0 +-1124 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1099 1107 1115 1120 1121 1130 1137 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1532 0 +1534 -800 1535 1098 0 +-1534 800 0 +-1534 -1535 0 +-1534 -1098 0 +1536 -800 -1535 -1098 0 +-1536 800 0 +-1536 1535 0 +-1536 1098 0 +1537 800 -1535 1098 0 +-1537 -800 0 +-1537 1535 0 +-1537 -1098 0 +1538 800 1535 -1098 -1539 0 +-1538 -1540 0 +-1538 -800 0 +-1538 -1535 0 +-1538 1098 0 +-1538 1539 0 +1541 800 1535 1098 1539 0 +-1541 -1540 0 +-1541 -800 0 +-1541 -1535 0 +-1541 -1098 0 +-1541 -1539 0 +1534 1536 1537 1538 1541 0 +-1099 0 +a 0 +1542 -1535 1543 1106 0 +-1542 1535 0 +-1542 -1543 0 +-1542 -1106 0 +1544 -1535 -1543 -1106 0 +-1544 1535 0 +-1544 1543 0 +-1544 1106 0 +1545 1535 -1543 1106 0 +-1545 -1535 0 +-1545 1543 0 +-1545 -1106 0 +1546 1535 1543 -1106 -1547 0 +-1546 -1548 0 +-1546 -1535 0 +-1546 -1543 0 +-1546 1106 0 +-1546 1547 0 +1549 1535 1543 1106 1547 0 +-1549 -1548 0 +-1549 -1535 0 +-1549 -1543 0 +-1549 -1106 0 +-1549 -1547 0 +1542 1544 1545 1546 1549 0 +-1107 0 +1550 -1543 1551 1114 0 +-1550 1543 0 +-1550 -1551 0 +-1550 -1114 0 +1552 -1543 -1551 -1114 0 +-1552 1543 0 +-1552 1551 0 +-1552 1114 0 +1553 1543 -1551 1114 0 +-1553 -1543 0 +-1553 1551 0 +-1553 -1114 0 +1554 1543 1551 -1114 -1555 0 +-1554 -1556 0 +-1554 -1543 0 +-1554 -1551 0 +-1554 1114 0 +-1554 1555 0 +1557 1543 1551 1114 1555 0 +-1557 -1556 0 +-1557 -1543 0 +-1557 -1551 0 +-1557 -1114 0 +-1557 -1555 0 +1550 1552 1553 1554 1557 0 +-1115 0 +1558 -1551 1559 1118 0 +-1558 1551 0 +-1558 -1559 0 +-1558 -1118 0 +1560 -1551 -1559 -1118 0 +-1560 1551 0 +-1560 1559 0 +-1560 1118 0 +1561 1551 -1559 1118 0 +-1561 -1551 0 +-1561 1559 0 +-1561 -1118 0 +1562 1551 1559 -1118 -1563 0 +-1562 -1564 0 +-1562 -1551 0 +-1562 -1559 0 +-1562 1118 0 +-1562 1563 0 +1565 1551 1559 1118 1563 0 +-1565 -1564 0 +-1565 -1551 0 +-1565 -1559 0 +-1565 -1118 0 +-1565 -1563 0 +1558 1560 1561 1562 1565 0 +-1120 0 +1566 -1567 1568 1559 0 +-1566 -48 0 +-1566 -49 0 +-1566 -1569 0 +-1566 -1570 0 +-1566 -42 0 +-1566 -43 0 +-1566 -44 0 +-1566 -45 0 +-1566 -46 0 +-1566 -47 0 +-1566 1567 0 +-1566 -1568 0 +-1566 -1559 0 +1571 -1572 -1568 0 +-1571 -48 0 +-1571 -49 0 +-1571 -42 0 +-1571 -43 0 +-1571 -44 0 +-1571 -45 0 +-1571 -46 0 +-1571 -47 0 +-1571 -1573 0 +-1571 1572 0 +-1571 1568 0 +1566 1571 0 +-1121 0 +1574 -575 1535 1129 0 +-1574 575 0 +-1574 -1535 0 +-1574 -1129 0 +1575 -575 -1535 -1129 0 +-1575 575 0 +-1575 1535 0 +-1575 1129 0 +1576 575 -1535 1129 0 +-1576 -575 0 +-1576 1535 0 +-1576 -1129 0 +1577 575 1535 -1129 -1578 0 +-1577 -1579 0 +-1577 -575 0 +-1577 -1535 0 +-1577 1129 0 +-1577 1578 0 +1580 575 1535 1129 1578 0 +-1580 -1579 0 +-1580 -575 0 +-1580 -1535 0 +-1580 -1129 0 +-1580 -1578 0 +1574 1575 1576 1577 1580 0 +-1130 0 +1581 -427 1543 1136 0 +-1581 427 0 +-1581 -1543 0 +-1581 -1136 0 +1582 -427 -1543 -1136 0 +-1582 427 0 +-1582 1543 0 +-1582 1136 0 +1583 427 -1543 1136 0 +-1583 -427 0 +-1583 1543 0 +-1583 -1136 0 +1584 427 1543 -1136 -1585 0 +-1584 -1586 0 +-1584 -427 0 +-1584 -1543 0 +-1584 1136 0 +-1584 1585 0 +1587 427 1543 1136 1585 0 +-1587 -1586 0 +-1587 -427 0 +-1587 -1543 0 +-1587 -1136 0 +-1587 -1585 0 +1581 1582 1583 1584 1587 0 +-1137 0 +1588 -219 1551 1531 0 +-1588 219 0 +-1588 -1551 0 +-1588 -1531 0 +1589 -219 -1551 -1531 0 +-1589 219 0 +-1589 1551 0 +-1589 1531 0 +1590 219 -1551 1531 0 +-1590 -219 0 +-1590 1551 0 +-1590 -1531 0 +1591 219 1551 -1531 -1592 0 +-1591 -1593 0 +-1591 -219 0 +-1591 -1551 0 +-1591 1531 0 +-1591 1592 0 +1594 219 1551 1531 1592 0 +-1594 -1593 0 +-1594 -219 0 +-1594 -1551 0 +-1594 -1531 0 +-1594 -1592 0 +1588 1589 1590 1591 1594 0 +-1532 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1144 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 0 +1595 -228 1147 1143 0 +-1595 228 0 +-1595 -1147 0 +-1595 -1143 0 +1596 -228 -1147 -1143 0 +-1596 228 0 +-1596 1147 0 +-1596 1143 0 +1597 228 -1147 1143 0 +-1597 -228 0 +-1597 1147 0 +-1597 -1143 0 +1598 228 1147 -1143 -1599 0 +-1598 -1600 0 +-1598 -228 0 +-1598 -1147 0 +-1598 1143 0 +-1598 1599 0 +1601 228 1147 1143 1599 0 +-1601 -1600 0 +-1601 -228 0 +-1601 -1147 0 +-1601 -1143 0 +-1601 -1599 0 +1595 1596 1597 1598 1601 0 +-1144 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1152 1160 1165 1166 1169 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 0 +1602 -85 1155 1168 0 +-1602 85 0 +-1602 -1155 0 +-1602 -1168 0 +1603 -85 -1155 -1168 0 +-1603 85 0 +-1603 1155 0 +-1603 1168 0 +1604 85 -1155 1168 0 +-1604 -85 0 +-1604 1155 0 +-1604 -1168 0 +1605 85 1155 -1168 -1606 0 +-1605 -1607 0 +-1605 -85 0 +-1605 -1155 0 +-1605 1168 0 +-1605 1606 0 +1608 85 1155 1168 1606 0 +-1608 -1607 0 +-1608 -85 0 +-1608 -1155 0 +-1608 -1168 0 +-1608 -1606 0 +1602 1603 1604 1605 1608 0 +-1169 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1152 1160 1165 1166 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 0 +1609 -575 1610 1151 0 +-1609 575 0 +-1609 -1610 0 +-1609 -1151 0 +1611 -575 -1610 -1151 0 +-1611 575 0 +-1611 1610 0 +-1611 1151 0 +1612 575 -1610 1151 0 +-1612 -575 0 +-1612 1610 0 +-1612 -1151 0 +1613 575 1610 -1151 -1614 0 +-1613 -1615 0 +-1613 -575 0 +-1613 -1610 0 +-1613 1151 0 +-1613 1614 0 +1616 575 1610 1151 1614 0 +-1616 -1615 0 +-1616 -575 0 +-1616 -1610 0 +-1616 -1151 0 +-1616 -1614 0 +1609 1611 1612 1613 1616 0 +-1152 0 +a 0 +1617 -1610 1618 1159 0 +-1617 1610 0 +-1617 -1618 0 +-1617 -1159 0 +1619 -1610 -1618 -1159 0 +-1619 1610 0 +-1619 1618 0 +-1619 1159 0 +1620 1610 -1618 1159 0 +-1620 -1610 0 +-1620 1618 0 +-1620 -1159 0 +1621 1610 1618 -1159 -1622 0 +-1621 -1623 0 +-1621 -1610 0 +-1621 -1618 0 +-1621 1159 0 +-1621 1622 0 +1624 1610 1618 1159 1622 0 +-1624 -1623 0 +-1624 -1610 0 +-1624 -1618 0 +-1624 -1159 0 +-1624 -1622 0 +1617 1619 1620 1621 1624 0 +-1160 0 +1625 -1618 1626 1163 0 +-1625 1618 0 +-1625 -1626 0 +-1625 -1163 0 +1627 -1618 -1626 -1163 0 +-1627 1618 0 +-1627 1626 0 +-1627 1163 0 +1628 1618 -1626 1163 0 +-1628 -1618 0 +-1628 1626 0 +-1628 -1163 0 +1629 1618 1626 -1163 -1630 0 +-1629 -1631 0 +-1629 -1618 0 +-1629 -1626 0 +-1629 1163 0 +-1629 1630 0 +1632 1618 1626 1163 1630 0 +-1632 -1631 0 +-1632 -1618 0 +-1632 -1626 0 +-1632 -1163 0 +-1632 -1630 0 +1625 1627 1628 1629 1632 0 +-1165 0 +1633 -1634 1635 1626 0 +-1633 -48 0 +-1633 -49 0 +-1633 -43 0 +-1633 -44 0 +-1633 -45 0 +-1633 -46 0 +-1633 -47 0 +-1633 -1636 0 +-1633 -1637 0 +-1633 1634 0 +-1633 -1635 0 +-1633 -1626 0 +1638 -1639 -1635 0 +-1638 -48 0 +-1638 -49 0 +-1638 -43 0 +-1638 -44 0 +-1638 -45 0 +-1638 -46 0 +-1638 -47 0 +-1638 -1640 0 +-1638 1639 0 +-1638 1635 0 +1633 1638 0 +-1166 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1182 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 0 +1641 -17 1466 1181 0 +-1641 17 0 +-1641 -1466 0 +-1641 -1181 0 +1642 -17 -1466 -1181 0 +-1642 17 0 +-1642 1466 0 +-1642 1181 0 +1643 17 -1466 1181 0 +-1643 -17 0 +-1643 1466 0 +-1643 -1181 0 +1644 17 1466 -1181 -1645 0 +-1644 -1646 0 +-1644 -17 0 +-1644 -1466 0 +-1644 1181 0 +-1644 1645 0 +1647 17 1466 1181 1645 0 +-1647 -1646 0 +-1647 -17 0 +-1647 -1466 0 +-1647 -1181 0 +-1647 -1645 0 +1641 1642 1643 1644 1647 0 +-1182 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1190 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 0 +1648 -1211 1649 1189 0 +-1648 1211 0 +-1648 -1649 0 +-1648 -1189 0 +1650 -1211 -1649 -1189 0 +-1650 1211 0 +-1650 1649 0 +-1650 1189 0 +1651 1211 -1649 1189 0 +-1651 -1211 0 +-1651 1649 0 +-1651 -1189 0 +1652 1211 1649 -1189 -1653 0 +-1652 -1654 0 +-1652 -1211 0 +-1652 -1649 0 +-1652 1189 0 +-1652 1653 0 +1655 1211 1649 1189 1653 0 +-1655 -1654 0 +-1655 -1211 0 +-1655 -1649 0 +-1655 -1189 0 +-1655 -1653 0 +1648 1650 1651 1652 1655 0 +-1190 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1198 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 0 +1656 -1649 1657 1197 0 +-1656 1649 0 +-1656 -1657 0 +-1656 -1197 0 +1658 -1649 -1657 -1197 0 +-1658 1649 0 +-1658 1657 0 +-1658 1197 0 +1659 1649 -1657 1197 0 +-1659 -1649 0 +-1659 1657 0 +-1659 -1197 0 +1660 1649 1657 -1197 -1661 0 +-1660 -1662 0 +-1660 -1649 0 +-1660 -1657 0 +-1660 1197 0 +-1660 1661 0 +1663 1649 1657 1197 1661 0 +-1663 -1662 0 +-1663 -1649 0 +-1663 -1657 0 +-1663 -1197 0 +-1663 -1661 0 +1656 1658 1659 1660 1663 0 +-1198 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1216 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 0 +1664 -1665 1666 1215 0 +-1664 1665 0 +-1664 -1666 0 +-1664 -1215 0 +1667 -1665 -1666 -1215 0 +-1667 1665 0 +-1667 1666 0 +-1667 1215 0 +1668 1665 -1666 1215 0 +-1668 -1665 0 +-1668 1666 0 +-1668 -1215 0 +1669 1665 1666 -1215 -1670 0 +-1669 -1671 0 +-1669 -1665 0 +-1669 -1666 0 +-1669 1215 0 +-1669 1670 0 +1672 1665 1666 1215 1670 0 +-1672 -1671 0 +-1672 -1665 0 +-1672 -1666 0 +-1672 -1215 0 +-1672 -1670 0 +1664 1667 1668 1669 1672 0 +-1216 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1223 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 0 +1673 -800 1333 1222 0 +-1673 800 0 +-1673 -1333 0 +-1673 -1222 0 +1674 -800 -1333 -1222 0 +-1674 800 0 +-1674 1333 0 +-1674 1222 0 +1675 800 -1333 1222 0 +-1675 -800 0 +-1675 1333 0 +-1675 -1222 0 +1676 800 1333 -1222 -1677 0 +-1676 -1678 0 +-1676 -800 0 +-1676 -1333 0 +-1676 1222 0 +-1676 1677 0 +1679 800 1333 1222 1677 0 +-1679 -1678 0 +-1679 -800 0 +-1679 -1333 0 +-1679 -1222 0 +-1679 -1677 0 +1673 1674 1675 1676 1679 0 +-1223 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1247 1261 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 0 +1680 -895 1265 1260 0 +-1680 895 0 +-1680 -1265 0 +-1680 -1260 0 +1681 -895 -1265 -1260 0 +-1681 895 0 +-1681 1265 0 +-1681 1260 0 +1682 895 -1265 1260 0 +-1682 -895 0 +-1682 1265 0 +-1682 -1260 0 +1683 895 1265 -1260 -1684 0 +-1683 -1685 0 +-1683 -895 0 +-1683 -1265 0 +-1683 1260 0 +-1683 1684 0 +1686 895 1265 1260 1684 0 +-1686 -1685 0 +-1686 -895 0 +-1686 -1265 0 +-1686 -1260 0 +-1686 -1684 0 +1680 1681 1682 1683 1686 0 +-1261 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1302 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 0 +1687 -110 1289 1301 0 +-1687 110 0 +-1687 -1289 0 +-1687 -1301 0 +1688 -110 -1289 -1301 0 +-1688 110 0 +-1688 1289 0 +-1688 1301 0 +1689 110 -1289 1301 0 +-1689 -110 0 +-1689 1289 0 +-1689 -1301 0 +1690 110 1289 -1301 -1691 0 +-1690 -1692 0 +-1690 -110 0 +-1690 -1289 0 +-1690 1301 0 +-1690 1691 0 +1693 110 1289 1301 1691 0 +-1693 -1692 0 +-1693 -110 0 +-1693 -1289 0 +-1693 -1301 0 +-1693 -1691 0 +1687 1688 1689 1690 1693 0 +-1302 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1316 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 0 +1694 -655 1265 1315 0 +-1694 655 0 +-1694 -1265 0 +-1694 -1315 0 +1695 -655 -1265 -1315 0 +-1695 655 0 +-1695 1265 0 +-1695 1315 0 +1696 655 -1265 1315 0 +-1696 -655 0 +-1696 1265 0 +-1696 -1315 0 +1697 655 1265 -1315 -1698 0 +-1697 -1699 0 +-1697 -655 0 +-1697 -1265 0 +-1697 1315 0 +-1697 1698 0 +1700 655 1265 1315 1698 0 +-1700 -1699 0 +-1700 -655 0 +-1700 -1265 0 +-1700 -1315 0 +-1700 -1698 0 +1694 1695 1696 1697 1700 0 +-1316 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1323 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 0 +1701 -435 1349 1322 0 +-1701 435 0 +-1701 -1349 0 +-1701 -1322 0 +1702 -435 -1349 -1322 0 +-1702 435 0 +-1702 1349 0 +-1702 1322 0 +1703 435 -1349 1322 0 +-1703 -435 0 +-1703 1349 0 +-1703 -1322 0 +1704 435 1349 -1322 -1705 0 +-1704 -1706 0 +-1704 -435 0 +-1704 -1349 0 +-1704 1322 0 +-1704 1705 0 +1707 435 1349 1322 1705 0 +-1707 -1706 0 +-1707 -435 0 +-1707 -1349 0 +-1707 -1322 0 +-1707 -1705 0 +1701 1702 1703 1704 1707 0 +-1323 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 766 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1330 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 0 +1708 -885 1226 765 0 +-1708 885 0 +-1708 -1226 0 +-1708 -765 0 +1709 -885 -1226 -765 0 +-1709 885 0 +-1709 1226 0 +-1709 765 0 +1710 885 -1226 765 0 +-1710 -885 0 +-1710 1226 0 +-1710 -765 0 +1711 885 1226 -765 -1712 0 +-1711 -1713 0 +-1711 -885 0 +-1711 -1226 0 +-1711 765 0 +-1711 1712 0 +1714 885 1226 765 1712 0 +-1714 -1713 0 +-1714 -885 0 +-1714 -1226 0 +-1714 -765 0 +-1714 -1712 0 +1708 1709 1710 1711 1714 0 +-766 0 +a 0 +1715 -210 1357 1329 0 +-1715 210 0 +-1715 -1357 0 +-1715 -1329 0 +1716 -210 -1357 -1329 0 +-1716 210 0 +-1716 1357 0 +-1716 1329 0 +1717 210 -1357 1329 0 +-1717 -210 0 +-1717 1357 0 +-1717 -1329 0 +1718 210 1357 -1329 -1719 0 +-1718 -1720 0 +-1718 -210 0 +-1718 -1357 0 +-1718 1329 0 +-1718 1719 0 +1721 210 1357 1329 1719 0 +-1721 -1720 0 +-1721 -210 0 +-1721 -1357 0 +-1721 -1329 0 +-1721 -1719 0 +1715 1716 1717 1718 1721 0 +-1330 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1338 1346 1354 1362 1370 1375 1376 1379 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 0 +1722 -67 1365 1378 0 +-1722 67 0 +-1722 -1365 0 +-1722 -1378 0 +1723 -67 -1365 -1378 0 +-1723 67 0 +-1723 1365 0 +-1723 1378 0 +1724 67 -1365 1378 0 +-1724 -67 0 +-1724 1365 0 +-1724 -1378 0 +1725 67 1365 -1378 -1726 0 +-1725 -1727 0 +-1725 -67 0 +-1725 -1365 0 +-1725 1378 0 +-1725 1726 0 +1728 67 1365 1378 1726 0 +-1728 -1727 0 +-1728 -67 0 +-1728 -1365 0 +-1728 -1378 0 +-1728 -1726 0 +1722 1723 1724 1725 1728 0 +-1379 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1338 1346 1354 1362 1370 1375 1376 1387 1395 1403 1411 1415 1416 1419 1425 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 0 +1729 -17 1730 1337 0 +-1729 17 0 +-1729 -1730 0 +-1729 -1337 0 +1731 -17 -1730 -1337 0 +-1731 17 0 +-1731 1730 0 +-1731 1337 0 +1732 17 -1730 1337 0 +-1732 -17 0 +-1732 1730 0 +-1732 -1337 0 +1733 17 1730 -1337 -1734 0 +-1733 -1735 0 +-1733 -17 0 +-1733 -1730 0 +-1733 1337 0 +-1733 1734 0 +1736 17 1730 1337 1734 0 +-1736 -1735 0 +-1736 -17 0 +-1736 -1730 0 +-1736 -1337 0 +-1736 -1734 0 +1729 1731 1732 1733 1736 0 +-1338 0 +a 0 +1737 -1730 1738 1345 0 +-1737 1730 0 +-1737 -1738 0 +-1737 -1345 0 +1739 -1730 -1738 -1345 0 +-1739 1730 0 +-1739 1738 0 +-1739 1345 0 +1740 1730 -1738 1345 0 +-1740 -1730 0 +-1740 1738 0 +-1740 -1345 0 +1741 1730 1738 -1345 -1742 0 +-1741 -1743 0 +-1741 -1730 0 +-1741 -1738 0 +-1741 1345 0 +-1741 1742 0 +1744 1730 1738 1345 1742 0 +-1744 -1743 0 +-1744 -1730 0 +-1744 -1738 0 +-1744 -1345 0 +-1744 -1742 0 +1737 1739 1740 1741 1744 0 +-1346 0 +1745 -1738 1746 1353 0 +-1745 1738 0 +-1745 -1746 0 +-1745 -1353 0 +1747 -1738 -1746 -1353 0 +-1747 1738 0 +-1747 1746 0 +-1747 1353 0 +1748 1738 -1746 1353 0 +-1748 -1738 0 +-1748 1746 0 +-1748 -1353 0 +1749 1738 1746 -1353 -1750 0 +-1749 -1751 0 +-1749 -1738 0 +-1749 -1746 0 +-1749 1353 0 +-1749 1750 0 +1752 1738 1746 1353 1750 0 +-1752 -1751 0 +-1752 -1738 0 +-1752 -1746 0 +-1752 -1353 0 +-1752 -1750 0 +1745 1747 1748 1749 1752 0 +-1354 0 +1753 -1746 1754 1361 0 +-1753 1746 0 +-1753 -1754 0 +-1753 -1361 0 +1755 -1746 -1754 -1361 0 +-1755 1746 0 +-1755 1754 0 +-1755 1361 0 +1756 1746 -1754 1361 0 +-1756 -1746 0 +-1756 1754 0 +-1756 -1361 0 +1757 1746 1754 -1361 -1758 0 +-1757 -1759 0 +-1757 -1746 0 +-1757 -1754 0 +-1757 1361 0 +-1757 1758 0 +1760 1746 1754 1361 1758 0 +-1760 -1759 0 +-1760 -1746 0 +-1760 -1754 0 +-1760 -1361 0 +-1760 -1758 0 +1753 1755 1756 1757 1760 0 +-1362 0 +1761 -1754 1762 1369 0 +-1761 1754 0 +-1761 -1762 0 +-1761 -1369 0 +1763 -1754 -1762 -1369 0 +-1763 1754 0 +-1763 1762 0 +-1763 1369 0 +1764 1754 -1762 1369 0 +-1764 -1754 0 +-1764 1762 0 +-1764 -1369 0 +1765 1754 1762 -1369 -1766 0 +-1765 -1767 0 +-1765 -1754 0 +-1765 -1762 0 +-1765 1369 0 +-1765 1766 0 +1768 1754 1762 1369 1766 0 +-1768 -1767 0 +-1768 -1754 0 +-1768 -1762 0 +-1768 -1369 0 +-1768 -1766 0 +1761 1763 1764 1765 1768 0 +-1370 0 +1769 -1762 1770 1373 0 +-1769 1762 0 +-1769 -1770 0 +-1769 -1373 0 +1771 -1762 -1770 -1373 0 +-1771 1762 0 +-1771 1770 0 +-1771 1373 0 +1772 1762 -1770 1373 0 +-1772 -1762 0 +-1772 1770 0 +-1772 -1373 0 +1773 1762 1770 -1373 -1774 0 +-1773 -1775 0 +-1773 -1762 0 +-1773 -1770 0 +-1773 1373 0 +-1773 1774 0 +1776 1762 1770 1373 1774 0 +-1776 -1775 0 +-1776 -1762 0 +-1776 -1770 0 +-1776 -1373 0 +-1776 -1774 0 +1769 1771 1772 1773 1776 0 +-1375 0 +1777 -1778 1779 1770 0 +-1777 -48 0 +-1777 -49 0 +-1777 -1780 0 +-1777 -1781 0 +-1777 -41 0 +-1777 -42 0 +-1777 -43 0 +-1777 -44 0 +-1777 -45 0 +-1777 -46 0 +-1777 -47 0 +-1777 1778 0 +-1777 -1779 0 +-1777 -1770 0 +1782 -1783 -1779 0 +-1782 -48 0 +-1782 -49 0 +-1782 -41 0 +-1782 -42 0 +-1782 -43 0 +-1782 -44 0 +-1782 -45 0 +-1782 -46 0 +-1782 -47 0 +-1782 -1784 0 +-1782 1783 0 +-1782 1779 0 +1777 1782 0 +-1376 0 +1785 -1786 1787 1386 0 +-1785 1786 0 +-1785 -1787 0 +-1785 -1386 0 +1788 -1786 -1787 -1386 0 +-1788 1786 0 +-1788 1787 0 +-1788 1386 0 +1789 1786 -1787 1386 0 +-1789 -1786 0 +-1789 1787 0 +-1789 -1386 0 +1790 1786 1787 -1386 -1791 0 +-1790 -1792 0 +-1790 -1786 0 +-1790 -1787 0 +-1790 1386 0 +-1790 1791 0 +1793 1786 1787 1386 1791 0 +-1793 -1792 0 +-1793 -1786 0 +-1793 -1787 0 +-1793 -1386 0 +-1793 -1791 0 +1785 1788 1789 1790 1793 0 +-1387 0 +1794 -1787 1795 1394 0 +-1794 1787 0 +-1794 -1795 0 +-1794 -1394 0 +1796 -1787 -1795 -1394 0 +-1796 1787 0 +-1796 1795 0 +-1796 1394 0 +1797 1787 -1795 1394 0 +-1797 -1787 0 +-1797 1795 0 +-1797 -1394 0 +1798 1787 1795 -1394 -1799 0 +-1798 -1800 0 +-1798 -1787 0 +-1798 -1795 0 +-1798 1394 0 +-1798 1799 0 +1801 1787 1795 1394 1799 0 +-1801 -1800 0 +-1801 -1787 0 +-1801 -1795 0 +-1801 -1394 0 +-1801 -1799 0 +1794 1796 1797 1798 1801 0 +-1395 0 +1802 -1795 1803 1402 0 +-1802 1795 0 +-1802 -1803 0 +-1802 -1402 0 +1804 -1795 -1803 -1402 0 +-1804 1795 0 +-1804 1803 0 +-1804 1402 0 +1805 1795 -1803 1402 0 +-1805 -1795 0 +-1805 1803 0 +-1805 -1402 0 +1806 1795 1803 -1402 -1807 0 +-1806 -1808 0 +-1806 -1795 0 +-1806 -1803 0 +-1806 1402 0 +-1806 1807 0 +1809 1795 1803 1402 1807 0 +-1809 -1808 0 +-1809 -1795 0 +-1809 -1803 0 +-1809 -1402 0 +-1809 -1807 0 +1802 1804 1805 1806 1809 0 +-1403 0 +1810 -1803 1811 1410 0 +-1810 1803 0 +-1810 -1811 0 +-1810 -1410 0 +1812 -1803 -1811 -1410 0 +-1812 1803 0 +-1812 1811 0 +-1812 1410 0 +1813 1803 -1811 1410 0 +-1813 -1803 0 +-1813 1811 0 +-1813 -1410 0 +1814 1803 1811 -1410 -1815 0 +-1814 -1816 0 +-1814 -1803 0 +-1814 -1811 0 +-1814 1410 0 +-1814 1815 0 +1817 1803 1811 1410 1815 0 +-1817 -1816 0 +-1817 -1803 0 +-1817 -1811 0 +-1817 -1410 0 +-1817 -1815 0 +1810 1812 1813 1814 1817 0 +-1411 0 +1818 -1811 1819 1414 0 +-1818 1811 0 +-1818 -1819 0 +-1818 -1414 0 +1820 -1811 -1819 -1414 0 +-1820 1811 0 +-1820 1819 0 +-1820 1414 0 +1821 1811 -1819 1414 0 +-1821 -1811 0 +-1821 1819 0 +-1821 -1414 0 +1822 1811 1819 -1414 -1823 0 +-1822 -1824 0 +-1822 -1811 0 +-1822 -1819 0 +-1822 1414 0 +-1822 1823 0 +1825 1811 1819 1414 1823 0 +-1825 -1824 0 +-1825 -1811 0 +-1825 -1819 0 +-1825 -1414 0 +-1825 -1823 0 +1818 1820 1821 1822 1825 0 +-1415 0 +1826 -1827 1779 1819 0 +-1826 -1828 0 +-1826 -1829 0 +-1826 1827 0 +-1826 -1779 0 +-1826 -1819 0 +1830 -1831 -1779 0 +-1830 -1832 0 +-1830 1831 0 +-1830 1779 0 +1826 1830 0 +-1416 0 +1833 -800 1738 1424 0 +-1833 800 0 +-1833 -1738 0 +-1833 -1424 0 +1834 -800 -1738 -1424 0 +-1834 800 0 +-1834 1738 0 +-1834 1424 0 +1835 800 -1738 1424 0 +-1835 -800 0 +-1835 1738 0 +-1835 -1424 0 +1836 800 1738 -1424 -1837 0 +-1836 -1838 0 +-1836 -800 0 +-1836 -1738 0 +-1836 1424 0 +-1836 1837 0 +1839 800 1738 1424 1837 0 +-1839 -1838 0 +-1839 -800 0 +-1839 -1738 0 +-1839 -1424 0 +-1839 -1837 0 +1833 1834 1835 1836 1839 0 +-1425 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1075 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1419 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 0 +1840 -1841 1842 1520 0 +-1840 -48 0 +-1840 -49 0 +-1840 -45 0 +-1840 -46 0 +-1840 -47 0 +-1840 -1843 0 +-1840 -1844 0 +-1840 1841 0 +-1840 -1842 0 +-1840 -1520 0 +1845 -1846 -1842 0 +-1845 -48 0 +-1845 -49 0 +-1845 -45 0 +-1845 -46 0 +-1845 -47 0 +-1845 -1847 0 +-1845 1846 0 +-1845 1842 0 +1840 1845 0 +-1075 0 +a 0 +a 112 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1419 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 0 +1848 -1849 245 1504 0 +-1848 -48 0 +-1848 -49 0 +-1848 -47 0 +-1848 -1850 0 +-1848 -1851 0 +-1848 1849 0 +-1848 -245 0 +-1848 -1504 0 +1852 -1853 -245 0 +-1852 -48 0 +-1852 -49 0 +-1852 -47 0 +-1852 -1854 0 +-1852 1853 0 +-1852 245 0 +1848 1852 0 +-112 0 +a 0 +1855 -118 1504 1853 0 +-1855 118 0 +-1855 -1504 0 +-1855 -1853 0 +1856 -118 -1504 -1853 0 +-1856 118 0 +-1856 1504 0 +-1856 1853 0 +1857 118 -1504 1853 0 +-1857 -118 0 +-1857 1504 0 +-1857 -1853 0 +1858 118 1504 -1853 -1859 0 +-1858 -1860 0 +-1858 -118 0 +-1858 -1504 0 +-1858 1853 0 +-1858 1859 0 +1861 118 1504 1853 1859 0 +-1861 -1860 0 +-1861 -118 0 +-1861 -1504 0 +-1861 -1853 0 +-1861 -1859 0 +1855 1856 1857 1858 1861 0 +-1854 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1084 1175 1247 1270 1278 1286 1294 1298 1299 1309 1419 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 0 +1862 -276 885 1174 0 +-1862 276 0 +-1862 -885 0 +-1862 -1174 0 +1863 -276 -885 -1174 0 +-1863 276 0 +-1863 885 0 +-1863 1174 0 +1864 276 -885 1174 0 +-1864 -276 0 +-1864 885 0 +-1864 -1174 0 +1865 276 885 -1174 -1866 0 +-1865 -1867 0 +-1865 -276 0 +-1865 -885 0 +-1865 1174 0 +-1865 1866 0 +1868 276 885 1174 1866 0 +-1868 -1867 0 +-1868 -276 0 +-1868 -885 0 +-1868 -1174 0 +-1868 -1866 0 +1862 1863 1864 1865 1868 0 +-1175 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1084 1247 1270 1278 1286 1294 1298 1299 1309 1419 1432 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 0 +1869 -1201 1018 1431 0 +-1869 1201 0 +-1869 -1018 0 +-1869 -1431 0 +1870 -1201 -1018 -1431 0 +-1870 1201 0 +-1870 1018 0 +-1870 1431 0 +1871 1201 -1018 1431 0 +-1871 -1201 0 +-1871 1018 0 +-1871 -1431 0 +1872 1201 1018 -1431 -1873 0 +-1872 -1874 0 +-1872 -1201 0 +-1872 -1018 0 +-1872 1431 0 +-1872 1873 0 +1875 1201 1018 1431 1873 0 +-1875 -1874 0 +-1875 -1201 0 +-1875 -1018 0 +-1875 -1431 0 +-1875 -1873 0 +1869 1870 1871 1872 1875 0 +-1432 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1084 1247 1270 1278 1286 1294 1298 1299 1309 1419 1440 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 0 +1876 -1666 1877 1439 0 +-1876 1666 0 +-1876 -1877 0 +-1876 -1439 0 +1878 -1666 -1877 -1439 0 +-1878 1666 0 +-1878 1877 0 +-1878 1439 0 +1879 1666 -1877 1439 0 +-1879 -1666 0 +-1879 1877 0 +-1879 -1439 0 +1880 1666 1877 -1439 -1881 0 +-1880 -1882 0 +-1880 -1666 0 +-1880 -1877 0 +-1880 1439 0 +-1880 1881 0 +1883 1666 1877 1439 1881 0 +-1883 -1882 0 +-1883 -1666 0 +-1883 -1877 0 +-1883 -1439 0 +-1883 -1881 0 +1876 1878 1879 1880 1883 0 +-1440 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1084 1247 1270 1278 1286 1294 1298 1299 1309 1419 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 0 +1884 -1202 1885 1083 0 +-1884 1202 0 +-1884 -1885 0 +-1884 -1083 0 +1886 -1202 -1885 -1083 0 +-1886 1202 0 +-1886 1885 0 +-1886 1083 0 +1887 1202 -1885 1083 0 +-1887 -1202 0 +-1887 1885 0 +-1887 -1083 0 +1888 1202 1885 -1083 -1889 0 +-1888 -1890 0 +-1888 -1202 0 +-1888 -1885 0 +-1888 1083 0 +-1888 1889 0 +1891 1202 1885 1083 1889 0 +-1891 -1890 0 +-1891 -1202 0 +-1891 -1885 0 +-1891 -1083 0 +-1891 -1889 0 +1884 1886 1887 1888 1891 0 +-1084 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1270 1278 1286 1294 1298 1299 1309 1419 1447 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 0 +1892 -1657 1665 1446 0 +-1892 1657 0 +-1892 -1665 0 +-1892 -1446 0 +1893 -1657 -1665 -1446 0 +-1893 1657 0 +-1893 1665 0 +-1893 1446 0 +1894 1657 -1665 1446 0 +-1894 -1657 0 +-1894 1665 0 +-1894 -1446 0 +1895 1657 1665 -1446 -1896 0 +-1895 -1897 0 +-1895 -1657 0 +-1895 -1665 0 +-1895 1446 0 +-1895 1896 0 +1898 1657 1665 1446 1896 0 +-1898 -1897 0 +-1898 -1657 0 +-1898 -1665 0 +-1898 -1446 0 +-1898 -1896 0 +1892 1893 1894 1895 1898 0 +-1447 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1270 1278 1286 1294 1298 1299 1309 1419 1455 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 0 +1899 -1657 1900 1454 0 +-1899 1657 0 +-1899 -1900 0 +-1899 -1454 0 +1901 -1657 -1900 -1454 0 +-1901 1657 0 +-1901 1900 0 +-1901 1454 0 +1902 1657 -1900 1454 0 +-1902 -1657 0 +-1902 1900 0 +-1902 -1454 0 +1903 1657 1900 -1454 -1904 0 +-1903 -1905 0 +-1903 -1657 0 +-1903 -1900 0 +-1903 1454 0 +-1903 1904 0 +1906 1657 1900 1454 1904 0 +-1906 -1905 0 +-1906 -1657 0 +-1906 -1900 0 +-1906 -1454 0 +-1906 -1904 0 +1899 1901 1902 1903 1906 0 +-1455 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1270 1278 1286 1294 1298 1299 1309 1419 1463 1471 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 0 +1907 -1900 1908 1470 0 +-1907 1900 0 +-1907 -1908 0 +-1907 -1470 0 +1909 -1900 -1908 -1470 0 +-1909 1900 0 +-1909 1908 0 +-1909 1470 0 +1910 1900 -1908 1470 0 +-1910 -1900 0 +-1910 1908 0 +-1910 -1470 0 +1911 1900 1908 -1470 -1912 0 +-1911 -1913 0 +-1911 -1900 0 +-1911 -1908 0 +-1911 1470 0 +-1911 1912 0 +1914 1900 1908 1470 1912 0 +-1914 -1913 0 +-1914 -1900 0 +-1914 -1908 0 +-1914 -1470 0 +-1914 -1912 0 +1907 1909 1910 1911 1914 0 +-1471 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1270 1278 1286 1294 1298 1299 1309 1419 1463 1478 1486 1494 1501 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 0 +1915 -522 1481 1477 0 +-1915 522 0 +-1915 -1481 0 +-1915 -1477 0 +1916 -522 -1481 -1477 0 +-1916 522 0 +-1916 1481 0 +-1916 1477 0 +1917 522 -1481 1477 0 +-1917 -522 0 +-1917 1481 0 +-1917 -1477 0 +1918 522 1481 -1477 -1919 0 +-1918 -1920 0 +-1918 -522 0 +-1918 -1481 0 +-1918 1477 0 +-1918 1919 0 +1921 522 1481 1477 1919 0 +-1921 -1920 0 +-1921 -522 0 +-1921 -1481 0 +-1921 -1477 0 +-1921 -1919 0 +1915 1916 1917 1918 1921 0 +-1478 0 +a 0 +1922 -1923 1924 1269 0 +-1922 1923 0 +-1922 -1924 0 +-1922 -1269 0 +1925 -1923 -1924 -1269 0 +-1925 1923 0 +-1925 1924 0 +-1925 1269 0 +1926 1923 -1924 1269 0 +-1926 -1923 0 +-1926 1924 0 +-1926 -1269 0 +1927 1923 1924 -1269 -1928 0 +-1927 -1929 0 +-1927 -1923 0 +-1927 -1924 0 +-1927 1269 0 +-1927 1928 0 +1930 1923 1924 1269 1928 0 +-1930 -1929 0 +-1930 -1923 0 +-1930 -1924 0 +-1930 -1269 0 +-1930 -1928 0 +1922 1925 1926 1927 1930 0 +-1270 0 +1931 -1924 1932 1277 0 +-1931 1924 0 +-1931 -1932 0 +-1931 -1277 0 +1933 -1924 -1932 -1277 0 +-1933 1924 0 +-1933 1932 0 +-1933 1277 0 +1934 1924 -1932 1277 0 +-1934 -1924 0 +-1934 1932 0 +-1934 -1277 0 +1935 1924 1932 -1277 -1936 0 +-1935 -1937 0 +-1935 -1924 0 +-1935 -1932 0 +-1935 1277 0 +-1935 1936 0 +1938 1924 1932 1277 1936 0 +-1938 -1937 0 +-1938 -1924 0 +-1938 -1932 0 +-1938 -1277 0 +-1938 -1936 0 +1931 1933 1934 1935 1938 0 +-1278 0 +1939 -1932 1940 1285 0 +-1939 1932 0 +-1939 -1940 0 +-1939 -1285 0 +1941 -1932 -1940 -1285 0 +-1941 1932 0 +-1941 1940 0 +-1941 1285 0 +1942 1932 -1940 1285 0 +-1942 -1932 0 +-1942 1940 0 +-1942 -1285 0 +1943 1932 1940 -1285 -1944 0 +-1943 -1945 0 +-1943 -1932 0 +-1943 -1940 0 +-1943 1285 0 +-1943 1944 0 +1946 1932 1940 1285 1944 0 +-1946 -1945 0 +-1946 -1932 0 +-1946 -1940 0 +-1946 -1285 0 +-1946 -1944 0 +1939 1941 1942 1943 1946 0 +-1286 0 +1947 -1940 1948 1293 0 +-1947 1940 0 +-1947 -1948 0 +-1947 -1293 0 +1949 -1940 -1948 -1293 0 +-1949 1940 0 +-1949 1948 0 +-1949 1293 0 +1950 1940 -1948 1293 0 +-1950 -1940 0 +-1950 1948 0 +-1950 -1293 0 +1951 1940 1948 -1293 -1952 0 +-1951 -1953 0 +-1951 -1940 0 +-1951 -1948 0 +-1951 1293 0 +-1951 1952 0 +1954 1940 1948 1293 1952 0 +-1954 -1953 0 +-1954 -1940 0 +-1954 -1948 0 +-1954 -1293 0 +-1954 -1952 0 +1947 1949 1950 1951 1954 0 +-1294 0 +1955 -1948 1956 1297 0 +-1955 1948 0 +-1955 -1956 0 +-1955 -1297 0 +1957 -1948 -1956 -1297 0 +-1957 1948 0 +-1957 1956 0 +-1957 1297 0 +1958 1948 -1956 1297 0 +-1958 -1948 0 +-1958 1956 0 +-1958 -1297 0 +1959 1948 1956 -1297 -1960 0 +-1959 -1961 0 +-1959 -1948 0 +-1959 -1956 0 +-1959 1297 0 +-1959 1960 0 +1962 1948 1956 1297 1960 0 +-1962 -1961 0 +-1962 -1948 0 +-1962 -1956 0 +-1962 -1297 0 +-1962 -1960 0 +1955 1957 1958 1959 1962 0 +-1298 0 +1963 -1964 1842 1956 0 +-1963 -48 0 +-1963 -49 0 +-1963 -1965 0 +-1963 -1966 0 +-1963 -46 0 +-1963 -47 0 +-1963 1964 0 +-1963 -1842 0 +-1963 -1956 0 +1967 -1968 -1842 0 +-1967 -48 0 +-1967 -49 0 +-1967 -46 0 +-1967 -47 0 +-1967 -1969 0 +-1967 1968 0 +-1967 1842 0 +1963 1967 0 +-1299 0 +1970 -1842 1971 1308 0 +-1970 1842 0 +-1970 -1971 0 +-1970 -1308 0 +1972 -1842 -1971 -1308 0 +-1972 1842 0 +-1972 1971 0 +-1972 1308 0 +1973 1842 -1971 1308 0 +-1973 -1842 0 +-1973 1971 0 +-1973 -1308 0 +1974 1842 1971 -1308 -1975 0 +-1974 -1976 0 +-1974 -1842 0 +-1974 -1971 0 +-1974 1308 0 +-1974 1975 0 +1977 1842 1971 1308 1975 0 +-1977 -1976 0 +-1977 -1842 0 +-1977 -1971 0 +-1977 -1308 0 +-1977 -1975 0 +1970 1972 1973 1974 1977 0 +-1309 0 +1978 -276 1489 1500 0 +-1978 276 0 +-1978 -1489 0 +-1978 -1500 0 +1979 -276 -1489 -1500 0 +-1979 276 0 +-1979 1489 0 +-1979 1500 0 +1980 276 -1489 1500 0 +-1980 -276 0 +-1980 1489 0 +-1980 -1500 0 +1981 276 1489 -1500 -1982 0 +-1981 -1983 0 +-1981 -276 0 +-1981 -1489 0 +-1981 1500 0 +-1981 1982 0 +1984 276 1489 1500 1982 0 +-1984 -1983 0 +-1984 -276 0 +-1984 -1489 0 +-1984 -1500 0 +-1984 -1982 0 +1978 1979 1980 1981 1984 0 +-1501 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1486 1494 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 0 +1985 -1435 1986 1485 0 +-1985 1435 0 +-1985 -1986 0 +-1985 -1485 0 +1987 -1435 -1986 -1485 0 +-1987 1435 0 +-1987 1986 0 +-1987 1485 0 +1988 1435 -1986 1485 0 +-1988 -1435 0 +-1988 1986 0 +-1988 -1485 0 +1989 1435 1986 -1485 -1990 0 +-1989 -1991 0 +-1989 -1435 0 +-1989 -1986 0 +-1989 1485 0 +-1989 1990 0 +1992 1435 1986 1485 1990 0 +-1992 -1991 0 +-1992 -1435 0 +-1992 -1986 0 +-1992 -1485 0 +-1992 -1990 0 +1985 1987 1988 1989 1992 0 +-1486 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1494 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 0 +1993 -1986 1994 1493 0 +-1993 1986 0 +-1993 -1994 0 +-1993 -1493 0 +1995 -1986 -1994 -1493 0 +-1995 1986 0 +-1995 1994 0 +-1995 1493 0 +1996 1986 -1994 1493 0 +-1996 -1986 0 +-1996 1994 0 +-1996 -1493 0 +1997 1986 1994 -1493 -1998 0 +-1997 -1999 0 +-1997 -1986 0 +-1997 -1994 0 +-1997 1493 0 +-1997 1998 0 +2000 1986 1994 1493 1998 0 +-2000 -1999 0 +-2000 -1986 0 +-2000 -1994 0 +-2000 -1493 0 +-2000 -1998 0 +1993 1995 1996 1997 2000 0 +-1494 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1509 1517 1525 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 0 +2001 -1994 2002 1524 0 +-2001 1994 0 +-2001 -2002 0 +-2001 -1524 0 +2003 -1994 -2002 -1524 0 +-2003 1994 0 +-2003 2002 0 +-2003 1524 0 +2004 1994 -2002 1524 0 +-2004 -1994 0 +-2004 2002 0 +-2004 -1524 0 +2005 1994 2002 -1524 -2006 0 +-2005 -2007 0 +-2005 -1994 0 +-2005 -2002 0 +-2005 1524 0 +-2005 2006 0 +2008 1994 2002 1524 2006 0 +-2008 -2007 0 +-2008 -1994 0 +-2008 -2002 0 +-2008 -1524 0 +-2008 -2006 0 +2001 2003 2004 2005 2008 0 +-1525 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1509 1517 1540 1548 1556 1564 1569 1570 1573 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 0 +2009 -76 1559 1572 0 +-2009 76 0 +-2009 -1559 0 +-2009 -1572 0 +2010 -76 -1559 -1572 0 +-2010 76 0 +-2010 1559 0 +-2010 1572 0 +2011 76 -1559 1572 0 +-2011 -76 0 +-2011 1559 0 +-2011 -1572 0 +2012 76 1559 -1572 -2013 0 +-2012 -2014 0 +-2012 -76 0 +-2012 -1559 0 +-2012 1572 0 +-2012 2013 0 +2015 76 1559 1572 2013 0 +-2015 -2014 0 +-2015 -76 0 +-2015 -1559 0 +-2015 -1572 0 +-2015 -2013 0 +2009 2010 2011 2012 2015 0 +-1573 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1509 1517 1540 1548 1556 1564 1569 1570 1579 1586 1593 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 0 +2016 -1041 2017 1539 0 +-2016 1041 0 +-2016 -2017 0 +-2016 -1539 0 +2018 -1041 -2017 -1539 0 +-2018 1041 0 +-2018 2017 0 +-2018 1539 0 +2019 1041 -2017 1539 0 +-2019 -1041 0 +-2019 2017 0 +-2019 -1539 0 +2020 1041 2017 -1539 -2021 0 +-2020 -2022 0 +-2020 -1041 0 +-2020 -2017 0 +-2020 1539 0 +-2020 2021 0 +2023 1041 2017 1539 2021 0 +-2023 -2022 0 +-2023 -1041 0 +-2023 -2017 0 +-2023 -1539 0 +-2023 -2021 0 +2016 2018 2019 2020 2023 0 +-1540 0 +a 0 +2024 -2017 2025 1547 0 +-2024 2017 0 +-2024 -2025 0 +-2024 -1547 0 +2026 -2017 -2025 -1547 0 +-2026 2017 0 +-2026 2025 0 +-2026 1547 0 +2027 2017 -2025 1547 0 +-2027 -2017 0 +-2027 2025 0 +-2027 -1547 0 +2028 2017 2025 -1547 -2029 0 +-2028 -2030 0 +-2028 -2017 0 +-2028 -2025 0 +-2028 1547 0 +-2028 2029 0 +2031 2017 2025 1547 2029 0 +-2031 -2030 0 +-2031 -2017 0 +-2031 -2025 0 +-2031 -1547 0 +-2031 -2029 0 +2024 2026 2027 2028 2031 0 +-1548 0 +2032 -2025 2033 1555 0 +-2032 2025 0 +-2032 -2033 0 +-2032 -1555 0 +2034 -2025 -2033 -1555 0 +-2034 2025 0 +-2034 2033 0 +-2034 1555 0 +2035 2025 -2033 1555 0 +-2035 -2025 0 +-2035 2033 0 +-2035 -1555 0 +2036 2025 2033 -1555 -2037 0 +-2036 -2038 0 +-2036 -2025 0 +-2036 -2033 0 +-2036 1555 0 +-2036 2037 0 +2039 2025 2033 1555 2037 0 +-2039 -2038 0 +-2039 -2025 0 +-2039 -2033 0 +-2039 -1555 0 +-2039 -2037 0 +2032 2034 2035 2036 2039 0 +-1556 0 +2040 -2033 2041 1563 0 +-2040 2033 0 +-2040 -2041 0 +-2040 -1563 0 +2042 -2033 -2041 -1563 0 +-2042 2033 0 +-2042 2041 0 +-2042 1563 0 +2043 2033 -2041 1563 0 +-2043 -2033 0 +-2043 2041 0 +-2043 -1563 0 +2044 2033 2041 -1563 -2045 0 +-2044 -2046 0 +-2044 -2033 0 +-2044 -2041 0 +-2044 1563 0 +-2044 2045 0 +2047 2033 2041 1563 2045 0 +-2047 -2046 0 +-2047 -2033 0 +-2047 -2041 0 +-2047 -1563 0 +-2047 -2045 0 +2040 2042 2043 2044 2047 0 +-1564 0 +2048 -2041 2049 1567 0 +-2048 2041 0 +-2048 -2049 0 +-2048 -1567 0 +2050 -2041 -2049 -1567 0 +-2050 2041 0 +-2050 2049 0 +-2050 1567 0 +2051 2041 -2049 1567 0 +-2051 -2041 0 +-2051 2049 0 +-2051 -1567 0 +2052 2041 2049 -1567 -2053 0 +-2052 -2054 0 +-2052 -2041 0 +-2052 -2049 0 +-2052 1567 0 +-2052 2053 0 +2055 2041 2049 1567 2053 0 +-2055 -2054 0 +-2055 -2041 0 +-2055 -2049 0 +-2055 -1567 0 +-2055 -2053 0 +2048 2050 2051 2052 2055 0 +-1569 0 +2056 -2057 2058 2049 0 +-2056 -48 0 +-2056 -49 0 +-2056 -46 0 +-2056 -47 0 +-2056 -2059 0 +-2056 -2060 0 +-2056 -42 0 +-2056 -43 0 +-2056 -44 0 +-2056 -45 0 +-2056 2057 0 +-2056 -2058 0 +-2056 -2049 0 +2061 -2062 -2058 0 +-2061 -48 0 +-2061 -49 0 +-2061 -46 0 +-2061 -47 0 +-2061 -42 0 +-2061 -43 0 +-2061 -44 0 +-2061 -45 0 +-2061 -2063 0 +-2061 2062 0 +-2061 2058 0 +2056 2061 0 +-1570 0 +2064 -792 2017 1578 0 +-2064 792 0 +-2064 -2017 0 +-2064 -1578 0 +2065 -792 -2017 -1578 0 +-2065 792 0 +-2065 2017 0 +-2065 1578 0 +2066 792 -2017 1578 0 +-2066 -792 0 +-2066 2017 0 +-2066 -1578 0 +2067 792 2017 -1578 -2068 0 +-2067 -2069 0 +-2067 -792 0 +-2067 -2017 0 +-2067 1578 0 +-2067 2068 0 +2070 792 2017 1578 2068 0 +-2070 -2069 0 +-2070 -792 0 +-2070 -2017 0 +-2070 -1578 0 +-2070 -2068 0 +2064 2065 2066 2067 2070 0 +-1579 0 +2071 -575 2025 1585 0 +-2071 575 0 +-2071 -2025 0 +-2071 -1585 0 +2072 -575 -2025 -1585 0 +-2072 575 0 +-2072 2025 0 +-2072 1585 0 +2073 575 -2025 1585 0 +-2073 -575 0 +-2073 2025 0 +-2073 -1585 0 +2074 575 2025 -1585 -2075 0 +-2074 -2076 0 +-2074 -575 0 +-2074 -2025 0 +-2074 1585 0 +-2074 2075 0 +2077 575 2025 1585 2075 0 +-2077 -2076 0 +-2077 -575 0 +-2077 -2025 0 +-2077 -1585 0 +-2077 -2075 0 +2071 2072 2073 2074 2077 0 +-1586 0 +2078 -427 2033 1592 0 +-2078 427 0 +-2078 -2033 0 +-2078 -1592 0 +2079 -427 -2033 -1592 0 +-2079 427 0 +-2079 2033 0 +-2079 1592 0 +2080 427 -2033 1592 0 +-2080 -427 0 +-2080 2033 0 +-2080 -1592 0 +2081 427 2033 -1592 -2082 0 +-2081 -2083 0 +-2081 -427 0 +-2081 -2033 0 +-2081 1592 0 +-2081 2082 0 +2084 427 2033 1592 2082 0 +-2084 -2083 0 +-2084 -427 0 +-2084 -2033 0 +-2084 -1592 0 +-2084 -2082 0 +2078 2079 2080 2081 2084 0 +-1593 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1509 1517 1600 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 0 +2085 -366 1610 1599 0 +-2085 366 0 +-2085 -1610 0 +-2085 -1599 0 +2086 -366 -1610 -1599 0 +-2086 366 0 +-2086 1610 0 +-2086 1599 0 +2087 366 -1610 1599 0 +-2087 -366 0 +-2087 1610 0 +-2087 -1599 0 +2088 366 1610 -1599 -2089 0 +-2088 -2090 0 +-2088 -366 0 +-2088 -1610 0 +-2088 1599 0 +-2088 2089 0 +2091 366 1610 1599 2089 0 +-2091 -2090 0 +-2091 -366 0 +-2091 -1610 0 +-2091 -1599 0 +-2091 -2089 0 +2085 2086 2087 2088 2091 0 +-1600 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1463 1509 1517 1607 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 0 +2092 -718 2093 1462 0 +-2092 718 0 +-2092 -2093 0 +-2092 -1462 0 +2094 -718 -2093 -1462 0 +-2094 718 0 +-2094 2093 0 +-2094 1462 0 +2095 718 -2093 1462 0 +-2095 -718 0 +-2095 2093 0 +-2095 -1462 0 +2096 718 2093 -1462 -2097 0 +-2096 -2098 0 +-2096 -718 0 +-2096 -2093 0 +-2096 1462 0 +-2096 2097 0 +2099 718 2093 1462 2097 0 +-2099 -2098 0 +-2099 -718 0 +-2099 -2093 0 +-2099 -1462 0 +-2099 -2097 0 +2092 2094 2095 2096 2099 0 +-1463 0 +a 0 +2100 -228 1618 1606 0 +-2100 228 0 +-2100 -1618 0 +-2100 -1606 0 +2101 -228 -1618 -1606 0 +-2101 228 0 +-2101 1618 0 +-2101 1606 0 +2102 228 -1618 1606 0 +-2102 -228 0 +-2102 1618 0 +-2102 -1606 0 +2103 228 1618 -1606 -2104 0 +-2103 -2105 0 +-2103 -228 0 +-2103 -1618 0 +-2103 1606 0 +-2103 2104 0 +2106 228 1618 1606 2104 0 +-2106 -2105 0 +-2106 -228 0 +-2106 -1618 0 +-2106 -1606 0 +-2106 -2104 0 +2100 2101 2102 2103 2106 0 +-1607 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1615 1623 1631 1636 1637 1640 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 0 +2107 -85 1626 1639 0 +-2107 85 0 +-2107 -1626 0 +-2107 -1639 0 +2108 -85 -1626 -1639 0 +-2108 85 0 +-2108 1626 0 +-2108 1639 0 +2109 85 -1626 1639 0 +-2109 -85 0 +-2109 1626 0 +-2109 -1639 0 +2110 85 1626 -1639 -2111 0 +-2110 -2112 0 +-2110 -85 0 +-2110 -1626 0 +-2110 1639 0 +-2110 2111 0 +2113 85 1626 1639 2111 0 +-2113 -2112 0 +-2113 -85 0 +-2113 -1626 0 +-2113 -1639 0 +-2113 -2111 0 +2107 2108 2109 2110 2113 0 +-1640 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1615 1623 1631 1636 1637 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 0 +2114 -792 2115 1614 0 +-2114 792 0 +-2114 -2115 0 +-2114 -1614 0 +2116 -792 -2115 -1614 0 +-2116 792 0 +-2116 2115 0 +-2116 1614 0 +2117 792 -2115 1614 0 +-2117 -792 0 +-2117 2115 0 +-2117 -1614 0 +2118 792 2115 -1614 -2119 0 +-2118 -2120 0 +-2118 -792 0 +-2118 -2115 0 +-2118 1614 0 +-2118 2119 0 +2121 792 2115 1614 2119 0 +-2121 -2120 0 +-2121 -792 0 +-2121 -2115 0 +-2121 -1614 0 +-2121 -2119 0 +2114 2116 2117 2118 2121 0 +-1615 0 +a 0 +2122 -2115 2123 1622 0 +-2122 2115 0 +-2122 -2123 0 +-2122 -1622 0 +2124 -2115 -2123 -1622 0 +-2124 2115 0 +-2124 2123 0 +-2124 1622 0 +2125 2115 -2123 1622 0 +-2125 -2115 0 +-2125 2123 0 +-2125 -1622 0 +2126 2115 2123 -1622 -2127 0 +-2126 -2128 0 +-2126 -2115 0 +-2126 -2123 0 +-2126 1622 0 +-2126 2127 0 +2129 2115 2123 1622 2127 0 +-2129 -2128 0 +-2129 -2115 0 +-2129 -2123 0 +-2129 -1622 0 +-2129 -2127 0 +2122 2124 2125 2126 2129 0 +-1623 0 +2130 -2123 2131 1630 0 +-2130 2123 0 +-2130 -2131 0 +-2130 -1630 0 +2132 -2123 -2131 -1630 0 +-2132 2123 0 +-2132 2131 0 +-2132 1630 0 +2133 2123 -2131 1630 0 +-2133 -2123 0 +-2133 2131 0 +-2133 -1630 0 +2134 2123 2131 -1630 -2135 0 +-2134 -2136 0 +-2134 -2123 0 +-2134 -2131 0 +-2134 1630 0 +-2134 2135 0 +2137 2123 2131 1630 2135 0 +-2137 -2136 0 +-2137 -2123 0 +-2137 -2131 0 +-2137 -1630 0 +-2137 -2135 0 +2130 2132 2133 2134 2137 0 +-1631 0 +2138 -2131 2139 1634 0 +-2138 2131 0 +-2138 -2139 0 +-2138 -1634 0 +2140 -2131 -2139 -1634 0 +-2140 2131 0 +-2140 2139 0 +-2140 1634 0 +2141 2131 -2139 1634 0 +-2141 -2131 0 +-2141 2139 0 +-2141 -1634 0 +2142 2131 2139 -1634 -2143 0 +-2142 -2144 0 +-2142 -2131 0 +-2142 -2139 0 +-2142 1634 0 +-2142 2143 0 +2145 2131 2139 1634 2143 0 +-2145 -2144 0 +-2145 -2131 0 +-2145 -2139 0 +-2145 -1634 0 +-2145 -2143 0 +2138 2140 2141 2142 2145 0 +-1636 0 +2146 -2147 2148 2139 0 +-2146 -48 0 +-2146 -49 0 +-2146 -2149 0 +-2146 -2150 0 +-2146 -43 0 +-2146 -44 0 +-2146 -45 0 +-2146 -46 0 +-2146 -47 0 +-2146 2147 0 +-2146 -2148 0 +-2146 -2139 0 +2151 -2152 -2148 0 +-2151 -48 0 +-2151 -49 0 +-2151 -43 0 +-2151 -44 0 +-2151 -45 0 +-2151 -46 0 +-2151 -47 0 +-2151 -2153 0 +-2151 2152 0 +-2151 2148 0 +2146 2151 0 +-1637 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1646 1654 1662 1671 1678 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 0 +2154 -1041 1730 1677 0 +-2154 1041 0 +-2154 -1730 0 +-2154 -1677 0 +2155 -1041 -1730 -1677 0 +-2155 1041 0 +-2155 1730 0 +-2155 1677 0 +2156 1041 -1730 1677 0 +-2156 -1041 0 +-2156 1730 0 +-2156 -1677 0 +2157 1041 1730 -1677 -2158 0 +-2157 -2159 0 +-2157 -1041 0 +-2157 -1730 0 +-2157 1677 0 +-2157 2158 0 +2160 1041 1730 1677 2158 0 +-2160 -2159 0 +-2160 -1041 0 +-2160 -1730 0 +-2160 -1677 0 +-2160 -2158 0 +2154 2155 2156 2157 2160 0 +-1678 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1646 1654 1662 1671 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 0 +2161 -16 1908 1645 0 +-2161 16 0 +-2161 -1908 0 +-2161 -1645 0 +2162 -16 -1908 -1645 0 +-2162 16 0 +-2162 1908 0 +-2162 1645 0 +2163 16 -1908 1645 0 +-2163 -16 0 +-2163 1908 0 +-2163 -1645 0 +2164 16 1908 -1645 -2165 0 +-2164 -2166 0 +-2164 -16 0 +-2164 -1908 0 +-2164 1645 0 +-2164 2165 0 +2167 16 1908 1645 2165 0 +-2167 -2166 0 +-2167 -16 0 +-2167 -1908 0 +-2167 -1645 0 +-2167 -2165 0 +2161 2162 2163 2164 2167 0 +-1646 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1654 1662 1671 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 0 +2168 -1665 2169 1653 0 +-2168 1665 0 +-2168 -2169 0 +-2168 -1653 0 +2170 -1665 -2169 -1653 0 +-2170 1665 0 +-2170 2169 0 +-2170 1653 0 +2171 1665 -2169 1653 0 +-2171 -1665 0 +-2171 2169 0 +-2171 -1653 0 +2172 1665 2169 -1653 -2173 0 +-2172 -2174 0 +-2172 -1665 0 +-2172 -2169 0 +-2172 1653 0 +-2172 2173 0 +2175 1665 2169 1653 2173 0 +-2175 -2174 0 +-2175 -1665 0 +-2175 -2169 0 +-2175 -1653 0 +-2175 -2173 0 +2168 2170 2171 2172 2175 0 +-1654 0 +a 0 +a 119 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1662 1671 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 0 +2176 -886 2177 117 0 +-2176 886 0 +-2176 -2177 0 +-2176 -117 0 +2178 -886 -2177 -117 0 +-2178 886 0 +-2178 2177 0 +-2178 117 0 +2179 886 -2177 117 0 +-2179 -886 0 +-2179 2177 0 +-2179 -117 0 +2180 886 2177 -117 -2181 0 +-2180 -2182 0 +-2180 -886 0 +-2180 -2177 0 +-2180 117 0 +-2180 2181 0 +2183 886 2177 117 2181 0 +-2183 -2182 0 +-2183 -886 0 +-2183 -2177 0 +-2183 -117 0 +-2183 -2181 0 +2176 2178 2179 2180 2183 0 +-119 0 +a 0 +2184 -2169 2185 1661 0 +-2184 2169 0 +-2184 -2185 0 +-2184 -1661 0 +2186 -2169 -2185 -1661 0 +-2186 2169 0 +-2186 2185 0 +-2186 1661 0 +2187 2169 -2185 1661 0 +-2187 -2169 0 +-2187 2185 0 +-2187 -1661 0 +2188 2169 2185 -1661 -2189 0 +-2188 -2190 0 +-2188 -2169 0 +-2188 -2185 0 +-2188 1661 0 +-2188 2189 0 +2191 2169 2185 1661 2189 0 +-2191 -2190 0 +-2191 -2169 0 +-2191 -2185 0 +-2191 -1661 0 +-2191 -2189 0 +2184 2186 2187 2188 2191 0 +-1662 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1671 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 0 +2192 -2193 2194 1670 0 +-2192 2193 0 +-2192 -2194 0 +-2192 -1670 0 +2195 -2193 -2194 -1670 0 +-2195 2193 0 +-2195 2194 0 +-2195 1670 0 +2196 2193 -2194 1670 0 +-2196 -2193 0 +-2196 2194 0 +-2196 -1670 0 +2197 2193 2194 -1670 -2198 0 +-2197 -2199 0 +-2197 -2193 0 +-2197 -2194 0 +-2197 1670 0 +-2197 2198 0 +2200 2193 2194 1670 2198 0 +-2200 -2199 0 +-2200 -2193 0 +-2200 -2194 0 +-2200 -1670 0 +-2200 -2198 0 +2192 2195 2196 2197 2200 0 +-1671 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1685 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 0 +2201 -1210 1923 1684 0 +-2201 1210 0 +-2201 -1923 0 +-2201 -1684 0 +2202 -1210 -1923 -1684 0 +-2202 1210 0 +-2202 1923 0 +-2202 1684 0 +2203 1210 -1923 1684 0 +-2203 -1210 0 +-2203 1923 0 +-2203 -1684 0 +2204 1210 1923 -1684 -2205 0 +-2204 -2206 0 +-2204 -1210 0 +-2204 -1923 0 +-2204 1684 0 +-2204 2205 0 +2207 1210 1923 1684 2205 0 +-2207 -2206 0 +-2207 -1210 0 +-2207 -1923 0 +-2207 -1684 0 +-2207 -2205 0 +2201 2202 2203 2204 2207 0 +-1685 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1692 1699 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 0 +2208 -894 1923 1698 0 +-2208 894 0 +-2208 -1923 0 +-2208 -1698 0 +2209 -894 -1923 -1698 0 +-2209 894 0 +-2209 1923 0 +-2209 1698 0 +2210 894 -1923 1698 0 +-2210 -894 0 +-2210 1923 0 +-2210 -1698 0 +2211 894 1923 -1698 -2212 0 +-2211 -2213 0 +-2211 -894 0 +-2211 -1923 0 +-2211 1698 0 +-2211 2212 0 +2214 894 1923 1698 2212 0 +-2214 -2213 0 +-2214 -894 0 +-2214 -1923 0 +-2214 -1698 0 +-2214 -2212 0 +2208 2209 2210 2211 2214 0 +-1699 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1517 1692 1706 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 0 +2215 -710 2216 1516 0 +-2215 710 0 +-2215 -2216 0 +-2215 -1516 0 +2217 -710 -2216 -1516 0 +-2217 710 0 +-2217 2216 0 +-2217 1516 0 +2218 710 -2216 1516 0 +-2218 -710 0 +-2218 2216 0 +-2218 -1516 0 +2219 710 2216 -1516 -2220 0 +-2219 -2221 0 +-2219 -710 0 +-2219 -2216 0 +-2219 1516 0 +-2219 2220 0 +2222 710 2216 1516 2220 0 +-2222 -2221 0 +-2222 -710 0 +-2222 -2216 0 +-2222 -1516 0 +-2222 -2220 0 +2215 2217 2218 2219 2222 0 +-1517 0 +a 0 +2223 -592 1746 1705 0 +-2223 592 0 +-2223 -1746 0 +-2223 -1705 0 +2224 -592 -1746 -1705 0 +-2224 592 0 +-2224 1746 0 +-2224 1705 0 +2225 592 -1746 1705 0 +-2225 -592 0 +-2225 1746 0 +-2225 -1705 0 +2226 592 1746 -1705 -2227 0 +-2226 -2228 0 +-2226 -592 0 +-2226 -1746 0 +-2226 1705 0 +-2226 2227 0 +2229 592 1746 1705 2227 0 +-2229 -2228 0 +-2229 -592 0 +-2229 -1746 0 +-2229 -1705 0 +-2229 -2227 0 +2223 2224 2225 2226 2229 0 +-1706 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1720 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 0 +2230 -435 1754 1719 0 +-2230 435 0 +-2230 -1754 0 +-2230 -1719 0 +2231 -435 -1754 -1719 0 +-2231 435 0 +-2231 1754 0 +-2231 1719 0 +2232 435 -1754 1719 0 +-2232 -435 0 +-2232 1754 0 +-2232 -1719 0 +2233 435 1754 -1719 -2234 0 +-2233 -2235 0 +-2233 -435 0 +-2233 -1754 0 +-2233 1719 0 +-2233 2234 0 +2236 435 1754 1719 2234 0 +-2236 -2235 0 +-2236 -435 0 +-2236 -1754 0 +-2236 -1719 0 +-2236 -2234 0 +2230 2231 2232 2233 2236 0 +-1720 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1727 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 0 +2237 -210 1762 1726 0 +-2237 210 0 +-2237 -1762 0 +-2237 -1726 0 +2238 -210 -1762 -1726 0 +-2238 210 0 +-2238 1762 0 +-2238 1726 0 +2239 210 -1762 1726 0 +-2239 -210 0 +-2239 1762 0 +-2239 -1726 0 +2240 210 1762 -1726 -2241 0 +-2240 -2242 0 +-2240 -210 0 +-2240 -1762 0 +-2240 1726 0 +-2240 2241 0 +2243 210 1762 1726 2241 0 +-2243 -2242 0 +-2243 -210 0 +-2243 -1762 0 +-2243 -1726 0 +-2243 -2241 0 +2237 2238 2239 2240 2243 0 +-1727 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1735 1743 1751 1759 1767 1775 1780 1781 1784 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 0 +2244 -67 1770 1783 0 +-2244 67 0 +-2244 -1770 0 +-2244 -1783 0 +2245 -67 -1770 -1783 0 +-2245 67 0 +-2245 1770 0 +-2245 1783 0 +2246 67 -1770 1783 0 +-2246 -67 0 +-2246 1770 0 +-2246 -1783 0 +2247 67 1770 -1783 -2248 0 +-2247 -2249 0 +-2247 -67 0 +-2247 -1770 0 +-2247 1783 0 +-2247 2248 0 +2250 67 1770 1783 2248 0 +-2250 -2249 0 +-2250 -67 0 +-2250 -1770 0 +-2250 -1783 0 +-2250 -2248 0 +2244 2245 2246 2247 2250 0 +-1784 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1735 1743 1751 1759 1767 1775 1780 1781 1792 1800 1808 1816 1824 1828 1829 1832 1838 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 0 +2251 -16 2252 1734 0 +-2251 16 0 +-2251 -2252 0 +-2251 -1734 0 +2253 -16 -2252 -1734 0 +-2253 16 0 +-2253 2252 0 +-2253 1734 0 +2254 16 -2252 1734 0 +-2254 -16 0 +-2254 2252 0 +-2254 -1734 0 +2255 16 2252 -1734 -2256 0 +-2255 -2257 0 +-2255 -16 0 +-2255 -2252 0 +-2255 1734 0 +-2255 2256 0 +2258 16 2252 1734 2256 0 +-2258 -2257 0 +-2258 -16 0 +-2258 -2252 0 +-2258 -1734 0 +-2258 -2256 0 +2251 2253 2254 2255 2258 0 +-1735 0 +a 0 +2259 -2252 2260 1742 0 +-2259 2252 0 +-2259 -2260 0 +-2259 -1742 0 +2261 -2252 -2260 -1742 0 +-2261 2252 0 +-2261 2260 0 +-2261 1742 0 +2262 2252 -2260 1742 0 +-2262 -2252 0 +-2262 2260 0 +-2262 -1742 0 +2263 2252 2260 -1742 -2264 0 +-2263 -2265 0 +-2263 -2252 0 +-2263 -2260 0 +-2263 1742 0 +-2263 2264 0 +2266 2252 2260 1742 2264 0 +-2266 -2265 0 +-2266 -2252 0 +-2266 -2260 0 +-2266 -1742 0 +-2266 -2264 0 +2259 2261 2262 2263 2266 0 +-1743 0 +2267 -2260 2268 1750 0 +-2267 2260 0 +-2267 -2268 0 +-2267 -1750 0 +2269 -2260 -2268 -1750 0 +-2269 2260 0 +-2269 2268 0 +-2269 1750 0 +2270 2260 -2268 1750 0 +-2270 -2260 0 +-2270 2268 0 +-2270 -1750 0 +2271 2260 2268 -1750 -2272 0 +-2271 -2273 0 +-2271 -2260 0 +-2271 -2268 0 +-2271 1750 0 +-2271 2272 0 +2274 2260 2268 1750 2272 0 +-2274 -2273 0 +-2274 -2260 0 +-2274 -2268 0 +-2274 -1750 0 +-2274 -2272 0 +2267 2269 2270 2271 2274 0 +-1751 0 +2275 -2268 2276 1758 0 +-2275 2268 0 +-2275 -2276 0 +-2275 -1758 0 +2277 -2268 -2276 -1758 0 +-2277 2268 0 +-2277 2276 0 +-2277 1758 0 +2278 2268 -2276 1758 0 +-2278 -2268 0 +-2278 2276 0 +-2278 -1758 0 +2279 2268 2276 -1758 -2280 0 +-2279 -2281 0 +-2279 -2268 0 +-2279 -2276 0 +-2279 1758 0 +-2279 2280 0 +2282 2268 2276 1758 2280 0 +-2282 -2281 0 +-2282 -2268 0 +-2282 -2276 0 +-2282 -1758 0 +-2282 -2280 0 +2275 2277 2278 2279 2282 0 +-1759 0 +2283 -2276 2284 1766 0 +-2283 2276 0 +-2283 -2284 0 +-2283 -1766 0 +2285 -2276 -2284 -1766 0 +-2285 2276 0 +-2285 2284 0 +-2285 1766 0 +2286 2276 -2284 1766 0 +-2286 -2276 0 +-2286 2284 0 +-2286 -1766 0 +2287 2276 2284 -1766 -2288 0 +-2287 -2289 0 +-2287 -2276 0 +-2287 -2284 0 +-2287 1766 0 +-2287 2288 0 +2290 2276 2284 1766 2288 0 +-2290 -2289 0 +-2290 -2276 0 +-2290 -2284 0 +-2290 -1766 0 +-2290 -2288 0 +2283 2285 2286 2287 2290 0 +-1767 0 +2291 -2284 2292 1774 0 +-2291 2284 0 +-2291 -2292 0 +-2291 -1774 0 +2293 -2284 -2292 -1774 0 +-2293 2284 0 +-2293 2292 0 +-2293 1774 0 +2294 2284 -2292 1774 0 +-2294 -2284 0 +-2294 2292 0 +-2294 -1774 0 +2295 2284 2292 -1774 -2296 0 +-2295 -2297 0 +-2295 -2284 0 +-2295 -2292 0 +-2295 1774 0 +-2295 2296 0 +2298 2284 2292 1774 2296 0 +-2298 -2297 0 +-2298 -2284 0 +-2298 -2292 0 +-2298 -1774 0 +-2298 -2296 0 +2291 2293 2294 2295 2298 0 +-1775 0 +2299 -2292 2300 1778 0 +-2299 2292 0 +-2299 -2300 0 +-2299 -1778 0 +2301 -2292 -2300 -1778 0 +-2301 2292 0 +-2301 2300 0 +-2301 1778 0 +2302 2292 -2300 1778 0 +-2302 -2292 0 +-2302 2300 0 +-2302 -1778 0 +2303 2292 2300 -1778 -2304 0 +-2303 -2305 0 +-2303 -2292 0 +-2303 -2300 0 +-2303 1778 0 +-2303 2304 0 +2306 2292 2300 1778 2304 0 +-2306 -2305 0 +-2306 -2292 0 +-2306 -2300 0 +-2306 -1778 0 +-2306 -2304 0 +2299 2301 2302 2303 2306 0 +-1780 0 +2307 -2308 2309 2300 0 +-2307 -48 0 +-2307 -49 0 +-2307 -2310 0 +-2307 -2311 0 +-2307 -41 0 +-2307 -42 0 +-2307 -43 0 +-2307 -44 0 +-2307 -45 0 +-2307 -46 0 +-2307 -47 0 +-2307 2308 0 +-2307 -2309 0 +-2307 -2300 0 +2312 -2313 -2309 0 +-2312 -48 0 +-2312 -49 0 +-2312 -41 0 +-2312 -42 0 +-2312 -43 0 +-2312 -44 0 +-2312 -45 0 +-2312 -46 0 +-2312 -47 0 +-2312 -2314 0 +-2312 2313 0 +-2312 2309 0 +2307 2312 0 +-1781 0 +2315 -2316 2317 1791 0 +-2315 2316 0 +-2315 -2317 0 +-2315 -1791 0 +2318 -2316 -2317 -1791 0 +-2318 2316 0 +-2318 2317 0 +-2318 1791 0 +2319 2316 -2317 1791 0 +-2319 -2316 0 +-2319 2317 0 +-2319 -1791 0 +2320 2316 2317 -1791 -2321 0 +-2320 -2322 0 +-2320 -2316 0 +-2320 -2317 0 +-2320 1791 0 +-2320 2321 0 +2323 2316 2317 1791 2321 0 +-2323 -2322 0 +-2323 -2316 0 +-2323 -2317 0 +-2323 -1791 0 +-2323 -2321 0 +2315 2318 2319 2320 2323 0 +-1792 0 +2324 -2316 2325 1799 0 +-2324 2316 0 +-2324 -2325 0 +-2324 -1799 0 +2326 -2316 -2325 -1799 0 +-2326 2316 0 +-2326 2325 0 +-2326 1799 0 +2327 2316 -2325 1799 0 +-2327 -2316 0 +-2327 2325 0 +-2327 -1799 0 +2328 2316 2325 -1799 -2329 0 +-2328 -2330 0 +-2328 -2316 0 +-2328 -2325 0 +-2328 1799 0 +-2328 2329 0 +2331 2316 2325 1799 2329 0 +-2331 -2330 0 +-2331 -2316 0 +-2331 -2325 0 +-2331 -1799 0 +-2331 -2329 0 +2324 2326 2327 2328 2331 0 +-1800 0 +2332 -2325 2333 1807 0 +-2332 2325 0 +-2332 -2333 0 +-2332 -1807 0 +2334 -2325 -2333 -1807 0 +-2334 2325 0 +-2334 2333 0 +-2334 1807 0 +2335 2325 -2333 1807 0 +-2335 -2325 0 +-2335 2333 0 +-2335 -1807 0 +2336 2325 2333 -1807 -2337 0 +-2336 -2338 0 +-2336 -2325 0 +-2336 -2333 0 +-2336 1807 0 +-2336 2337 0 +2339 2325 2333 1807 2337 0 +-2339 -2338 0 +-2339 -2325 0 +-2339 -2333 0 +-2339 -1807 0 +-2339 -2337 0 +2332 2334 2335 2336 2339 0 +-1808 0 +2340 -2333 2341 1815 0 +-2340 2333 0 +-2340 -2341 0 +-2340 -1815 0 +2342 -2333 -2341 -1815 0 +-2342 2333 0 +-2342 2341 0 +-2342 1815 0 +2343 2333 -2341 1815 0 +-2343 -2333 0 +-2343 2341 0 +-2343 -1815 0 +2344 2333 2341 -1815 -2345 0 +-2344 -2346 0 +-2344 -2333 0 +-2344 -2341 0 +-2344 1815 0 +-2344 2345 0 +2347 2333 2341 1815 2345 0 +-2347 -2346 0 +-2347 -2333 0 +-2347 -2341 0 +-2347 -1815 0 +-2347 -2345 0 +2340 2342 2343 2344 2347 0 +-1816 0 +2348 -2341 2349 1823 0 +-2348 2341 0 +-2348 -2349 0 +-2348 -1823 0 +2350 -2341 -2349 -1823 0 +-2350 2341 0 +-2350 2349 0 +-2350 1823 0 +2351 2341 -2349 1823 0 +-2351 -2341 0 +-2351 2349 0 +-2351 -1823 0 +2352 2341 2349 -1823 -2353 0 +-2352 -2354 0 +-2352 -2341 0 +-2352 -2349 0 +-2352 1823 0 +-2352 2353 0 +2355 2341 2349 1823 2353 0 +-2355 -2354 0 +-2355 -2341 0 +-2355 -2349 0 +-2355 -1823 0 +-2355 -2353 0 +2348 2350 2351 2352 2355 0 +-1824 0 +2356 -2349 2357 1827 0 +-2356 2349 0 +-2356 -2357 0 +-2356 -1827 0 +2358 -2349 -2357 -1827 0 +-2358 2349 0 +-2358 2357 0 +-2358 1827 0 +2359 2349 -2357 1827 0 +-2359 -2349 0 +-2359 2357 0 +-2359 -1827 0 +2360 2349 2357 -1827 -2361 0 +-2360 -2362 0 +-2360 -2349 0 +-2360 -2357 0 +-2360 1827 0 +-2360 2361 0 +2363 2349 2357 1827 2361 0 +-2363 -2362 0 +-2363 -2349 0 +-2363 -2357 0 +-2363 -1827 0 +-2363 -2361 0 +2356 2358 2359 2360 2363 0 +-1828 0 +2364 -2365 2309 2357 0 +-2364 -2366 0 +-2364 -2367 0 +-2364 2365 0 +-2364 -2309 0 +-2364 -2357 0 +2368 -2369 -2309 0 +-2368 -2370 0 +-2368 2369 0 +-2368 2309 0 +2364 2368 0 +-1829 0 +2371 -1041 2260 1837 0 +-2371 1041 0 +-2371 -2260 0 +-2371 -1837 0 +2372 -1041 -2260 -1837 0 +-2372 1041 0 +-2372 2260 0 +-2372 1837 0 +2373 1041 -2260 1837 0 +-2373 -1041 0 +-2373 2260 0 +-2373 -1837 0 +2374 1041 2260 -1837 -2375 0 +-2374 -2376 0 +-2374 -1041 0 +-2374 -2260 0 +-2374 1837 0 +-2374 2375 0 +2377 1041 2260 1837 2375 0 +-2377 -2376 0 +-2377 -1041 0 +-2377 -2260 0 +-2377 -1837 0 +-2377 -2375 0 +2371 2372 2373 2374 2377 0 +-1838 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1843 1844 1847 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 0 +2378 -102 1520 1846 0 +-2378 102 0 +-2378 -1520 0 +-2378 -1846 0 +2379 -102 -1520 -1846 0 +-2379 102 0 +-2379 1520 0 +-2379 1846 0 +2380 102 -1520 1846 0 +-2380 -102 0 +-2380 1520 0 +-2380 -1846 0 +2381 102 1520 -1846 -2382 0 +-2381 -2383 0 +-2381 -102 0 +-2381 -1520 0 +-2381 1846 0 +-2381 2382 0 +2384 102 1520 1846 2382 0 +-2384 -2383 0 +-2384 -102 0 +-2384 -1520 0 +-2384 -1846 0 +-2384 -2382 0 +2378 2379 2380 2381 2384 0 +-1847 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1843 1844 1850 1851 1860 1867 1874 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 0 +2385 -1435 1265 1873 0 +-2385 1435 0 +-2385 -1265 0 +-2385 -1873 0 +2386 -1435 -1265 -1873 0 +-2386 1435 0 +-2386 1265 0 +-2386 1873 0 +2387 1435 -1265 1873 0 +-2387 -1435 0 +-2387 1265 0 +-2387 -1873 0 +2388 1435 1265 -1873 -2389 0 +-2388 -2390 0 +-2388 -1435 0 +-2388 -1265 0 +-2388 1873 0 +-2388 2389 0 +2391 1435 1265 1873 2389 0 +-2391 -2390 0 +-2391 -1435 0 +-2391 -1265 0 +-2391 -1873 0 +-2391 -2389 0 +2385 2386 2387 2388 2391 0 +-1874 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1843 1844 1850 1851 1860 1867 1882 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 0 +2392 -2193 2393 1881 0 +-2392 2193 0 +-2392 -2393 0 +-2392 -1881 0 +2394 -2193 -2393 -1881 0 +-2394 2193 0 +-2394 2393 0 +-2394 1881 0 +2395 2193 -2393 1881 0 +-2395 -2193 0 +-2395 2393 0 +-2395 -1881 0 +2396 2193 2393 -1881 -2397 0 +-2396 -2398 0 +-2396 -2193 0 +-2396 -2393 0 +-2396 1881 0 +-2396 2397 0 +2399 2193 2393 1881 2397 0 +-2399 -2398 0 +-2399 -2193 0 +-2399 -2393 0 +-2399 -1881 0 +-2399 -2397 0 +2392 2394 2395 2396 2399 0 +-1882 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1843 1844 1850 1851 1860 1867 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 0 +2400 -2002 2401 1841 0 +-2400 2002 0 +-2400 -2401 0 +-2400 -1841 0 +2402 -2002 -2401 -1841 0 +-2402 2002 0 +-2402 2401 0 +-2402 1841 0 +2403 2002 -2401 1841 0 +-2403 -2002 0 +-2403 2401 0 +-2403 -1841 0 +2404 2002 2401 -1841 -2405 0 +-2404 -2406 0 +-2404 -2002 0 +-2404 -2401 0 +-2404 1841 0 +-2404 2405 0 +2407 2002 2401 1841 2405 0 +-2407 -2406 0 +-2407 -2002 0 +-2407 -2401 0 +-2407 -1841 0 +-2407 -2405 0 +2400 2402 2403 2404 2407 0 +-1843 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1844 1850 1851 1860 1867 1890 1897 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 0 +2408 -2185 2194 1896 0 +-2408 2185 0 +-2408 -2194 0 +-2408 -1896 0 +2409 -2185 -2194 -1896 0 +-2409 2185 0 +-2409 2194 0 +-2409 1896 0 +2410 2185 -2194 1896 0 +-2410 -2185 0 +-2410 2194 0 +-2410 -1896 0 +2411 2185 2194 -1896 -2412 0 +-2411 -2413 0 +-2411 -2185 0 +-2411 -2194 0 +-2411 1896 0 +-2411 2412 0 +2414 2185 2194 1896 2412 0 +-2414 -2413 0 +-2414 -2185 0 +-2414 -2194 0 +-2414 -1896 0 +-2414 -2412 0 +2408 2409 2410 2411 2414 0 +-1897 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1844 1850 1851 1860 1867 1890 1905 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 0 +2415 -2185 2416 1904 0 +-2415 2185 0 +-2415 -2416 0 +-2415 -1904 0 +2417 -2185 -2416 -1904 0 +-2417 2185 0 +-2417 2416 0 +-2417 1904 0 +2418 2185 -2416 1904 0 +-2418 -2185 0 +-2418 2416 0 +-2418 -1904 0 +2419 2185 2416 -1904 -2420 0 +-2419 -2421 0 +-2419 -2185 0 +-2419 -2416 0 +-2419 1904 0 +-2419 2420 0 +2422 2185 2416 1904 2420 0 +-2422 -2421 0 +-2422 -2185 0 +-2422 -2416 0 +-2422 -1904 0 +-2422 -2420 0 +2415 2417 2418 2419 2422 0 +-1905 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1692 1713 1832 1844 1850 1851 1860 1867 1890 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 0 +2423 -885 1948 1691 0 +-2423 885 0 +-2423 -1948 0 +-2423 -1691 0 +2424 -885 -1948 -1691 0 +-2424 885 0 +-2424 1948 0 +-2424 1691 0 +2425 885 -1948 1691 0 +-2425 -885 0 +-2425 1948 0 +-2425 -1691 0 +2426 885 1948 -1691 -2427 0 +-2426 -2428 0 +-2426 -885 0 +-2426 -1948 0 +-2426 1691 0 +-2426 2427 0 +2429 885 1948 1691 2427 0 +-2429 -2428 0 +-2429 -885 0 +-2429 -1948 0 +-2429 -1691 0 +-2429 -2427 0 +2423 2424 2425 2426 2429 0 +-1692 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1860 1867 1890 1913 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 0 +2430 -2416 2431 1912 0 +-2430 2416 0 +-2430 -2431 0 +-2430 -1912 0 +2432 -2416 -2431 -1912 0 +-2432 2416 0 +-2432 2431 0 +-2432 1912 0 +2433 2416 -2431 1912 0 +-2433 -2416 0 +-2433 2431 0 +-2433 -1912 0 +2434 2416 2431 -1912 -2435 0 +-2434 -2436 0 +-2434 -2416 0 +-2434 -2431 0 +-2434 1912 0 +-2434 2435 0 +2437 2416 2431 1912 2435 0 +-2437 -2436 0 +-2437 -2416 0 +-2437 -2431 0 +-2437 -1912 0 +-2437 -2435 0 +2430 2432 2433 2434 2437 0 +-1913 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1860 1867 1890 1920 1929 1937 1945 1953 1961 1965 1966 1969 1976 1983 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 0 +2438 -1201 1986 1919 0 +-2438 1201 0 +-2438 -1986 0 +-2438 -1919 0 +2439 -1201 -1986 -1919 0 +-2439 1201 0 +-2439 1986 0 +-2439 1919 0 +2440 1201 -1986 1919 0 +-2440 -1201 0 +-2440 1986 0 +-2440 -1919 0 +2441 1201 1986 -1919 -2442 0 +-2441 -2443 0 +-2441 -1201 0 +-2441 -1986 0 +-2441 1919 0 +-2441 2442 0 +2444 1201 1986 1919 2442 0 +-2444 -2443 0 +-2444 -1201 0 +-2444 -1986 0 +-2444 -1919 0 +-2444 -2442 0 +2438 2439 2440 2441 2444 0 +-1920 0 +a 0 +2445 -522 1994 1982 0 +-2445 522 0 +-2445 -1994 0 +-2445 -1982 0 +2446 -522 -1994 -1982 0 +-2446 522 0 +-2446 1994 0 +-2446 1982 0 +2447 522 -1994 1982 0 +-2447 -522 0 +-2447 1994 0 +-2447 -1982 0 +2448 522 1994 -1982 -2449 0 +-2448 -2450 0 +-2448 -522 0 +-2448 -1994 0 +-2448 1982 0 +-2448 2449 0 +2451 522 1994 1982 2449 0 +-2451 -2450 0 +-2451 -522 0 +-2451 -1994 0 +-2451 -1982 0 +-2451 -2449 0 +2445 2446 2447 2448 2451 0 +-1983 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1860 1867 1890 1929 1937 1945 1953 1961 1965 1966 1969 1976 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 0 +2452 -110 1956 1968 0 +-2452 110 0 +-2452 -1956 0 +-2452 -1968 0 +2453 -110 -1956 -1968 0 +-2453 110 0 +-2453 1956 0 +-2453 1968 0 +2454 110 -1956 1968 0 +-2454 -110 0 +-2454 1956 0 +-2454 -1968 0 +2455 110 1956 -1968 -2456 0 +-2455 -2457 0 +-2455 -110 0 +-2455 -1956 0 +-2455 1968 0 +-2455 2456 0 +2458 110 1956 1968 2456 0 +-2458 -2457 0 +-2458 -110 0 +-2458 -1956 0 +-2458 -1968 0 +-2458 -2456 0 +2452 2453 2454 2455 2458 0 +-1969 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1860 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 1991 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 0 +2459 -1877 2460 1990 0 +-2459 1877 0 +-2459 -2460 0 +-2459 -1990 0 +2461 -1877 -2460 -1990 0 +-2461 1877 0 +-2461 2460 0 +-2461 1990 0 +2462 1877 -2460 1990 0 +-2462 -1877 0 +-2462 2460 0 +-2462 -1990 0 +2463 1877 2460 -1990 -2464 0 +-2463 -2465 0 +-2463 -1877 0 +-2463 -2460 0 +-2463 1990 0 +-2463 2464 0 +2466 1877 2460 1990 2464 0 +-2466 -2465 0 +-2466 -1877 0 +-2466 -2460 0 +-2466 -1990 0 +-2466 -2464 0 +2459 2461 2462 2463 2466 0 +-1991 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1860 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 0 +2467 -886 2468 1859 0 +-2467 886 0 +-2467 -2468 0 +-2467 -1859 0 +2469 -886 -2468 -1859 0 +-2469 886 0 +-2469 2468 0 +-2469 1859 0 +2470 886 -2468 1859 0 +-2470 -886 0 +-2470 2468 0 +-2470 -1859 0 +2471 886 2468 -1859 -2472 0 +-2471 -2473 0 +-2471 -886 0 +-2471 -2468 0 +-2471 1859 0 +-2471 2472 0 +2474 886 2468 1859 2472 0 +-2474 -2473 0 +-2474 -886 0 +-2474 -2468 0 +-2474 -1859 0 +-2474 -2472 0 +2467 2469 2470 2471 2474 0 +-1860 0 +a 0 +a 120 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 0 +2475 -2476 855 2177 0 +-2475 -48 0 +-2475 -49 0 +-2475 -2477 0 +-2475 -2478 0 +-2475 2476 0 +-2475 -855 0 +-2475 -2177 0 +2479 -2480 -855 0 +-2479 -48 0 +-2479 -49 0 +-2479 -2481 0 +-2479 2480 0 +-2479 855 0 +2475 2479 0 +-120 0 +a 0 +2482 -2483 2484 2476 0 +-2482 2483 0 +-2482 -2484 0 +-2482 -2476 0 +2485 -2483 -2484 -2476 0 +-2485 2483 0 +-2485 2484 0 +-2485 2476 0 +2486 2483 -2484 2476 0 +-2486 -2483 0 +-2486 2484 0 +-2486 -2476 0 +2487 2483 2484 -2476 -2488 0 +-2487 -2489 0 +-2487 -2483 0 +-2487 -2484 0 +-2487 2476 0 +-2487 2488 0 +2490 2483 2484 2476 2488 0 +-2490 -2489 0 +-2490 -2483 0 +-2490 -2484 0 +-2490 -2476 0 +-2490 -2488 0 +2482 2485 2486 2487 2490 0 +-2477 0 +2491 -2492 1164 2484 0 +-2491 -48 0 +-2491 -49 0 +-2491 -2493 0 +-2491 -2494 0 +-2491 2492 0 +-2491 -1164 0 +-2491 -2484 0 +2495 -2496 -1164 0 +-2495 -48 0 +-2495 -49 0 +-2495 -2497 0 +-2495 2496 0 +-2495 1164 0 +2491 2495 0 +-2478 0 +a 0 +2498 -2499 2500 2488 0 +-2498 2499 0 +-2498 -2500 0 +-2498 -2488 0 +2501 -2499 -2500 -2488 0 +-2501 2499 0 +-2501 2500 0 +-2501 2488 0 +2502 2499 -2500 2488 0 +-2502 -2499 0 +-2502 2500 0 +-2502 -2488 0 +2503 2499 2500 -2488 -2504 0 +-2503 -2505 0 +-2503 -2499 0 +-2503 -2500 0 +-2503 2488 0 +-2503 2504 0 +2506 2499 2500 2488 2504 0 +-2506 -2505 0 +-2506 -2499 0 +-2506 -2500 0 +-2506 -2488 0 +-2506 -2504 0 +2498 2501 2502 2503 2506 0 +-2489 0 +2507 -2499 2508 2492 0 +-2507 2499 0 +-2507 -2508 0 +-2507 -2492 0 +2509 -2499 -2508 -2492 0 +-2509 2499 0 +-2509 2508 0 +-2509 2492 0 +2510 2499 -2508 2492 0 +-2510 -2499 0 +-2510 2508 0 +-2510 -2492 0 +2511 2499 2508 -2492 -2512 0 +-2511 -2513 0 +-2511 -2499 0 +-2511 -2508 0 +-2511 2492 0 +-2511 2512 0 +2514 2499 2508 2492 2512 0 +-2514 -2513 0 +-2514 -2499 0 +-2514 -2508 0 +-2514 -2492 0 +-2514 -2512 0 +2507 2509 2510 2511 2514 0 +-2493 0 +2515 -2516 1635 2508 0 +-2515 -48 0 +-2515 -49 0 +-2515 -2517 0 +-2515 -2518 0 +-2515 2516 0 +-2515 -1635 0 +-2515 -2508 0 +2519 -2520 -1635 0 +-2519 -48 0 +-2519 -49 0 +-2519 -2521 0 +-2519 2520 0 +-2519 1635 0 +2515 2519 0 +-2494 0 +a 0 +2522 -2523 2524 2504 0 +-2522 2523 0 +-2522 -2524 0 +-2522 -2504 0 +2525 -2523 -2524 -2504 0 +-2525 2523 0 +-2525 2524 0 +-2525 2504 0 +2526 2523 -2524 2504 0 +-2526 -2523 0 +-2526 2524 0 +-2526 -2504 0 +2527 2523 2524 -2504 -2528 0 +-2527 -2529 0 +-2527 -2523 0 +-2527 -2524 0 +-2527 2504 0 +-2527 2528 0 +2530 2523 2524 2504 2528 0 +-2530 -2529 0 +-2530 -2523 0 +-2530 -2524 0 +-2530 -2504 0 +-2530 -2528 0 +2522 2525 2526 2527 2530 0 +-2505 0 +2531 -2524 2532 2512 0 +-2531 2524 0 +-2531 -2532 0 +-2531 -2512 0 +2533 -2524 -2532 -2512 0 +-2533 2524 0 +-2533 2532 0 +-2533 2512 0 +2534 2524 -2532 2512 0 +-2534 -2524 0 +-2534 2532 0 +-2534 -2512 0 +2535 2524 2532 -2512 -2536 0 +-2535 -2537 0 +-2535 -2524 0 +-2535 -2532 0 +-2535 2512 0 +-2535 2536 0 +2538 2524 2532 2512 2536 0 +-2538 -2537 0 +-2538 -2524 0 +-2538 -2532 0 +-2538 -2512 0 +-2538 -2536 0 +2531 2533 2534 2535 2538 0 +-2513 0 +2539 -2532 2540 2516 0 +-2539 2532 0 +-2539 -2540 0 +-2539 -2516 0 +2541 -2532 -2540 -2516 0 +-2541 2532 0 +-2541 2540 0 +-2541 2516 0 +2542 2532 -2540 2516 0 +-2542 -2532 0 +-2542 2540 0 +-2542 -2516 0 +2543 2532 2540 -2516 -2544 0 +-2543 -2545 0 +-2543 -2532 0 +-2543 -2540 0 +-2543 2516 0 +-2543 2544 0 +2546 2532 2540 2516 2544 0 +-2546 -2545 0 +-2546 -2532 0 +-2546 -2540 0 +-2546 -2516 0 +-2546 -2544 0 +2539 2541 2542 2543 2546 0 +-2517 0 +2547 -2548 2148 2540 0 +-2547 -48 0 +-2547 -49 0 +-2547 -2549 0 +-2547 -2550 0 +-2547 2548 0 +-2547 -2148 0 +-2547 -2540 0 +2551 -2552 -2148 0 +-2551 -48 0 +-2551 -49 0 +-2551 -2553 0 +-2551 2552 0 +-2551 2148 0 +2547 2551 0 +-2518 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 1999 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 0 +2554 -2460 2555 1998 0 +-2554 2460 0 +-2554 -2555 0 +-2554 -1998 0 +2556 -2460 -2555 -1998 0 +-2556 2460 0 +-2556 2555 0 +-2556 1998 0 +2557 2460 -2555 1998 0 +-2557 -2460 0 +-2557 2555 0 +-2557 -1998 0 +2558 2460 2555 -1998 -2559 0 +-2558 -2560 0 +-2558 -2460 0 +-2558 -2555 0 +-2558 1998 0 +-2558 2559 0 +2561 2460 2555 1998 2559 0 +-2561 -2560 0 +-2561 -2460 0 +-2561 -2555 0 +-2561 -1998 0 +-2561 -2559 0 +2554 2556 2557 2558 2561 0 +-1999 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 2007 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 0 +2562 -2555 2563 2006 0 +-2562 2555 0 +-2562 -2563 0 +-2562 -2006 0 +2564 -2555 -2563 -2006 0 +-2564 2555 0 +-2564 2563 0 +-2564 2006 0 +2565 2555 -2563 2006 0 +-2565 -2555 0 +-2565 2563 0 +-2565 -2006 0 +2566 2555 2563 -2006 -2567 0 +-2566 -2568 0 +-2566 -2555 0 +-2566 -2563 0 +-2566 2006 0 +-2566 2567 0 +2569 2555 2563 2006 2567 0 +-2569 -2568 0 +-2569 -2555 0 +-2569 -2563 0 +-2569 -2006 0 +-2569 -2567 0 +2562 2564 2565 2566 2569 0 +-2007 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1844 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 2014 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 0 +2570 -2571 2572 2401 0 +-2570 -48 0 +-2570 -49 0 +-2570 -2573 0 +-2570 -2574 0 +-2570 -45 0 +-2570 -46 0 +-2570 -47 0 +-2570 2571 0 +-2570 -2572 0 +-2570 -2401 0 +2575 -2576 -2572 0 +-2575 -48 0 +-2575 -49 0 +-2575 -45 0 +-2575 -46 0 +-2575 -47 0 +-2575 -2577 0 +-2575 2576 0 +-2575 2572 0 +2570 2575 0 +-1844 0 +a 0 +2578 -219 2041 2013 0 +-2578 219 0 +-2578 -2041 0 +-2578 -2013 0 +2579 -219 -2041 -2013 0 +-2579 219 0 +-2579 2041 0 +-2579 2013 0 +2580 219 -2041 2013 0 +-2580 -219 0 +-2580 2041 0 +-2580 -2013 0 +2581 219 2041 -2013 -2582 0 +-2581 -2583 0 +-2581 -219 0 +-2581 -2041 0 +-2581 2013 0 +-2581 2582 0 +2584 219 2041 2013 2582 0 +-2584 -2583 0 +-2584 -219 0 +-2584 -2041 0 +-2584 -2013 0 +-2584 -2582 0 +2578 2579 2580 2581 2584 0 +-2014 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 2022 2030 2038 2046 2054 2059 2060 2063 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 0 +2585 -76 2049 2062 0 +-2585 76 0 +-2585 -2049 0 +-2585 -2062 0 +2586 -76 -2049 -2062 0 +-2586 76 0 +-2586 2049 0 +-2586 2062 0 +2587 76 -2049 2062 0 +-2587 -76 0 +-2587 2049 0 +-2587 -2062 0 +2588 76 2049 -2062 -2589 0 +-2588 -2590 0 +-2588 -76 0 +-2588 -2049 0 +-2588 2062 0 +-2588 2589 0 +2591 76 2049 2062 2589 0 +-2591 -2590 0 +-2591 -76 0 +-2591 -2049 0 +-2591 -2062 0 +-2591 -2589 0 +2585 2586 2587 2588 2591 0 +-2063 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1929 1937 1945 1953 1961 1965 1966 1976 2022 2030 2038 2046 2054 2059 2060 2069 2076 2083 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 0 +2592 -2593 2594 1928 0 +-2592 2593 0 +-2592 -2594 0 +-2592 -1928 0 +2595 -2593 -2594 -1928 0 +-2595 2593 0 +-2595 2594 0 +-2595 1928 0 +2596 2593 -2594 1928 0 +-2596 -2593 0 +-2596 2594 0 +-2596 -1928 0 +2597 2593 2594 -1928 -2598 0 +-2597 -2599 0 +-2597 -2593 0 +-2597 -2594 0 +-2597 1928 0 +-2597 2598 0 +2600 2593 2594 1928 2598 0 +-2600 -2599 0 +-2600 -2593 0 +-2600 -2594 0 +-2600 -1928 0 +-2600 -2598 0 +2592 2595 2596 2597 2600 0 +-1929 0 +a 0 +2601 -1466 2602 2021 0 +-2601 1466 0 +-2601 -2602 0 +-2601 -2021 0 +2603 -1466 -2602 -2021 0 +-2603 1466 0 +-2603 2602 0 +-2603 2021 0 +2604 1466 -2602 2021 0 +-2604 -1466 0 +-2604 2602 0 +-2604 -2021 0 +2605 1466 2602 -2021 -2606 0 +-2605 -2607 0 +-2605 -1466 0 +-2605 -2602 0 +-2605 2021 0 +-2605 2606 0 +2608 1466 2602 2021 2606 0 +-2608 -2607 0 +-2608 -1466 0 +-2608 -2602 0 +-2608 -2021 0 +-2608 -2606 0 +2601 2603 2604 2605 2608 0 +-2022 0 +2609 -2602 2610 2029 0 +-2609 2602 0 +-2609 -2610 0 +-2609 -2029 0 +2611 -2602 -2610 -2029 0 +-2611 2602 0 +-2611 2610 0 +-2611 2029 0 +2612 2602 -2610 2029 0 +-2612 -2602 0 +-2612 2610 0 +-2612 -2029 0 +2613 2602 2610 -2029 -2614 0 +-2613 -2615 0 +-2613 -2602 0 +-2613 -2610 0 +-2613 2029 0 +-2613 2614 0 +2616 2602 2610 2029 2614 0 +-2616 -2615 0 +-2616 -2602 0 +-2616 -2610 0 +-2616 -2029 0 +-2616 -2614 0 +2609 2611 2612 2613 2616 0 +-2030 0 +2617 -2610 2618 2037 0 +-2617 2610 0 +-2617 -2618 0 +-2617 -2037 0 +2619 -2610 -2618 -2037 0 +-2619 2610 0 +-2619 2618 0 +-2619 2037 0 +2620 2610 -2618 2037 0 +-2620 -2610 0 +-2620 2618 0 +-2620 -2037 0 +2621 2610 2618 -2037 -2622 0 +-2621 -2623 0 +-2621 -2610 0 +-2621 -2618 0 +-2621 2037 0 +-2621 2622 0 +2624 2610 2618 2037 2622 0 +-2624 -2623 0 +-2624 -2610 0 +-2624 -2618 0 +-2624 -2037 0 +-2624 -2622 0 +2617 2619 2620 2621 2624 0 +-2038 0 +2625 -2618 2626 2045 0 +-2625 2618 0 +-2625 -2626 0 +-2625 -2045 0 +2627 -2618 -2626 -2045 0 +-2627 2618 0 +-2627 2626 0 +-2627 2045 0 +2628 2618 -2626 2045 0 +-2628 -2618 0 +-2628 2626 0 +-2628 -2045 0 +2629 2618 2626 -2045 -2630 0 +-2629 -2631 0 +-2629 -2618 0 +-2629 -2626 0 +-2629 2045 0 +-2629 2630 0 +2632 2618 2626 2045 2630 0 +-2632 -2631 0 +-2632 -2618 0 +-2632 -2626 0 +-2632 -2045 0 +-2632 -2630 0 +2625 2627 2628 2629 2632 0 +-2046 0 +2633 -2626 2634 2053 0 +-2633 2626 0 +-2633 -2634 0 +-2633 -2053 0 +2635 -2626 -2634 -2053 0 +-2635 2626 0 +-2635 2634 0 +-2635 2053 0 +2636 2626 -2634 2053 0 +-2636 -2626 0 +-2636 2634 0 +-2636 -2053 0 +2637 2626 2634 -2053 -2638 0 +-2637 -2639 0 +-2637 -2626 0 +-2637 -2634 0 +-2637 2053 0 +-2637 2638 0 +2640 2626 2634 2053 2638 0 +-2640 -2639 0 +-2640 -2626 0 +-2640 -2634 0 +-2640 -2053 0 +-2640 -2638 0 +2633 2635 2636 2637 2640 0 +-2054 0 +2641 -2634 2642 2057 0 +-2641 2634 0 +-2641 -2642 0 +-2641 -2057 0 +2643 -2634 -2642 -2057 0 +-2643 2634 0 +-2643 2642 0 +-2643 2057 0 +2644 2634 -2642 2057 0 +-2644 -2634 0 +-2644 2642 0 +-2644 -2057 0 +2645 2634 2642 -2057 -2646 0 +-2645 -2647 0 +-2645 -2634 0 +-2645 -2642 0 +-2645 2057 0 +-2645 2646 0 +2648 2634 2642 2057 2646 0 +-2648 -2647 0 +-2648 -2634 0 +-2648 -2642 0 +-2648 -2057 0 +-2648 -2646 0 +2641 2643 2644 2645 2648 0 +-2059 0 +2649 -2650 2651 2642 0 +-2649 -48 0 +-2649 -49 0 +-2649 -45 0 +-2649 -46 0 +-2649 -47 0 +-2649 -2652 0 +-2649 -2653 0 +-2649 -42 0 +-2649 -43 0 +-2649 -44 0 +-2649 2650 0 +-2649 -2651 0 +-2649 -2642 0 +2654 -2655 -2651 0 +-2654 -48 0 +-2654 -49 0 +-2654 -45 0 +-2654 -46 0 +-2654 -47 0 +-2654 -42 0 +-2654 -43 0 +-2654 -44 0 +-2654 -2656 0 +-2654 2655 0 +-2654 2651 0 +2649 2654 0 +-2060 0 +2657 -1033 2602 2068 0 +-2657 1033 0 +-2657 -2602 0 +-2657 -2068 0 +2658 -1033 -2602 -2068 0 +-2658 1033 0 +-2658 2602 0 +-2658 2068 0 +2659 1033 -2602 2068 0 +-2659 -1033 0 +-2659 2602 0 +-2659 -2068 0 +2660 1033 2602 -2068 -2661 0 +-2660 -2662 0 +-2660 -1033 0 +-2660 -2602 0 +-2660 2068 0 +-2660 2661 0 +2663 1033 2602 2068 2661 0 +-2663 -2662 0 +-2663 -1033 0 +-2663 -2602 0 +-2663 -2068 0 +-2663 -2661 0 +2657 2658 2659 2660 2663 0 +-2069 0 +2664 -792 2610 2075 0 +-2664 792 0 +-2664 -2610 0 +-2664 -2075 0 +2665 -792 -2610 -2075 0 +-2665 792 0 +-2665 2610 0 +-2665 2075 0 +2666 792 -2610 2075 0 +-2666 -792 0 +-2666 2610 0 +-2666 -2075 0 +2667 792 2610 -2075 -2668 0 +-2667 -2669 0 +-2667 -792 0 +-2667 -2610 0 +-2667 2075 0 +-2667 2668 0 +2670 792 2610 2075 2668 0 +-2670 -2669 0 +-2670 -792 0 +-2670 -2610 0 +-2670 -2075 0 +-2670 -2668 0 +2664 2665 2666 2667 2670 0 +-2076 0 +2671 -575 2618 2082 0 +-2671 575 0 +-2671 -2618 0 +-2671 -2082 0 +2672 -575 -2618 -2082 0 +-2672 575 0 +-2672 2618 0 +-2672 2082 0 +2673 575 -2618 2082 0 +-2673 -575 0 +-2673 2618 0 +-2673 -2082 0 +2674 575 2618 -2082 -2675 0 +-2674 -2676 0 +-2674 -575 0 +-2674 -2618 0 +-2674 2082 0 +-2674 2675 0 +2677 575 2618 2082 2675 0 +-2677 -2676 0 +-2677 -575 0 +-2677 -2618 0 +-2677 -2082 0 +-2677 -2675 0 +2671 2672 2673 2674 2677 0 +-2083 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2090 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 0 +2678 -458 2115 2089 0 +-2678 458 0 +-2678 -2115 0 +-2678 -2089 0 +2679 -458 -2115 -2089 0 +-2679 458 0 +-2679 2115 0 +-2679 2089 0 +2680 458 -2115 2089 0 +-2680 -458 0 +-2680 2115 0 +-2680 -2089 0 +2681 458 2115 -2089 -2682 0 +-2681 -2683 0 +-2681 -458 0 +-2681 -2115 0 +-2681 2089 0 +-2681 2682 0 +2684 458 2115 2089 2682 0 +-2684 -2683 0 +-2684 -458 0 +-2684 -2115 0 +-2684 -2089 0 +-2684 -2682 0 +2678 2679 2680 2681 2684 0 +-2090 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2105 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 0 +2685 -366 2123 2104 0 +-2685 366 0 +-2685 -2123 0 +-2685 -2104 0 +2686 -366 -2123 -2104 0 +-2686 366 0 +-2686 2123 0 +-2686 2104 0 +2687 366 -2123 2104 0 +-2687 -366 0 +-2687 2123 0 +-2687 -2104 0 +2688 366 2123 -2104 -2689 0 +-2688 -2690 0 +-2688 -366 0 +-2688 -2123 0 +-2688 2104 0 +-2688 2689 0 +2691 366 2123 2104 2689 0 +-2691 -2690 0 +-2691 -366 0 +-2691 -2123 0 +-2691 -2104 0 +-2691 -2689 0 +2685 2686 2687 2688 2691 0 +-2105 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2112 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 0 +2692 -228 2131 2111 0 +-2692 228 0 +-2692 -2131 0 +-2692 -2111 0 +2693 -228 -2131 -2111 0 +-2693 228 0 +-2693 2131 0 +-2693 2111 0 +2694 228 -2131 2111 0 +-2694 -228 0 +-2694 2131 0 +-2694 -2111 0 +2695 228 2131 -2111 -2696 0 +-2695 -2697 0 +-2695 -228 0 +-2695 -2131 0 +-2695 2111 0 +-2695 2696 0 +2698 228 2131 2111 2696 0 +-2698 -2697 0 +-2698 -228 0 +-2698 -2131 0 +-2698 -2111 0 +-2698 -2696 0 +2692 2693 2694 2695 2698 0 +-2112 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2120 2128 2136 2144 2149 2150 2153 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 0 +2699 -85 2139 2152 0 +-2699 85 0 +-2699 -2139 0 +-2699 -2152 0 +2700 -85 -2139 -2152 0 +-2700 85 0 +-2700 2139 0 +-2700 2152 0 +2701 85 -2139 2152 0 +-2701 -85 0 +-2701 2139 0 +-2701 -2152 0 +2702 85 2139 -2152 -2703 0 +-2702 -2704 0 +-2702 -85 0 +-2702 -2139 0 +-2702 2152 0 +-2702 2703 0 +2705 85 2139 2152 2703 0 +-2705 -2704 0 +-2705 -85 0 +-2705 -2139 0 +-2705 -2152 0 +-2705 -2703 0 +2699 2700 2701 2702 2705 0 +-2153 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2120 2128 2136 2144 2149 2150 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2529 2537 2545 2549 2550 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 0 +2706 -1033 2707 2119 0 +-2706 1033 0 +-2706 -2707 0 +-2706 -2119 0 +2708 -1033 -2707 -2119 0 +-2708 1033 0 +-2708 2707 0 +-2708 2119 0 +2709 1033 -2707 2119 0 +-2709 -1033 0 +-2709 2707 0 +-2709 -2119 0 +2710 1033 2707 -2119 -2711 0 +-2710 -2712 0 +-2710 -1033 0 +-2710 -2707 0 +-2710 2119 0 +-2710 2711 0 +2713 1033 2707 2119 2711 0 +-2713 -2712 0 +-2713 -1033 0 +-2713 -2707 0 +-2713 -2119 0 +-2713 -2711 0 +2706 2708 2709 2710 2713 0 +-2120 0 +a 0 +2714 -2707 2715 2127 0 +-2714 2707 0 +-2714 -2715 0 +-2714 -2127 0 +2716 -2707 -2715 -2127 0 +-2716 2707 0 +-2716 2715 0 +-2716 2127 0 +2717 2707 -2715 2127 0 +-2717 -2707 0 +-2717 2715 0 +-2717 -2127 0 +2718 2707 2715 -2127 -2719 0 +-2718 -2720 0 +-2718 -2707 0 +-2718 -2715 0 +-2718 2127 0 +-2718 2719 0 +2721 2707 2715 2127 2719 0 +-2721 -2720 0 +-2721 -2707 0 +-2721 -2715 0 +-2721 -2127 0 +-2721 -2719 0 +2714 2716 2717 2718 2721 0 +-2128 0 +2722 -2715 2723 2135 0 +-2722 2715 0 +-2722 -2723 0 +-2722 -2135 0 +2724 -2715 -2723 -2135 0 +-2724 2715 0 +-2724 2723 0 +-2724 2135 0 +2725 2715 -2723 2135 0 +-2725 -2715 0 +-2725 2723 0 +-2725 -2135 0 +2726 2715 2723 -2135 -2727 0 +-2726 -2728 0 +-2726 -2715 0 +-2726 -2723 0 +-2726 2135 0 +-2726 2727 0 +2729 2715 2723 2135 2727 0 +-2729 -2728 0 +-2729 -2715 0 +-2729 -2723 0 +-2729 -2135 0 +-2729 -2727 0 +2722 2724 2725 2726 2729 0 +-2136 0 +2730 -2723 2731 2143 0 +-2730 2723 0 +-2730 -2731 0 +-2730 -2143 0 +2732 -2723 -2731 -2143 0 +-2732 2723 0 +-2732 2731 0 +-2732 2143 0 +2733 2723 -2731 2143 0 +-2733 -2723 0 +-2733 2731 0 +-2733 -2143 0 +2734 2723 2731 -2143 -2735 0 +-2734 -2736 0 +-2734 -2723 0 +-2734 -2731 0 +-2734 2143 0 +-2734 2735 0 +2737 2723 2731 2143 2735 0 +-2737 -2736 0 +-2737 -2723 0 +-2737 -2731 0 +-2737 -2143 0 +-2737 -2735 0 +2730 2732 2733 2734 2737 0 +-2144 0 +2738 -2731 2739 2147 0 +-2738 2731 0 +-2738 -2739 0 +-2738 -2147 0 +2740 -2731 -2739 -2147 0 +-2740 2731 0 +-2740 2739 0 +-2740 2147 0 +2741 2731 -2739 2147 0 +-2741 -2731 0 +-2741 2739 0 +-2741 -2147 0 +2742 2731 2739 -2147 -2743 0 +-2742 -2744 0 +-2742 -2731 0 +-2742 -2739 0 +-2742 2147 0 +-2742 2743 0 +2745 2731 2739 2147 2743 0 +-2745 -2744 0 +-2745 -2731 0 +-2745 -2739 0 +-2745 -2147 0 +-2745 -2743 0 +2738 2740 2741 2742 2745 0 +-2149 0 +2746 -2747 2748 2739 0 +-2746 -48 0 +-2746 -49 0 +-2746 -2749 0 +-2746 -2750 0 +-2746 -43 0 +-2746 -44 0 +-2746 -45 0 +-2746 -46 0 +-2746 -47 0 +-2746 2747 0 +-2746 -2748 0 +-2746 -2739 0 +2751 -2752 -2748 0 +-2751 -48 0 +-2751 -49 0 +-2751 -43 0 +-2751 -44 0 +-2751 -45 0 +-2751 -46 0 +-2751 -47 0 +-2751 -2753 0 +-2751 2752 0 +-2751 2748 0 +2746 2751 0 +-2150 0 +2754 -2755 2756 2528 0 +-2754 2755 0 +-2754 -2756 0 +-2754 -2528 0 +2757 -2755 -2756 -2528 0 +-2757 2755 0 +-2757 2756 0 +-2757 2528 0 +2758 2755 -2756 2528 0 +-2758 -2755 0 +-2758 2756 0 +-2758 -2528 0 +2759 2755 2756 -2528 -2760 0 +-2759 -2761 0 +-2759 -2755 0 +-2759 -2756 0 +-2759 2528 0 +-2759 2760 0 +2762 2755 2756 2528 2760 0 +-2762 -2761 0 +-2762 -2755 0 +-2762 -2756 0 +-2762 -2528 0 +-2762 -2760 0 +2754 2757 2758 2759 2762 0 +-2529 0 +2763 -2755 2764 2536 0 +-2763 2755 0 +-2763 -2764 0 +-2763 -2536 0 +2765 -2755 -2764 -2536 0 +-2765 2755 0 +-2765 2764 0 +-2765 2536 0 +2766 2755 -2764 2536 0 +-2766 -2755 0 +-2766 2764 0 +-2766 -2536 0 +2767 2755 2764 -2536 -2768 0 +-2767 -2769 0 +-2767 -2755 0 +-2767 -2764 0 +-2767 2536 0 +-2767 2768 0 +2770 2755 2764 2536 2768 0 +-2770 -2769 0 +-2770 -2755 0 +-2770 -2764 0 +-2770 -2536 0 +-2770 -2768 0 +2763 2765 2766 2767 2770 0 +-2537 0 +2771 -2764 2772 2544 0 +-2771 2764 0 +-2771 -2772 0 +-2771 -2544 0 +2773 -2764 -2772 -2544 0 +-2773 2764 0 +-2773 2772 0 +-2773 2544 0 +2774 2764 -2772 2544 0 +-2774 -2764 0 +-2774 2772 0 +-2774 -2544 0 +2775 2764 2772 -2544 -2776 0 +-2775 -2777 0 +-2775 -2764 0 +-2775 -2772 0 +-2775 2544 0 +-2775 2776 0 +2778 2764 2772 2544 2776 0 +-2778 -2777 0 +-2778 -2764 0 +-2778 -2772 0 +-2778 -2544 0 +-2778 -2776 0 +2771 2773 2774 2775 2778 0 +-2545 0 +2779 -2772 2780 2548 0 +-2779 2772 0 +-2779 -2780 0 +-2779 -2548 0 +2781 -2772 -2780 -2548 0 +-2781 2772 0 +-2781 2780 0 +-2781 2548 0 +2782 2772 -2780 2548 0 +-2782 -2772 0 +-2782 2780 0 +-2782 -2548 0 +2783 2772 2780 -2548 -2784 0 +-2783 -2785 0 +-2783 -2772 0 +-2783 -2780 0 +-2783 2548 0 +-2783 2784 0 +2786 2772 2780 2548 2784 0 +-2786 -2785 0 +-2786 -2772 0 +-2786 -2780 0 +-2786 -2548 0 +-2786 -2784 0 +2779 2781 2782 2783 2786 0 +-2549 0 +2787 -2788 2748 2780 0 +-2787 -48 0 +-2787 -49 0 +-2787 -2789 0 +-2787 -2790 0 +-2787 2788 0 +-2787 -2748 0 +-2787 -2780 0 +2791 -2792 -2748 0 +-2791 -48 0 +-2791 -49 0 +-2791 -2793 0 +-2791 2792 0 +-2791 2748 0 +2787 2791 0 +-2550 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2159 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 0 +2794 -1466 2252 2158 0 +-2794 1466 0 +-2794 -2252 0 +-2794 -2158 0 +2795 -1466 -2252 -2158 0 +-2795 1466 0 +-2795 2252 0 +-2795 2158 0 +2796 1466 -2252 2158 0 +-2796 -1466 0 +-2796 2252 0 +-2796 -2158 0 +2797 1466 2252 -2158 -2798 0 +-2797 -2799 0 +-2797 -1466 0 +-2797 -2252 0 +-2797 2158 0 +-2797 2798 0 +2800 1466 2252 2158 2798 0 +-2800 -2799 0 +-2800 -1466 0 +-2800 -2252 0 +-2800 -2158 0 +-2800 -2798 0 +2794 2795 2796 2797 2800 0 +-2159 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2166 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 0 +2801 -15 2431 2165 0 +-2801 15 0 +-2801 -2431 0 +-2801 -2165 0 +2802 -15 -2431 -2165 0 +-2802 15 0 +-2802 2431 0 +-2802 2165 0 +2803 15 -2431 2165 0 +-2803 -15 0 +-2803 2431 0 +-2803 -2165 0 +2804 15 2431 -2165 -2805 0 +-2804 -2806 0 +-2804 -15 0 +-2804 -2431 0 +-2804 2165 0 +-2804 2805 0 +2807 15 2431 2165 2805 0 +-2807 -2806 0 +-2807 -15 0 +-2807 -2431 0 +-2807 -2165 0 +-2807 -2805 0 +2801 2802 2803 2804 2807 0 +-2166 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1937 1945 1953 1961 1965 1966 1976 2098 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 0 +2808 -2593 2809 1936 0 +-2808 2593 0 +-2808 -2809 0 +-2808 -1936 0 +2810 -2593 -2809 -1936 0 +-2810 2593 0 +-2810 2809 0 +-2810 1936 0 +2811 2593 -2809 1936 0 +-2811 -2593 0 +-2811 2809 0 +-2811 -1936 0 +2812 2593 2809 -1936 -2813 0 +-2812 -2814 0 +-2812 -2593 0 +-2812 -2809 0 +-2812 1936 0 +-2812 2813 0 +2815 2593 2809 1936 2813 0 +-2815 -2814 0 +-2815 -2593 0 +-2815 -2809 0 +-2815 -1936 0 +-2815 -2813 0 +2808 2810 2811 2812 2815 0 +-1937 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1945 1953 1961 1965 1966 1976 2098 2174 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 0 +2816 -2194 2817 2173 0 +-2816 2194 0 +-2816 -2817 0 +-2816 -2173 0 +2818 -2194 -2817 -2173 0 +-2818 2194 0 +-2818 2817 0 +-2818 2173 0 +2819 2194 -2817 2173 0 +-2819 -2194 0 +-2819 2817 0 +-2819 -2173 0 +2820 2194 2817 -2173 -2821 0 +-2820 -2822 0 +-2820 -2194 0 +-2820 -2817 0 +-2820 2173 0 +-2820 2821 0 +2823 2194 2817 2173 2821 0 +-2823 -2822 0 +-2823 -2194 0 +-2823 -2817 0 +-2823 -2173 0 +-2823 -2821 0 +2816 2818 2819 2820 2823 0 +-2174 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1867 1890 1945 1953 1961 1965 1966 1976 2098 2182 2190 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 0 +2824 -2817 2825 2189 0 +-2824 2817 0 +-2824 -2825 0 +-2824 -2189 0 +2826 -2817 -2825 -2189 0 +-2826 2817 0 +-2826 2825 0 +-2826 2189 0 +2827 2817 -2825 2189 0 +-2827 -2817 0 +-2827 2825 0 +-2827 -2189 0 +2828 2817 2825 -2189 -2829 0 +-2828 -2830 0 +-2828 -2817 0 +-2828 -2825 0 +-2828 2189 0 +-2828 2829 0 +2831 2817 2825 2189 2829 0 +-2831 -2830 0 +-2831 -2817 0 +-2831 -2825 0 +-2831 -2189 0 +-2831 -2829 0 +2824 2826 2827 2828 2831 0 +-2190 0 +a 0 +2832 -522 1885 1866 0 +-2832 522 0 +-2832 -1885 0 +-2832 -1866 0 +2833 -522 -1885 -1866 0 +-2833 522 0 +-2833 1885 0 +-2833 1866 0 +2834 522 -1885 1866 0 +-2834 -522 0 +-2834 1885 0 +-2834 -1866 0 +2835 522 1885 -1866 -2836 0 +-2835 -2837 0 +-2835 -522 0 +-2835 -1885 0 +-2835 1866 0 +-2835 2836 0 +2838 522 1885 1866 2836 0 +-2838 -2837 0 +-2838 -522 0 +-2838 -1885 0 +-2838 -1866 0 +-2838 -2836 0 +2832 2833 2834 2835 2838 0 +-1867 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1945 1953 1961 1965 1966 1976 2098 2182 2199 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 0 +2839 -2840 2841 2198 0 +-2839 2840 0 +-2839 -2841 0 +-2839 -2198 0 +2842 -2840 -2841 -2198 0 +-2842 2840 0 +-2842 2841 0 +-2842 2198 0 +2843 2840 -2841 2198 0 +-2843 -2840 0 +-2843 2841 0 +-2843 -2198 0 +2844 2840 2841 -2198 -2845 0 +-2844 -2846 0 +-2844 -2840 0 +-2844 -2841 0 +-2844 2198 0 +-2844 2845 0 +2847 2840 2841 2198 2845 0 +-2847 -2846 0 +-2847 -2840 0 +-2847 -2841 0 +-2847 -2198 0 +-2847 -2845 0 +2839 2842 2843 2844 2847 0 +-2199 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1945 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2228 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 0 +2848 -800 2268 2227 0 +-2848 800 0 +-2848 -2268 0 +-2848 -2227 0 +2849 -800 -2268 -2227 0 +-2849 800 0 +-2849 2268 0 +-2849 2227 0 +2850 800 -2268 2227 0 +-2850 -800 0 +-2850 2268 0 +-2850 -2227 0 +2851 800 2268 -2227 -2852 0 +-2851 -2853 0 +-2851 -800 0 +-2851 -2268 0 +-2851 2227 0 +-2851 2852 0 +2854 800 2268 2227 2852 0 +-2854 -2853 0 +-2854 -800 0 +-2854 -2268 0 +-2854 -2227 0 +-2854 -2852 0 +2848 2849 2850 2851 2854 0 +-2228 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1945 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2235 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 0 +2855 -2809 2856 1944 0 +-2855 2809 0 +-2855 -2856 0 +-2855 -1944 0 +2857 -2809 -2856 -1944 0 +-2857 2809 0 +-2857 2856 0 +-2857 1944 0 +2858 2809 -2856 1944 0 +-2858 -2809 0 +-2858 2856 0 +-2858 -1944 0 +2859 2809 2856 -1944 -2860 0 +-2859 -2861 0 +-2859 -2809 0 +-2859 -2856 0 +-2859 1944 0 +-2859 2860 0 +2862 2809 2856 1944 2860 0 +-2862 -2861 0 +-2862 -2809 0 +-2862 -2856 0 +-2862 -1944 0 +-2862 -2860 0 +2855 2857 2858 2859 2862 0 +-1945 0 +a 0 +2863 -592 2276 2234 0 +-2863 592 0 +-2863 -2276 0 +-2863 -2234 0 +2864 -592 -2276 -2234 0 +-2864 592 0 +-2864 2276 0 +-2864 2234 0 +2865 592 -2276 2234 0 +-2865 -592 0 +-2865 2276 0 +-2865 -2234 0 +2866 592 2276 -2234 -2867 0 +-2866 -2868 0 +-2866 -592 0 +-2866 -2276 0 +-2866 2234 0 +-2866 2867 0 +2869 592 2276 2234 2867 0 +-2869 -2868 0 +-2869 -592 0 +-2869 -2276 0 +-2869 -2234 0 +-2869 -2867 0 +2863 2864 2865 2866 2869 0 +-2235 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 908 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2242 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 0 +2870 -2871 2872 907 0 +-2870 2871 0 +-2870 -2872 0 +-2870 -907 0 +2873 -2871 -2872 -907 0 +-2873 2871 0 +-2873 2872 0 +-2873 907 0 +2874 2871 -2872 907 0 +-2874 -2871 0 +-2874 2872 0 +-2874 -907 0 +2875 2871 2872 -907 -2876 0 +-2875 -2877 0 +-2875 -2871 0 +-2875 -2872 0 +-2875 907 0 +-2875 2876 0 +2878 2871 2872 907 2876 0 +-2878 -2877 0 +-2878 -2871 0 +-2878 -2872 0 +-2878 -907 0 +-2878 -2876 0 +2870 2873 2874 2875 2878 0 +-908 0 +a 0 +2879 -435 2284 2241 0 +-2879 435 0 +-2879 -2284 0 +-2879 -2241 0 +2880 -435 -2284 -2241 0 +-2880 435 0 +-2880 2284 0 +-2880 2241 0 +2881 435 -2284 2241 0 +-2881 -435 0 +-2881 2284 0 +-2881 -2241 0 +2882 435 2284 -2241 -2883 0 +-2882 -2884 0 +-2882 -435 0 +-2882 -2284 0 +-2882 2241 0 +-2882 2883 0 +2885 435 2284 2241 2883 0 +-2885 -2884 0 +-2885 -435 0 +-2885 -2284 0 +-2885 -2241 0 +-2885 -2883 0 +2879 2880 2881 2882 2885 0 +-2242 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 891 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2249 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 0 +2886 -2871 1885 890 0 +-2886 2871 0 +-2886 -1885 0 +-2886 -890 0 +2887 -2871 -1885 -890 0 +-2887 2871 0 +-2887 1885 0 +-2887 890 0 +2888 2871 -1885 890 0 +-2888 -2871 0 +-2888 1885 0 +-2888 -890 0 +2889 2871 1885 -890 -2890 0 +-2889 -2891 0 +-2889 -2871 0 +-2889 -1885 0 +-2889 890 0 +-2889 2890 0 +2892 2871 1885 890 2890 0 +-2892 -2891 0 +-2892 -2871 0 +-2892 -1885 0 +-2892 -890 0 +-2892 -2890 0 +2886 2887 2888 2889 2892 0 +-891 0 +a 0 +2893 -210 2292 2248 0 +-2893 210 0 +-2893 -2292 0 +-2893 -2248 0 +2894 -210 -2292 -2248 0 +-2894 210 0 +-2894 2292 0 +-2894 2248 0 +2895 210 -2292 2248 0 +-2895 -210 0 +-2895 2292 0 +-2895 -2248 0 +2896 210 2292 -2248 -2897 0 +-2896 -2898 0 +-2896 -210 0 +-2896 -2292 0 +-2896 2248 0 +-2896 2897 0 +2899 210 2292 2248 2897 0 +-2899 -2898 0 +-2899 -210 0 +-2899 -2292 0 +-2899 -2248 0 +-2899 -2897 0 +2893 2894 2895 2896 2899 0 +-2249 0 +a 0 +a 127 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 0 +2900 -903 2901 125 0 +-2900 903 0 +-2900 -2901 0 +-2900 -125 0 +2902 -903 -2901 -125 0 +-2902 903 0 +-2902 2901 0 +-2902 125 0 +2903 903 -2901 125 0 +-2903 -903 0 +-2903 2901 0 +-2903 -125 0 +2904 903 2901 -125 -2905 0 +-2904 -2906 0 +-2904 -903 0 +-2904 -2901 0 +-2904 125 0 +-2904 2905 0 +2907 903 2901 125 2905 0 +-2907 -2906 0 +-2907 -903 0 +-2907 -2901 0 +-2907 -125 0 +-2907 -2905 0 +2900 2902 2903 2904 2907 0 +-127 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1953 1961 1965 1966 1976 2098 2182 2206 2213 2221 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 0 +2908 -2856 2909 1952 0 +-2908 2856 0 +-2908 -2909 0 +-2908 -1952 0 +2910 -2856 -2909 -1952 0 +-2910 2856 0 +-2910 2909 0 +-2910 1952 0 +2911 2856 -2909 1952 0 +-2911 -2856 0 +-2911 2909 0 +-2911 -1952 0 +2912 2856 2909 -1952 -2913 0 +-2912 -2914 0 +-2912 -2856 0 +-2912 -2909 0 +-2912 1952 0 +-2912 2913 0 +2915 2856 2909 1952 2913 0 +-2915 -2914 0 +-2915 -2856 0 +-2915 -2909 0 +-2915 -1952 0 +-2915 -2913 0 +2908 2910 2911 2912 2915 0 +-1953 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1713 1832 1850 1851 1890 1961 1965 1966 1976 2098 2182 2206 2213 2221 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 0 +2916 -1885 1273 1712 0 +-2916 1885 0 +-2916 -1273 0 +-2916 -1712 0 +2917 -1885 -1273 -1712 0 +-2917 1885 0 +-2917 1273 0 +-2917 1712 0 +2918 1885 -1273 1712 0 +-2918 -1885 0 +-2918 1273 0 +-2918 -1712 0 +2919 1885 1273 -1712 -2920 0 +-2919 -2921 0 +-2919 -1885 0 +-2919 -1273 0 +-2919 1712 0 +-2919 2920 0 +2922 1885 1273 1712 2920 0 +-2922 -2921 0 +-2922 -1885 0 +-2922 -1273 0 +-2922 -1712 0 +-2922 -2920 0 +2916 2917 2918 2919 2922 0 +-1713 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1961 1965 1966 1976 2098 2182 2206 2213 2221 2257 2265 2273 2281 2289 2297 2305 2310 2311 2314 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 0 +2923 -67 2300 2313 0 +-2923 67 0 +-2923 -2300 0 +-2923 -2313 0 +2924 -67 -2300 -2313 0 +-2924 67 0 +-2924 2300 0 +-2924 2313 0 +2925 67 -2300 2313 0 +-2925 -67 0 +-2925 2300 0 +-2925 -2313 0 +2926 67 2300 -2313 -2927 0 +-2926 -2928 0 +-2926 -67 0 +-2926 -2300 0 +-2926 2313 0 +-2926 2927 0 +2929 67 2300 2313 2927 0 +-2929 -2928 0 +-2929 -67 0 +-2929 -2300 0 +-2929 -2313 0 +-2929 -2927 0 +2923 2924 2925 2926 2929 0 +-2314 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1961 1965 1966 1976 2098 2182 2206 2213 2221 2257 2265 2273 2281 2289 2297 2305 2310 2311 2322 2330 2338 2346 2354 2362 2366 2367 2370 2376 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2928 0 +2930 -15 2931 2256 0 +-2930 15 0 +-2930 -2931 0 +-2930 -2256 0 +2932 -15 -2931 -2256 0 +-2932 15 0 +-2932 2931 0 +-2932 2256 0 +2933 15 -2931 2256 0 +-2933 -15 0 +-2933 2931 0 +-2933 -2256 0 +2934 15 2931 -2256 -2935 0 +-2934 -2936 0 +-2934 -15 0 +-2934 -2931 0 +-2934 2256 0 +-2934 2935 0 +2937 15 2931 2256 2935 0 +-2937 -2936 0 +-2937 -15 0 +-2937 -2931 0 +-2937 -2256 0 +-2937 -2935 0 +2930 2932 2933 2934 2937 0 +-2257 0 +a 0 +2938 -2931 2939 2264 0 +-2938 2931 0 +-2938 -2939 0 +-2938 -2264 0 +2940 -2931 -2939 -2264 0 +-2940 2931 0 +-2940 2939 0 +-2940 2264 0 +2941 2931 -2939 2264 0 +-2941 -2931 0 +-2941 2939 0 +-2941 -2264 0 +2942 2931 2939 -2264 -2943 0 +-2942 -2944 0 +-2942 -2931 0 +-2942 -2939 0 +-2942 2264 0 +-2942 2943 0 +2945 2931 2939 2264 2943 0 +-2945 -2944 0 +-2945 -2931 0 +-2945 -2939 0 +-2945 -2264 0 +-2945 -2943 0 +2938 2940 2941 2942 2945 0 +-2265 0 +2946 -2939 2947 2272 0 +-2946 2939 0 +-2946 -2947 0 +-2946 -2272 0 +2948 -2939 -2947 -2272 0 +-2948 2939 0 +-2948 2947 0 +-2948 2272 0 +2949 2939 -2947 2272 0 +-2949 -2939 0 +-2949 2947 0 +-2949 -2272 0 +2950 2939 2947 -2272 -2951 0 +-2950 -2952 0 +-2950 -2939 0 +-2950 -2947 0 +-2950 2272 0 +-2950 2951 0 +2953 2939 2947 2272 2951 0 +-2953 -2952 0 +-2953 -2939 0 +-2953 -2947 0 +-2953 -2272 0 +-2953 -2951 0 +2946 2948 2949 2950 2953 0 +-2273 0 +2954 -2947 2955 2280 0 +-2954 2947 0 +-2954 -2955 0 +-2954 -2280 0 +2956 -2947 -2955 -2280 0 +-2956 2947 0 +-2956 2955 0 +-2956 2280 0 +2957 2947 -2955 2280 0 +-2957 -2947 0 +-2957 2955 0 +-2957 -2280 0 +2958 2947 2955 -2280 -2959 0 +-2958 -2960 0 +-2958 -2947 0 +-2958 -2955 0 +-2958 2280 0 +-2958 2959 0 +2961 2947 2955 2280 2959 0 +-2961 -2960 0 +-2961 -2947 0 +-2961 -2955 0 +-2961 -2280 0 +-2961 -2959 0 +2954 2956 2957 2958 2961 0 +-2281 0 +2962 -2955 2963 2288 0 +-2962 2955 0 +-2962 -2963 0 +-2962 -2288 0 +2964 -2955 -2963 -2288 0 +-2964 2955 0 +-2964 2963 0 +-2964 2288 0 +2965 2955 -2963 2288 0 +-2965 -2955 0 +-2965 2963 0 +-2965 -2288 0 +2966 2955 2963 -2288 -2967 0 +-2966 -2968 0 +-2966 -2955 0 +-2966 -2963 0 +-2966 2288 0 +-2966 2967 0 +2969 2955 2963 2288 2967 0 +-2969 -2968 0 +-2969 -2955 0 +-2969 -2963 0 +-2969 -2288 0 +-2969 -2967 0 +2962 2964 2965 2966 2969 0 +-2289 0 +2970 -2963 2971 2296 0 +-2970 2963 0 +-2970 -2971 0 +-2970 -2296 0 +2972 -2963 -2971 -2296 0 +-2972 2963 0 +-2972 2971 0 +-2972 2296 0 +2973 2963 -2971 2296 0 +-2973 -2963 0 +-2973 2971 0 +-2973 -2296 0 +2974 2963 2971 -2296 -2975 0 +-2974 -2976 0 +-2974 -2963 0 +-2974 -2971 0 +-2974 2296 0 +-2974 2975 0 +2977 2963 2971 2296 2975 0 +-2977 -2976 0 +-2977 -2963 0 +-2977 -2971 0 +-2977 -2296 0 +-2977 -2975 0 +2970 2972 2973 2974 2977 0 +-2297 0 +2978 -2971 2979 2304 0 +-2978 2971 0 +-2978 -2979 0 +-2978 -2304 0 +2980 -2971 -2979 -2304 0 +-2980 2971 0 +-2980 2979 0 +-2980 2304 0 +2981 2971 -2979 2304 0 +-2981 -2971 0 +-2981 2979 0 +-2981 -2304 0 +2982 2971 2979 -2304 -2983 0 +-2982 -2984 0 +-2982 -2971 0 +-2982 -2979 0 +-2982 2304 0 +-2982 2983 0 +2985 2971 2979 2304 2983 0 +-2985 -2984 0 +-2985 -2971 0 +-2985 -2979 0 +-2985 -2304 0 +-2985 -2983 0 +2978 2980 2981 2982 2985 0 +-2305 0 +2986 -2979 2987 2308 0 +-2986 2979 0 +-2986 -2987 0 +-2986 -2308 0 +2988 -2979 -2987 -2308 0 +-2988 2979 0 +-2988 2987 0 +-2988 2308 0 +2989 2979 -2987 2308 0 +-2989 -2979 0 +-2989 2987 0 +-2989 -2308 0 +2990 2979 2987 -2308 -2991 0 +-2990 -2992 0 +-2990 -2979 0 +-2990 -2987 0 +-2990 2308 0 +-2990 2991 0 +2993 2979 2987 2308 2991 0 +-2993 -2992 0 +-2993 -2979 0 +-2993 -2987 0 +-2993 -2308 0 +-2993 -2991 0 +2986 2988 2989 2990 2993 0 +-2310 0 +2994 -2995 2996 2987 0 +-2994 -48 0 +-2994 -49 0 +-2994 -2997 0 +-2994 -2998 0 +-2994 -41 0 +-2994 -42 0 +-2994 -43 0 +-2994 -44 0 +-2994 -45 0 +-2994 -46 0 +-2994 -47 0 +-2994 2995 0 +-2994 -2996 0 +-2994 -2987 0 +2999 -3000 -2996 0 +-2999 -48 0 +-2999 -49 0 +-2999 -41 0 +-2999 -42 0 +-2999 -43 0 +-2999 -44 0 +-2999 -45 0 +-2999 -46 0 +-2999 -47 0 +-2999 -3001 0 +-2999 3000 0 +-2999 2996 0 +2994 2999 0 +-2311 0 +3002 -3003 3004 2321 0 +-3002 3003 0 +-3002 -3004 0 +-3002 -2321 0 +3005 -3003 -3004 -2321 0 +-3005 3003 0 +-3005 3004 0 +-3005 2321 0 +3006 3003 -3004 2321 0 +-3006 -3003 0 +-3006 3004 0 +-3006 -2321 0 +3007 3003 3004 -2321 -3008 0 +-3007 -3009 0 +-3007 -3003 0 +-3007 -3004 0 +-3007 2321 0 +-3007 3008 0 +3010 3003 3004 2321 3008 0 +-3010 -3009 0 +-3010 -3003 0 +-3010 -3004 0 +-3010 -2321 0 +-3010 -3008 0 +3002 3005 3006 3007 3010 0 +-2322 0 +3011 -3004 3012 2329 0 +-3011 3004 0 +-3011 -3012 0 +-3011 -2329 0 +3013 -3004 -3012 -2329 0 +-3013 3004 0 +-3013 3012 0 +-3013 2329 0 +3014 3004 -3012 2329 0 +-3014 -3004 0 +-3014 3012 0 +-3014 -2329 0 +3015 3004 3012 -2329 -3016 0 +-3015 -3017 0 +-3015 -3004 0 +-3015 -3012 0 +-3015 2329 0 +-3015 3016 0 +3018 3004 3012 2329 3016 0 +-3018 -3017 0 +-3018 -3004 0 +-3018 -3012 0 +-3018 -2329 0 +-3018 -3016 0 +3011 3013 3014 3015 3018 0 +-2330 0 +3019 -3012 3020 2337 0 +-3019 3012 0 +-3019 -3020 0 +-3019 -2337 0 +3021 -3012 -3020 -2337 0 +-3021 3012 0 +-3021 3020 0 +-3021 2337 0 +3022 3012 -3020 2337 0 +-3022 -3012 0 +-3022 3020 0 +-3022 -2337 0 +3023 3012 3020 -2337 -3024 0 +-3023 -3025 0 +-3023 -3012 0 +-3023 -3020 0 +-3023 2337 0 +-3023 3024 0 +3026 3012 3020 2337 3024 0 +-3026 -3025 0 +-3026 -3012 0 +-3026 -3020 0 +-3026 -2337 0 +-3026 -3024 0 +3019 3021 3022 3023 3026 0 +-2338 0 +3027 -3020 3028 2345 0 +-3027 3020 0 +-3027 -3028 0 +-3027 -2345 0 +3029 -3020 -3028 -2345 0 +-3029 3020 0 +-3029 3028 0 +-3029 2345 0 +3030 3020 -3028 2345 0 +-3030 -3020 0 +-3030 3028 0 +-3030 -2345 0 +3031 3020 3028 -2345 -3032 0 +-3031 -3033 0 +-3031 -3020 0 +-3031 -3028 0 +-3031 2345 0 +-3031 3032 0 +3034 3020 3028 2345 3032 0 +-3034 -3033 0 +-3034 -3020 0 +-3034 -3028 0 +-3034 -2345 0 +-3034 -3032 0 +3027 3029 3030 3031 3034 0 +-2346 0 +3035 -3028 3036 2353 0 +-3035 3028 0 +-3035 -3036 0 +-3035 -2353 0 +3037 -3028 -3036 -2353 0 +-3037 3028 0 +-3037 3036 0 +-3037 2353 0 +3038 3028 -3036 2353 0 +-3038 -3028 0 +-3038 3036 0 +-3038 -2353 0 +3039 3028 3036 -2353 -3040 0 +-3039 -3041 0 +-3039 -3028 0 +-3039 -3036 0 +-3039 2353 0 +-3039 3040 0 +3042 3028 3036 2353 3040 0 +-3042 -3041 0 +-3042 -3028 0 +-3042 -3036 0 +-3042 -2353 0 +-3042 -3040 0 +3035 3037 3038 3039 3042 0 +-2354 0 +3043 -3036 3044 2361 0 +-3043 3036 0 +-3043 -3044 0 +-3043 -2361 0 +3045 -3036 -3044 -2361 0 +-3045 3036 0 +-3045 3044 0 +-3045 2361 0 +3046 3036 -3044 2361 0 +-3046 -3036 0 +-3046 3044 0 +-3046 -2361 0 +3047 3036 3044 -2361 -3048 0 +-3047 -3049 0 +-3047 -3036 0 +-3047 -3044 0 +-3047 2361 0 +-3047 3048 0 +3050 3036 3044 2361 3048 0 +-3050 -3049 0 +-3050 -3036 0 +-3050 -3044 0 +-3050 -2361 0 +-3050 -3048 0 +3043 3045 3046 3047 3050 0 +-2362 0 +3051 -3044 3052 2365 0 +-3051 3044 0 +-3051 -3052 0 +-3051 -2365 0 +3053 -3044 -3052 -2365 0 +-3053 3044 0 +-3053 3052 0 +-3053 2365 0 +3054 3044 -3052 2365 0 +-3054 -3044 0 +-3054 3052 0 +-3054 -2365 0 +3055 3044 3052 -2365 -3056 0 +-3055 -3057 0 +-3055 -3044 0 +-3055 -3052 0 +-3055 2365 0 +-3055 3056 0 +3058 3044 3052 2365 3056 0 +-3058 -3057 0 +-3058 -3044 0 +-3058 -3052 0 +-3058 -2365 0 +-3058 -3056 0 +3051 3053 3054 3055 3058 0 +-2366 0 +3059 -3060 2996 3052 0 +-3059 -3061 0 +-3059 -3062 0 +-3059 3060 0 +-3059 -2996 0 +-3059 -3052 0 +3063 -3064 -2996 0 +-3063 -3065 0 +-3063 3064 0 +-3063 2996 0 +3059 3063 0 +-2367 0 +3066 -1466 2939 2375 0 +-3066 1466 0 +-3066 -2939 0 +-3066 -2375 0 +3067 -1466 -2939 -2375 0 +-3067 1466 0 +-3067 2939 0 +-3067 2375 0 +3068 1466 -2939 2375 0 +-3068 -1466 0 +-3068 2939 0 +-3068 -2375 0 +3069 1466 2939 -2375 -3070 0 +-3069 -3071 0 +-3069 -1466 0 +-3069 -2939 0 +-3069 2375 0 +-3069 3070 0 +3072 1466 2939 2375 3070 0 +-3072 -3071 0 +-3072 -1466 0 +-3072 -2939 0 +-3072 -2375 0 +-3072 -3070 0 +3066 3067 3068 3069 3072 0 +-2376 0 +3073 -210 2979 2927 0 +-3073 210 0 +-3073 -2979 0 +-3073 -2927 0 +3074 -210 -2979 -2927 0 +-3074 210 0 +-3074 2979 0 +-3074 2927 0 +3075 210 -2979 2927 0 +-3075 -210 0 +-3075 2979 0 +-3075 -2927 0 +3076 210 2979 -2927 -3077 0 +-3076 -3078 0 +-3076 -210 0 +-3076 -2979 0 +-3076 2927 0 +-3076 3077 0 +3079 210 2979 2927 3077 0 +-3079 -3078 0 +-3079 -210 0 +-3079 -2979 0 +-3079 -2927 0 +-3079 -3077 0 +3073 3074 3075 3076 3079 0 +-2928 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1961 1965 1966 1976 2098 2182 2206 2213 2221 2370 2383 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 0 +3080 -276 2002 2382 0 +-3080 276 0 +-3080 -2002 0 +-3080 -2382 0 +3081 -276 -2002 -2382 0 +-3081 276 0 +-3081 2002 0 +-3081 2382 0 +3082 276 -2002 2382 0 +-3082 -276 0 +-3082 2002 0 +-3082 -2382 0 +3083 276 2002 -2382 -3084 0 +-3083 -3085 0 +-3083 -276 0 +-3083 -2002 0 +-3083 2382 0 +-3083 3084 0 +3086 276 2002 2382 3084 0 +-3086 -3085 0 +-3086 -276 0 +-3086 -2002 0 +-3086 -2382 0 +-3086 -3084 0 +3080 3081 3082 3083 3086 0 +-2383 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1961 1965 1966 1976 2098 2182 2206 2213 2221 2370 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 0 +3087 -2909 3088 1960 0 +-3087 2909 0 +-3087 -3088 0 +-3087 -1960 0 +3089 -2909 -3088 -1960 0 +-3089 2909 0 +-3089 3088 0 +-3089 1960 0 +3090 2909 -3088 1960 0 +-3090 -2909 0 +-3090 3088 0 +-3090 -1960 0 +3091 2909 3088 -1960 -3092 0 +-3091 -3093 0 +-3091 -2909 0 +-3091 -3088 0 +-3091 1960 0 +-3091 3092 0 +3094 2909 3088 1960 3092 0 +-3094 -3093 0 +-3094 -2909 0 +-3094 -3088 0 +-3094 -1960 0 +-3094 -3092 0 +3087 3089 3090 3091 3094 0 +-1961 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1965 1966 1976 2098 2182 2206 2213 2221 2370 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 0 +3095 -2871 2483 2181 0 +-3095 2871 0 +-3095 -2483 0 +-3095 -2181 0 +3096 -2871 -2483 -2181 0 +-3096 2871 0 +-3096 2483 0 +-3096 2181 0 +3097 2871 -2483 2181 0 +-3097 -2871 0 +-3097 2483 0 +-3097 -2181 0 +3098 2871 2483 -2181 -3099 0 +-3098 -3100 0 +-3098 -2871 0 +-3098 -2483 0 +-3098 2181 0 +-3098 3099 0 +3101 2871 2483 2181 3099 0 +-3101 -3100 0 +-3101 -2871 0 +-3101 -2483 0 +-3101 -2181 0 +-3101 -3099 0 +3095 3096 3097 3098 3101 0 +-2182 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1965 1966 1976 2098 2206 2213 2221 2370 2390 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 0 +3102 -1923 1877 2389 0 +-3102 1923 0 +-3102 -1877 0 +-3102 -2389 0 +3103 -1923 -1877 -2389 0 +-3103 1923 0 +-3103 1877 0 +-3103 2389 0 +3104 1923 -1877 2389 0 +-3104 -1923 0 +-3104 1877 0 +-3104 -2389 0 +3105 1923 1877 -2389 -3106 0 +-3105 -3107 0 +-3105 -1923 0 +-3105 -1877 0 +-3105 2389 0 +-3105 3106 0 +3108 1923 1877 2389 3106 0 +-3108 -3107 0 +-3108 -1923 0 +-3108 -1877 0 +-3108 -2389 0 +-3108 -3106 0 +3102 3103 3104 3105 3108 0 +-2390 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1965 1966 1976 2098 2206 2213 2221 2370 2398 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 0 +3109 -2841 3110 2397 0 +-3109 2841 0 +-3109 -3110 0 +-3109 -2397 0 +3111 -2841 -3110 -2397 0 +-3111 2841 0 +-3111 3110 0 +-3111 2397 0 +3112 2841 -3110 2397 0 +-3112 -2841 0 +-3112 3110 0 +-3112 -2397 0 +3113 2841 3110 -2397 -3114 0 +-3113 -3115 0 +-3113 -2841 0 +-3113 -3110 0 +-3113 2397 0 +-3113 3114 0 +3116 2841 3110 2397 3114 0 +-3116 -3115 0 +-3116 -2841 0 +-3116 -3110 0 +-3116 -2397 0 +-3116 -3114 0 +3109 3111 3112 3113 3116 0 +-2398 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1890 1965 1966 1976 2098 2206 2213 2221 2370 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 0 +3117 -1264 3118 1889 0 +-3117 1264 0 +-3117 -3118 0 +-3117 -1889 0 +3119 -1264 -3118 -1889 0 +-3119 1264 0 +-3119 3118 0 +-3119 1889 0 +3120 1264 -3118 1889 0 +-3120 -1264 0 +-3120 3118 0 +-3120 -1889 0 +3121 1264 3118 -1889 -3122 0 +-3121 -3123 0 +-3121 -1264 0 +-3121 -3118 0 +-3121 1889 0 +-3121 3122 0 +3124 1264 3118 1889 3122 0 +-3124 -3123 0 +-3124 -1264 0 +-3124 -3118 0 +-3124 -1889 0 +-3124 -3122 0 +3117 3119 3120 3121 3124 0 +-1890 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1965 1966 1976 2098 2206 2213 2221 2370 2406 2413 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 0 +3125 -2825 2840 2412 0 +-3125 2825 0 +-3125 -2840 0 +-3125 -2412 0 +3126 -2825 -2840 -2412 0 +-3126 2825 0 +-3126 2840 0 +-3126 2412 0 +3127 2825 -2840 2412 0 +-3127 -2825 0 +-3127 2840 0 +-3127 -2412 0 +3128 2825 2840 -2412 -3129 0 +-3128 -3130 0 +-3128 -2825 0 +-3128 -2840 0 +-3128 2412 0 +-3128 3129 0 +3131 2825 2840 2412 3129 0 +-3131 -3130 0 +-3131 -2825 0 +-3131 -2840 0 +-3131 -2412 0 +-3131 -3129 0 +3125 3126 3127 3128 3131 0 +-2413 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1965 1966 1976 2098 2206 2213 2221 2370 2406 2421 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 0 +3132 -2825 3133 2420 0 +-3132 2825 0 +-3132 -3133 0 +-3132 -2420 0 +3134 -2825 -3133 -2420 0 +-3134 2825 0 +-3134 3133 0 +-3134 2420 0 +3135 2825 -3133 2420 0 +-3135 -2825 0 +-3135 3133 0 +-3135 -2420 0 +3136 2825 3133 -2420 -3137 0 +-3136 -3138 0 +-3136 -2825 0 +-3136 -3133 0 +-3136 2420 0 +-3136 3137 0 +3139 2825 3133 2420 3137 0 +-3139 -3138 0 +-3139 -2825 0 +-3139 -3133 0 +-3139 -2420 0 +-3139 -3137 0 +3132 3134 3135 3136 3139 0 +-2421 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1965 1966 1976 2098 2206 2213 2221 2370 2406 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 0 +3140 -1666 2594 2205 0 +-3140 1666 0 +-3140 -2594 0 +-3140 -2205 0 +3141 -1666 -2594 -2205 0 +-3141 1666 0 +-3141 2594 0 +-3141 2205 0 +3142 1666 -2594 2205 0 +-3142 -1666 0 +-3142 2594 0 +-3142 -2205 0 +3143 1666 2594 -2205 -3144 0 +-3143 -3145 0 +-3143 -1666 0 +-3143 -2594 0 +-3143 2205 0 +-3143 3144 0 +3146 1666 2594 2205 3144 0 +-3146 -3145 0 +-3146 -1666 0 +-3146 -2594 0 +-3146 -2205 0 +-3146 -3144 0 +3140 3141 3142 3143 3146 0 +-2206 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1965 1966 1976 2098 2213 2221 2370 2406 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 0 +3147 -3088 3148 1964 0 +-3147 3088 0 +-3147 -3148 0 +-3147 -1964 0 +3149 -3088 -3148 -1964 0 +-3149 3088 0 +-3149 3148 0 +-3149 1964 0 +3150 3088 -3148 1964 0 +-3150 -3088 0 +-3150 3148 0 +-3150 -1964 0 +3151 3088 3148 -1964 -3152 0 +-3151 -3153 0 +-3151 -3088 0 +-3151 -3148 0 +-3151 1964 0 +-3151 3152 0 +3154 3088 3148 1964 3152 0 +-3154 -3153 0 +-3154 -3088 0 +-3154 -3148 0 +-3154 -1964 0 +-3154 -3152 0 +3147 3149 3150 3151 3154 0 +-1965 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2436 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 0 +3155 -3133 3156 2435 0 +-3155 3133 0 +-3155 -3156 0 +-3155 -2435 0 +3157 -3133 -3156 -2435 0 +-3157 3133 0 +-3157 3156 0 +-3157 2435 0 +3158 3133 -3156 2435 0 +-3158 -3133 0 +-3158 3156 0 +-3158 -2435 0 +3159 3133 3156 -2435 -3160 0 +-3159 -3161 0 +-3159 -3133 0 +-3159 -3156 0 +-3159 2435 0 +-3159 3160 0 +3162 3133 3156 2435 3160 0 +-3162 -3161 0 +-3162 -3133 0 +-3162 -3156 0 +-3162 -2435 0 +-3162 -3160 0 +3155 3157 3158 3159 3162 0 +-2436 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2443 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 0 +3163 -1265 2460 2442 0 +-3163 1265 0 +-3163 -2460 0 +-3163 -2442 0 +3164 -1265 -2460 -2442 0 +-3164 1265 0 +-3164 2460 0 +-3164 2442 0 +3165 1265 -2460 2442 0 +-3165 -1265 0 +-3165 2460 0 +-3165 -2442 0 +3166 1265 2460 -2442 -3167 0 +-3166 -3168 0 +-3166 -1265 0 +-3166 -2460 0 +-3166 2442 0 +-3166 3167 0 +3169 1265 2460 2442 3167 0 +-3169 -3168 0 +-3169 -1265 0 +-3169 -2460 0 +-3169 -2442 0 +-3169 -3167 0 +3163 3164 3165 3166 3169 0 +-2443 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2450 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 0 +3170 -1201 2555 2449 0 +-3170 1201 0 +-3170 -2555 0 +-3170 -2449 0 +3171 -1201 -2555 -2449 0 +-3171 1201 0 +-3171 2555 0 +-3171 2449 0 +3172 1201 -2555 2449 0 +-3172 -1201 0 +-3172 2555 0 +-3172 -2449 0 +3173 1201 2555 -2449 -3174 0 +-3173 -3175 0 +-3173 -1201 0 +-3173 -2555 0 +-3173 2449 0 +-3173 3174 0 +3176 1201 2555 2449 3174 0 +-3176 -3175 0 +-3176 -1201 0 +-3176 -2555 0 +-3176 -2449 0 +-3176 -3174 0 +3170 3171 3172 3173 3176 0 +-2450 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2457 2465 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 0 +3177 -2393 3178 2464 0 +-3177 2393 0 +-3177 -3178 0 +-3177 -2464 0 +3179 -2393 -3178 -2464 0 +-3179 2393 0 +-3179 3178 0 +-3179 2464 0 +3180 2393 -3178 2464 0 +-3180 -2393 0 +-3180 3178 0 +-3180 -2464 0 +3181 2393 3178 -2464 -3182 0 +-3181 -3183 0 +-3181 -2393 0 +-3181 -3178 0 +-3181 2464 0 +-3181 3182 0 +3184 2393 3178 2464 3182 0 +-3184 -3183 0 +-3184 -2393 0 +-3184 -3178 0 +-3184 -2464 0 +-3184 -3182 0 +3177 3179 3180 3181 3184 0 +-2465 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2560 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 0 +3185 -3178 3186 2559 0 +-3185 3178 0 +-3185 -3186 0 +-3185 -2559 0 +3187 -3178 -3186 -2559 0 +-3187 3178 0 +-3187 3186 0 +-3187 2559 0 +3188 3178 -3186 2559 0 +-3188 -3178 0 +-3188 3186 0 +-3188 -2559 0 +3189 3178 3186 -2559 -3190 0 +-3189 -3191 0 +-3189 -3178 0 +-3189 -3186 0 +-3189 2559 0 +-3189 3190 0 +3192 3178 3186 2559 3190 0 +-3192 -3191 0 +-3192 -3178 0 +-3192 -3186 0 +-3192 -2559 0 +-3192 -3190 0 +3185 3187 3188 3189 3192 0 +-2560 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1966 1976 2098 2213 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 3191 0 +3193 -3194 2572 3148 0 +-3193 -48 0 +-3193 -49 0 +-3193 -3195 0 +-3193 -3196 0 +-3193 -46 0 +-3193 -47 0 +-3193 3194 0 +-3193 -2572 0 +-3193 -3148 0 +3197 -3198 -2572 0 +-3197 -48 0 +-3197 -49 0 +-3197 -46 0 +-3197 -47 0 +-3197 -3199 0 +-3197 3198 0 +-3197 2572 0 +3193 3197 0 +-1966 0 +a 0 +a 128 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1976 2098 2213 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 3191 3195 3196 3199 0 +3200 -3201 601 2901 0 +-3200 -49 0 +-3200 -3202 0 +-3200 -3203 0 +-3200 3201 0 +-3200 -601 0 +-3200 -2901 0 +3204 -3205 -601 0 +-3204 -49 0 +-3204 -3206 0 +-3204 3205 0 +-3204 601 0 +3200 3204 0 +-128 0 +a 0 +3207 -3208 3209 3201 0 +-3207 3208 0 +-3207 -3209 0 +-3207 -3201 0 +3210 -3208 -3209 -3201 0 +-3210 3208 0 +-3210 3209 0 +-3210 3201 0 +3211 3208 -3209 3201 0 +-3211 -3208 0 +-3211 3209 0 +-3211 -3201 0 +3212 3208 3209 -3201 -3213 0 +-3212 -3214 0 +-3212 -3208 0 +-3212 -3209 0 +-3212 3201 0 +-3212 3213 0 +3215 3208 3209 3201 3213 0 +-3215 -3214 0 +-3215 -3208 0 +-3215 -3209 0 +-3215 -3201 0 +-3215 -3213 0 +3207 3210 3211 3212 3215 0 +-3202 0 +3216 -3217 840 3209 0 +-3216 -49 0 +-3216 -3218 0 +-3216 -3219 0 +-3216 3217 0 +-3216 -840 0 +-3216 -3209 0 +3220 -3221 -840 0 +-3220 -49 0 +-3220 -3222 0 +-3220 3221 0 +-3220 840 0 +3216 3220 0 +-3203 0 +a 0 +3223 -3224 3225 3213 0 +-3223 3224 0 +-3223 -3225 0 +-3223 -3213 0 +3226 -3224 -3225 -3213 0 +-3226 3224 0 +-3226 3225 0 +-3226 3213 0 +3227 3224 -3225 3213 0 +-3227 -3224 0 +-3227 3225 0 +-3227 -3213 0 +3228 3224 3225 -3213 -3229 0 +-3228 -3230 0 +-3228 -3224 0 +-3228 -3225 0 +-3228 3213 0 +-3228 3229 0 +3231 3224 3225 3213 3229 0 +-3231 -3230 0 +-3231 -3224 0 +-3231 -3225 0 +-3231 -3213 0 +-3231 -3229 0 +3223 3226 3227 3228 3231 0 +-3214 0 +3232 -3224 3233 3217 0 +-3232 3224 0 +-3232 -3233 0 +-3232 -3217 0 +3234 -3224 -3233 -3217 0 +-3234 3224 0 +-3234 3233 0 +-3234 3217 0 +3235 3224 -3233 3217 0 +-3235 -3224 0 +-3235 3233 0 +-3235 -3217 0 +3236 3224 3233 -3217 -3237 0 +-3236 -3238 0 +-3236 -3224 0 +-3236 -3233 0 +-3236 3217 0 +-3236 3237 0 +3239 3224 3233 3217 3237 0 +-3239 -3238 0 +-3239 -3224 0 +-3239 -3233 0 +-3239 -3217 0 +-3239 -3237 0 +3232 3234 3235 3236 3239 0 +-3218 0 +3240 -3241 1119 3233 0 +-3240 -49 0 +-3240 -3242 0 +-3240 -3243 0 +-3240 3241 0 +-3240 -1119 0 +-3240 -3233 0 +3244 -3245 -1119 0 +-3244 -49 0 +-3244 -3246 0 +-3244 3245 0 +-3244 1119 0 +3240 3244 0 +-3219 0 +a 0 +3247 -3248 3249 3229 0 +-3247 3248 0 +-3247 -3249 0 +-3247 -3229 0 +3250 -3248 -3249 -3229 0 +-3250 3248 0 +-3250 3249 0 +-3250 3229 0 +3251 3248 -3249 3229 0 +-3251 -3248 0 +-3251 3249 0 +-3251 -3229 0 +3252 3248 3249 -3229 -3253 0 +-3252 -3254 0 +-3252 -3248 0 +-3252 -3249 0 +-3252 3229 0 +-3252 3253 0 +3255 3248 3249 3229 3253 0 +-3255 -3254 0 +-3255 -3248 0 +-3255 -3249 0 +-3255 -3229 0 +-3255 -3253 0 +3247 3250 3251 3252 3255 0 +-3230 0 +3256 -3249 3257 3237 0 +-3256 3249 0 +-3256 -3257 0 +-3256 -3237 0 +3258 -3249 -3257 -3237 0 +-3258 3249 0 +-3258 3257 0 +-3258 3237 0 +3259 3249 -3257 3237 0 +-3259 -3249 0 +-3259 3257 0 +-3259 -3237 0 +3260 3249 3257 -3237 -3261 0 +-3260 -3262 0 +-3260 -3249 0 +-3260 -3257 0 +-3260 3237 0 +-3260 3261 0 +3263 3249 3257 3237 3261 0 +-3263 -3262 0 +-3263 -3249 0 +-3263 -3257 0 +-3263 -3237 0 +-3263 -3261 0 +3256 3258 3259 3260 3263 0 +-3238 0 +3264 -3257 3265 3241 0 +-3264 3257 0 +-3264 -3265 0 +-3264 -3241 0 +3266 -3257 -3265 -3241 0 +-3266 3257 0 +-3266 3265 0 +-3266 3241 0 +3267 3257 -3265 3241 0 +-3267 -3257 0 +-3267 3265 0 +-3267 -3241 0 +3268 3257 3265 -3241 -3269 0 +-3268 -3270 0 +-3268 -3257 0 +-3268 -3265 0 +-3268 3241 0 +-3268 3269 0 +3271 3257 3265 3241 3269 0 +-3271 -3270 0 +-3271 -3257 0 +-3271 -3265 0 +-3271 -3241 0 +-3271 -3269 0 +3264 3266 3267 3268 3271 0 +-3242 0 +3272 -3273 1568 3265 0 +-3272 -49 0 +-3272 -3274 0 +-3272 -3275 0 +-3272 3273 0 +-3272 -1568 0 +-3272 -3265 0 +3276 -3277 -1568 0 +-3276 -49 0 +-3276 -3278 0 +-3276 3277 0 +-3276 1568 0 +3272 3276 0 +-3243 0 +a 0 +3279 -3280 3281 3253 0 +-3279 3280 0 +-3279 -3281 0 +-3279 -3253 0 +3282 -3280 -3281 -3253 0 +-3282 3280 0 +-3282 3281 0 +-3282 3253 0 +3283 3280 -3281 3253 0 +-3283 -3280 0 +-3283 3281 0 +-3283 -3253 0 +3284 3280 3281 -3253 -3285 0 +-3284 -3286 0 +-3284 -3280 0 +-3284 -3281 0 +-3284 3253 0 +-3284 3285 0 +3287 3280 3281 3253 3285 0 +-3287 -3286 0 +-3287 -3280 0 +-3287 -3281 0 +-3287 -3253 0 +-3287 -3285 0 +3279 3282 3283 3284 3287 0 +-3254 0 +3288 -3280 3289 3261 0 +-3288 3280 0 +-3288 -3289 0 +-3288 -3261 0 +3290 -3280 -3289 -3261 0 +-3290 3280 0 +-3290 3289 0 +-3290 3261 0 +3291 3280 -3289 3261 0 +-3291 -3280 0 +-3291 3289 0 +-3291 -3261 0 +3292 3280 3289 -3261 -3293 0 +-3292 -3294 0 +-3292 -3280 0 +-3292 -3289 0 +-3292 3261 0 +-3292 3293 0 +3295 3280 3289 3261 3293 0 +-3295 -3294 0 +-3295 -3280 0 +-3295 -3289 0 +-3295 -3261 0 +-3295 -3293 0 +3288 3290 3291 3292 3295 0 +-3262 0 +3296 -3289 3297 3269 0 +-3296 3289 0 +-3296 -3297 0 +-3296 -3269 0 +3298 -3289 -3297 -3269 0 +-3298 3289 0 +-3298 3297 0 +-3298 3269 0 +3299 3289 -3297 3269 0 +-3299 -3289 0 +-3299 3297 0 +-3299 -3269 0 +3300 3289 3297 -3269 -3301 0 +-3300 -3302 0 +-3300 -3289 0 +-3300 -3297 0 +-3300 3269 0 +-3300 3301 0 +3303 3289 3297 3269 3301 0 +-3303 -3302 0 +-3303 -3289 0 +-3303 -3297 0 +-3303 -3269 0 +-3303 -3301 0 +3296 3298 3299 3300 3303 0 +-3270 0 +3304 -3297 3305 3273 0 +-3304 3297 0 +-3304 -3305 0 +-3304 -3273 0 +3306 -3297 -3305 -3273 0 +-3306 3297 0 +-3306 3305 0 +-3306 3273 0 +3307 3297 -3305 3273 0 +-3307 -3297 0 +-3307 3305 0 +-3307 -3273 0 +3308 3297 3305 -3273 -3309 0 +-3308 -3310 0 +-3308 -3297 0 +-3308 -3305 0 +-3308 3273 0 +-3308 3309 0 +3311 3297 3305 3273 3309 0 +-3311 -3310 0 +-3311 -3297 0 +-3311 -3305 0 +-3311 -3273 0 +-3311 -3309 0 +3304 3306 3307 3308 3311 0 +-3274 0 +3312 -3313 2058 3305 0 +-3312 -49 0 +-3312 -3314 0 +-3312 -3315 0 +-3312 3313 0 +-3312 -2058 0 +-3312 -3305 0 +3316 -3317 -2058 0 +-3316 -49 0 +-3316 -3318 0 +-3316 3317 0 +-3316 2058 0 +3312 3316 0 +-3275 0 +a 0 +3319 -3320 3321 3285 0 +-3319 3320 0 +-3319 -3321 0 +-3319 -3285 0 +3322 -3320 -3321 -3285 0 +-3322 3320 0 +-3322 3321 0 +-3322 3285 0 +3323 3320 -3321 3285 0 +-3323 -3320 0 +-3323 3321 0 +-3323 -3285 0 +3324 3320 3321 -3285 -3325 0 +-3324 -3326 0 +-3324 -3320 0 +-3324 -3321 0 +-3324 3285 0 +-3324 3325 0 +3327 3320 3321 3285 3325 0 +-3327 -3326 0 +-3327 -3320 0 +-3327 -3321 0 +-3327 -3285 0 +-3327 -3325 0 +3319 3322 3323 3324 3327 0 +-3286 0 +3328 -3321 3329 3293 0 +-3328 3321 0 +-3328 -3329 0 +-3328 -3293 0 +3330 -3321 -3329 -3293 0 +-3330 3321 0 +-3330 3329 0 +-3330 3293 0 +3331 3321 -3329 3293 0 +-3331 -3321 0 +-3331 3329 0 +-3331 -3293 0 +3332 3321 3329 -3293 -3333 0 +-3332 -3334 0 +-3332 -3321 0 +-3332 -3329 0 +-3332 3293 0 +-3332 3333 0 +3335 3321 3329 3293 3333 0 +-3335 -3334 0 +-3335 -3321 0 +-3335 -3329 0 +-3335 -3293 0 +-3335 -3333 0 +3328 3330 3331 3332 3335 0 +-3294 0 +3336 -3329 3337 3301 0 +-3336 3329 0 +-3336 -3337 0 +-3336 -3301 0 +3338 -3329 -3337 -3301 0 +-3338 3329 0 +-3338 3337 0 +-3338 3301 0 +3339 3329 -3337 3301 0 +-3339 -3329 0 +-3339 3337 0 +-3339 -3301 0 +3340 3329 3337 -3301 -3341 0 +-3340 -3342 0 +-3340 -3329 0 +-3340 -3337 0 +-3340 3301 0 +-3340 3341 0 +3343 3329 3337 3301 3341 0 +-3343 -3342 0 +-3343 -3329 0 +-3343 -3337 0 +-3343 -3301 0 +-3343 -3341 0 +3336 3338 3339 3340 3343 0 +-3302 0 +3344 -3337 3345 3309 0 +-3344 3337 0 +-3344 -3345 0 +-3344 -3309 0 +3346 -3337 -3345 -3309 0 +-3346 3337 0 +-3346 3345 0 +-3346 3309 0 +3347 3337 -3345 3309 0 +-3347 -3337 0 +-3347 3345 0 +-3347 -3309 0 +3348 3337 3345 -3309 -3349 0 +-3348 -3350 0 +-3348 -3337 0 +-3348 -3345 0 +-3348 3309 0 +-3348 3349 0 +3351 3337 3345 3309 3349 0 +-3351 -3350 0 +-3351 -3337 0 +-3351 -3345 0 +-3351 -3309 0 +-3351 -3349 0 +3344 3346 3347 3348 3351 0 +-3310 0 +3352 -3345 3353 3313 0 +-3352 3345 0 +-3352 -3353 0 +-3352 -3313 0 +3354 -3345 -3353 -3313 0 +-3354 3345 0 +-3354 3353 0 +-3354 3313 0 +3355 3345 -3353 3313 0 +-3355 -3345 0 +-3355 3353 0 +-3355 -3313 0 +3356 3345 3353 -3313 -3357 0 +-3356 -3358 0 +-3356 -3345 0 +-3356 -3353 0 +-3356 3313 0 +-3356 3357 0 +3359 3345 3353 3313 3357 0 +-3359 -3358 0 +-3359 -3345 0 +-3359 -3353 0 +-3359 -3313 0 +-3359 -3357 0 +3352 3354 3355 3356 3359 0 +-3314 0 +3360 -3361 2651 3353 0 +-3360 -49 0 +-3360 -3362 0 +-3360 -3363 0 +-3360 3361 0 +-3360 -2651 0 +-3360 -3353 0 +3364 -3365 -2651 0 +-3364 -49 0 +-3364 -3366 0 +-3364 3365 0 +-3364 2651 0 +3360 3364 0 +-3315 0 +a 0 +a 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1976 2098 2213 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2568 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 3191 3195 3196 3199 3206 3222 3246 3278 3318 3326 3334 3342 3350 3358 3362 3363 3366 0 +3367 -3186 3368 2567 0 +-3367 3186 0 +-3367 -3368 0 +-3367 -2567 0 +3369 -3186 -3368 -2567 0 +-3369 3186 0 +-3369 3368 0 +-3369 2567 0 +3370 3186 -3368 2567 0 +-3370 -3186 0 +-3370 3368 0 +-3370 -2567 0 +3371 3186 3368 -2567 -3372 0 +-3371 -3373 0 +-3371 -3186 0 +-3371 -3368 0 +-3371 2567 0 +-3371 3372 0 +3374 3186 3368 2567 3372 0 +-3374 -3373 0 +-3374 -3186 0 +-3374 -3368 0 +-3374 -2567 0 +-3374 -3372 0 +3367 3369 3370 3371 3374 0 +-2568 0 +a 0 +a 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1976 2098 2213 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 3191 3195 3196 3199 3206 3222 3246 3278 3318 3326 3334 3342 3350 3358 3362 3363 3366 3373 0 +3375 -1211 2594 2212 0 +-3375 1211 0 +-3375 -2594 0 +-3375 -2212 0 +3376 -1211 -2594 -2212 0 +-3376 1211 0 +-3376 2594 0 +-3376 2212 0 +3377 1211 -2594 2212 0 +-3377 -1211 0 +-3377 2594 0 +-3377 -2212 0 +3378 1211 2594 -2212 -3379 0 +-3378 -3380 0 +-3378 -1211 0 +-3378 -2594 0 +-3378 2212 0 +-3378 3379 0 +3381 1211 2594 2212 3379 0 +-3381 -3380 0 +-3381 -1211 0 +-3381 -2594 0 +-3381 -2212 0 +-3381 -3379 0 +3375 3376 3377 3378 3381 0 +-2213 0 +a 0 +a 154 162 170 246 247 620 636 739 929 1002 1247 1419 1509 1832 1850 1851 1976 2098 2221 2370 2406 2428 2457 2473 2481 2497 2521 2553 2573 2574 2577 2583 2590 2599 2607 2615 2623 2631 2639 2647 2652 2653 2656 2662 2669 2676 2683 2690 2697 2704 2712 2720 2728 2736 2744 2749 2750 2753 2761 2769 2777 2785 2789 2790 2793 2799 2806 2814 2822 2830 2837 2846 2853 2861 2868 2877 2884 2891 2898 2906 2914 2921 2936 2944 2952 2960 2968 2976 2984 2992 2997 2998 3001 3009 3017 3025 3033 3041 3049 3057 3061 3062 3065 3071 3078 3085 3093 3100 3107 3115 3123 3130 3138 3145 3153 3161 3168 3175 3183 3191 3195 3196 3199 3206 3222 3246 3278 3318 3326 3334 3342 3350 3358 3362 3363 3366 3373 3380 0 +3382 -102 2401 2576 0 +-3382 102 0 +-3382 -2401 0 +-3382 -2576 0 +3383 -102 -2401 -2576 0 +-3383 102 0 +-3383 2401 0 +-3383 2576 0 +3384 102 -2401 2576 0 +-3384 -102 0 +-3384 2401 0 +-3384 -2576 0 +3385 102 2401 -2576 -3386 0 +-3385 -3387 0 +-3385 -102 0 +-3385 -2401 0 +-3385 2576 0 +-3385 3386 0 +3388 102 2401 2576 3386 0 +-3388 -3387 0 +-3388 -102 0 +-3388 -2401 0 +-3388 -2576 0 +-3388 -3386 0 +3382 3383 3384 3385 3388 0 +-2577 0 +a 0 diff --git a/tests/regression/regression2.icnf b/tests/regression/regression2.icnf new file mode 100644 index 00000000..da5e1c3d --- /dev/null +++ b/tests/regression/regression2.icnf @@ -0,0 +1,4484 @@ +p inccnf +1 0 +2 0 +-3 0 +2 0 +-4 0 +-5 0 +-6 0 +-7 0 +a 0 +8 -9 10 3 0 +-8 9 0 +-8 -10 0 +-8 -3 0 +11 -9 -10 -3 0 +-11 9 0 +-11 10 0 +-11 3 0 +12 9 -10 3 0 +-12 -9 0 +-12 10 0 +-12 -3 0 +13 9 10 -3 -14 -15 0 +-13 -16 0 +-13 -17 0 +-13 -9 0 +-13 -10 0 +-13 3 0 +-13 14 0 +-13 15 0 +18 9 10 3 14 0 +-18 -16 0 +-18 -9 0 +-18 -10 0 +-18 -3 0 +-18 -14 0 +19 9 10 3 15 0 +-19 -17 0 +-19 -9 0 +-19 -10 0 +-19 -3 0 +-19 -15 0 +8 11 12 13 18 19 0 +-4 0 +20 -21 -22 23 9 0 +-20 -24 0 +-20 -25 0 +-20 -26 0 +-20 21 0 +-20 22 0 +-20 -23 0 +-20 -9 0 +27 -28 -23 0 +-27 -29 0 +-27 28 0 +-27 23 0 +20 27 0 +-5 0 +30 -31 -32 33 10 0 +-30 -34 0 +-30 -35 0 +-30 -36 0 +-30 31 0 +-30 32 0 +-30 -33 0 +-30 -10 0 +37 -38 -33 0 +-37 -39 0 +-37 38 0 +-37 33 0 +30 37 0 +-6 0 +a 0 +a 16 17 24 25 26 29 34 35 36 39 0 +40 -33 9 28 0 +-40 33 0 +-40 -9 0 +-40 -28 0 +41 -33 -9 -28 0 +-41 33 0 +-41 9 0 +-41 28 0 +42 33 -9 28 0 +-42 -33 0 +-42 9 0 +-42 -28 0 +43 33 9 -28 -44 -45 0 +-43 -46 0 +-43 -47 0 +-43 -33 0 +-43 -9 0 +-43 28 0 +-43 44 0 +-43 45 0 +48 33 9 28 44 0 +-48 -46 0 +-48 -33 0 +-48 -9 0 +-48 -28 0 +-48 -44 0 +49 33 9 28 45 0 +-49 -47 0 +-49 -33 0 +-49 -9 0 +-49 -28 0 +-49 -45 0 +40 41 42 43 48 49 0 +-29 0 +a 0 +a 16 17 24 25 26 34 35 36 39 46 47 0 +50 -23 10 38 0 +-50 23 0 +-50 -10 0 +-50 -38 0 +51 -23 -10 -38 0 +-51 23 0 +-51 10 0 +-51 38 0 +52 23 -10 38 0 +-52 -23 0 +-52 10 0 +-52 -38 0 +53 23 10 -38 -54 -55 0 +-53 -56 0 +-53 -57 0 +-53 -23 0 +-53 -10 0 +-53 38 0 +-53 54 0 +-53 55 0 +58 23 10 38 54 0 +-58 -56 0 +-58 -23 0 +-58 -10 0 +-58 -38 0 +-58 -54 0 +59 23 10 38 55 0 +-59 -57 0 +-59 -23 0 +-59 -10 0 +-59 -38 0 +-59 -55 0 +50 51 52 53 58 59 0 +-39 0 +a 0 +a 16 17 24 25 26 34 35 36 46 47 56 57 0 +60 -61 62 15 0 +-60 61 0 +-60 -62 0 +-60 -15 0 +63 -61 -62 -15 0 +-63 61 0 +-63 62 0 +-63 15 0 +64 61 -62 15 0 +-64 -61 0 +-64 62 0 +-64 -15 0 +65 61 62 -15 -66 -67 0 +-65 -68 0 +-65 -69 0 +-65 -61 0 +-65 -62 0 +-65 15 0 +-65 66 0 +-65 67 0 +70 61 62 15 66 0 +-70 -68 0 +-70 -61 0 +-70 -62 0 +-70 -15 0 +-70 -66 0 +71 61 62 15 67 0 +-71 -69 0 +-71 -61 0 +-71 -62 0 +-71 -15 0 +-71 -67 0 +60 63 64 65 70 71 0 +-17 0 +a 0 +a 16 24 25 26 34 35 36 46 47 56 57 68 69 0 +72 -62 73 21 0 +-72 62 0 +-72 -73 0 +-72 -21 0 +74 -62 -73 -21 0 +-74 62 0 +-74 73 0 +-74 21 0 +75 62 -73 21 0 +-75 -62 0 +-75 73 0 +-75 -21 0 +76 62 73 -21 -77 -78 0 +-76 -79 0 +-76 -80 0 +-76 -62 0 +-76 -73 0 +-76 21 0 +-76 77 0 +-76 78 0 +81 62 73 21 77 0 +-81 -79 0 +-81 -62 0 +-81 -73 0 +-81 -21 0 +-81 -77 0 +82 62 73 21 78 0 +-82 -80 0 +-82 -62 0 +-82 -73 0 +-82 -21 0 +-82 -78 0 +72 74 75 76 81 82 0 +-24 0 +a 0 +a 16 25 26 34 35 36 46 47 56 57 68 69 79 80 0 +83 -84 85 14 0 +-83 84 0 +-83 -85 0 +-83 -14 0 +86 -84 -85 -14 0 +-86 84 0 +-86 85 0 +-86 14 0 +87 84 -85 14 0 +-87 -84 0 +-87 85 0 +-87 -14 0 +88 84 85 -14 -89 0 +-88 -90 0 +-88 -84 0 +-88 -85 0 +-88 14 0 +-88 89 0 +91 84 85 14 89 0 +-91 -90 0 +-91 -84 0 +-91 -85 0 +-91 -14 0 +-91 -89 0 +83 86 87 88 91 0 +-16 0 +a 0 +a 25 26 34 35 36 46 47 56 57 68 69 79 80 90 0 +92 -61 93 31 0 +-92 61 0 +-92 -93 0 +-92 -31 0 +94 -61 -93 -31 0 +-94 61 0 +-94 93 0 +-94 31 0 +95 61 -93 31 0 +-95 -61 0 +-95 93 0 +-95 -31 0 +96 61 93 -31 -97 -98 0 +-96 -99 0 +-96 -100 0 +-96 -61 0 +-96 -93 0 +-96 31 0 +-96 97 0 +-96 98 0 +101 61 93 31 97 0 +-101 -99 0 +-101 -61 0 +-101 -93 0 +-101 -31 0 +-101 -97 0 +102 61 93 31 98 0 +-102 -100 0 +-102 -61 0 +-102 -93 0 +-102 -31 0 +-102 -98 0 +92 94 95 96 101 102 0 +-34 0 +a 0 +103 -104 -105 106 93 0 +-103 -107 0 +-103 -108 0 +-103 -109 0 +-103 104 0 +-103 105 0 +-103 -106 0 +-103 -93 0 +110 -111 -106 0 +-110 -112 0 +-110 111 0 +-110 106 0 +103 110 0 +-35 0 +113 -84 114 32 0 +-113 84 0 +-113 -114 0 +-113 -32 0 +115 -84 -114 -32 0 +-115 84 0 +-115 114 0 +-115 32 0 +116 84 -114 32 0 +-116 -84 0 +-116 114 0 +-116 -32 0 +117 84 114 -32 -118 0 +-117 -119 0 +-117 -84 0 +-117 -114 0 +-117 32 0 +-117 118 0 +120 84 114 32 118 0 +-120 -119 0 +-120 -84 0 +-120 -114 0 +-120 -32 0 +-120 -118 0 +113 115 116 117 120 0 +-36 0 +a 0 +a 25 26 46 47 56 57 68 69 79 80 90 99 100 107 108 109 112 119 0 +121 -122 -123 124 73 0 +-121 -125 0 +-121 -126 0 +-121 -127 0 +-121 122 0 +-121 123 0 +-121 -124 0 +-121 -73 0 +128 -129 -124 0 +-128 -130 0 +-128 129 0 +-128 124 0 +121 128 0 +-25 0 +a 0 +a 26 46 47 56 57 68 69 79 80 90 99 100 107 108 109 112 119 125 126 127 130 0 +131 -85 114 44 0 +-131 85 0 +-131 -114 0 +-131 -44 0 +132 -85 -114 -44 0 +-132 85 0 +-132 114 0 +-132 44 0 +133 85 -114 44 0 +-133 -85 0 +-133 114 0 +-133 -44 0 +134 85 114 -44 -135 0 +-134 -136 0 +-134 -85 0 +-134 -114 0 +-134 44 0 +-134 135 0 +137 85 114 44 135 0 +-137 -136 0 +-137 -85 0 +-137 -114 0 +-137 -44 0 +-137 -135 0 +131 132 133 134 137 0 +-46 0 +a 0 +a 26 47 56 57 68 69 79 80 90 99 100 107 108 109 112 119 125 126 127 130 136 0 +138 -62 106 45 0 +-138 62 0 +-138 -106 0 +-138 -45 0 +139 -62 -106 -45 0 +-139 62 0 +-139 106 0 +-139 45 0 +140 62 -106 45 0 +-140 -62 0 +-140 106 0 +-140 -45 0 +141 62 106 -45 -142 -143 0 +-141 -144 0 +-141 -145 0 +-141 -62 0 +-141 -106 0 +-141 45 0 +-141 142 0 +-141 143 0 +146 62 106 45 142 0 +-146 -144 0 +-146 -62 0 +-146 -106 0 +-146 -45 0 +-146 -142 0 +147 62 106 45 143 0 +-147 -145 0 +-147 -62 0 +-147 -106 0 +-147 -45 0 +-147 -143 0 +138 139 140 141 146 147 0 +-47 0 +a 0 +a 26 56 57 68 69 79 80 90 99 100 107 108 109 112 119 125 126 127 130 136 144 145 0 +148 -23 93 111 0 +-148 23 0 +-148 -93 0 +-148 -111 0 +149 -23 -93 -111 0 +-149 23 0 +-149 93 0 +-149 111 0 +150 23 -93 111 0 +-150 -23 0 +-150 93 0 +-150 -111 0 +151 23 93 -111 -152 -153 0 +-151 -154 0 +-151 -155 0 +-151 -23 0 +-151 -93 0 +-151 111 0 +-151 152 0 +-151 153 0 +156 23 93 111 152 0 +-156 -154 0 +-156 -23 0 +-156 -93 0 +-156 -111 0 +-156 -152 0 +157 23 93 111 153 0 +-157 -155 0 +-157 -23 0 +-157 -93 0 +-157 -111 0 +-157 -153 0 +148 149 150 151 156 157 0 +-112 0 +a 0 +a 26 56 57 68 69 79 80 90 99 100 107 108 109 119 125 126 127 130 136 144 145 154 155 0 +158 -159 160 89 0 +-158 159 0 +-158 -160 0 +-158 -89 0 +161 -159 -160 -89 0 +-161 159 0 +-161 160 0 +-161 89 0 +162 159 -160 89 0 +-162 -159 0 +-162 160 0 +-162 -89 0 +163 159 160 -89 -164 0 +-163 -165 0 +-163 -159 0 +-163 -160 0 +-163 89 0 +-163 164 0 +166 159 160 89 164 0 +-166 -165 0 +-166 -159 0 +-166 -160 0 +-166 -89 0 +-166 -164 0 +158 161 162 163 166 0 +-90 0 +a 0 +167 -160 168 118 0 +-167 160 0 +-167 -168 0 +-167 -118 0 +169 -160 -168 -118 0 +-169 160 0 +-169 168 0 +-169 118 0 +170 160 -168 118 0 +-170 -160 0 +-170 168 0 +-170 -118 0 +171 160 168 -118 -172 0 +-171 -173 0 +-171 -160 0 +-171 -168 0 +-171 118 0 +-171 172 0 +174 160 168 118 172 0 +-174 -173 0 +-174 -160 0 +-174 -168 0 +-174 -118 0 +-174 -172 0 +167 169 170 171 174 0 +-119 0 +a 0 +a 26 56 57 68 69 79 80 99 100 107 108 109 125 126 127 130 136 144 145 154 155 165 173 0 +175 -33 73 129 0 +-175 33 0 +-175 -73 0 +-175 -129 0 +176 -33 -73 -129 0 +-176 33 0 +-176 73 0 +-176 129 0 +177 33 -73 129 0 +-177 -33 0 +-177 73 0 +-177 -129 0 +178 33 73 -129 -179 -180 0 +-178 -181 0 +-178 -182 0 +-178 -33 0 +-178 -73 0 +-178 129 0 +-178 179 0 +-178 180 0 +183 33 73 129 179 0 +-183 -181 0 +-183 -33 0 +-183 -73 0 +-183 -129 0 +-183 -179 0 +184 33 73 129 180 0 +-184 -182 0 +-184 -33 0 +-184 -73 0 +-184 -129 0 +-184 -180 0 +175 176 177 178 183 184 0 +-130 0 +a 0 +a 26 56 57 68 69 79 80 99 100 107 108 109 125 126 127 136 144 145 154 155 165 173 181 182 0 +185 -85 186 22 0 +-185 85 0 +-185 -186 0 +-185 -22 0 +187 -85 -186 -22 0 +-187 85 0 +-187 186 0 +-187 22 0 +188 85 -186 22 0 +-188 -85 0 +-188 186 0 +-188 -22 0 +189 85 186 -22 -190 0 +-189 -191 0 +-189 -85 0 +-189 -186 0 +-189 22 0 +-189 190 0 +192 85 186 22 190 0 +-192 -191 0 +-192 -85 0 +-192 -186 0 +-192 -22 0 +-192 -190 0 +185 187 188 189 192 0 +-26 0 +a 0 +a 56 57 68 69 79 80 99 100 107 108 109 125 126 127 136 144 145 154 155 165 173 181 182 191 0 +193 -159 168 135 0 +-193 159 0 +-193 -168 0 +-193 -135 0 +194 -159 -168 -135 0 +-194 159 0 +-194 168 0 +-194 135 0 +195 159 -168 135 0 +-195 -159 0 +-195 168 0 +-195 -135 0 +196 159 168 -135 -197 0 +-196 -198 0 +-196 -159 0 +-196 -168 0 +-196 135 0 +-196 197 0 +199 159 168 135 197 0 +-199 -198 0 +-199 -159 0 +-199 -168 0 +-199 -135 0 +-199 -197 0 +193 194 195 196 199 0 +-136 0 +a 0 +a 56 57 68 69 79 80 99 100 107 108 109 125 126 127 144 145 154 155 165 173 181 182 191 198 0 +200 -201 202 122 0 +-200 201 0 +-200 -202 0 +-200 -122 0 +203 -201 -202 -122 0 +-203 201 0 +-203 202 0 +-203 122 0 +204 201 -202 122 0 +-204 -201 0 +-204 202 0 +-204 -122 0 +205 201 202 -122 -206 -207 0 +-205 -208 0 +-205 -209 0 +-205 -201 0 +-205 -202 0 +-205 122 0 +-205 206 0 +-205 207 0 +210 201 202 122 206 0 +-210 -208 0 +-210 -201 0 +-210 -202 0 +-210 -122 0 +-210 -206 0 +211 201 202 122 207 0 +-211 -209 0 +-211 -201 0 +-211 -202 0 +-211 -122 0 +-211 -207 0 +200 203 204 205 210 211 0 +-125 0 +a 0 +212 -213 214 164 0 +-212 213 0 +-212 -214 0 +-212 -164 0 +215 -213 -214 -164 0 +-215 213 0 +-215 214 0 +-215 164 0 +216 213 -214 164 0 +-216 -213 0 +-216 214 0 +-216 -164 0 +217 213 214 -164 -218 0 +-217 -219 0 +-217 -213 0 +-217 -214 0 +-217 164 0 +-217 218 0 +220 213 214 164 218 0 +-220 -219 0 +-220 -213 0 +-220 -214 0 +-220 -164 0 +-220 -218 0 +212 215 216 217 220 0 +-165 0 +221 -213 222 172 0 +-221 213 0 +-221 -222 0 +-221 -172 0 +223 -213 -222 -172 0 +-223 213 0 +-223 222 0 +-223 172 0 +224 213 -222 172 0 +-224 -213 0 +-224 222 0 +-224 -172 0 +225 213 222 -172 -226 0 +-225 -227 0 +-225 -213 0 +-225 -222 0 +-225 172 0 +-225 226 0 +228 213 222 172 226 0 +-228 -227 0 +-228 -213 0 +-228 -222 0 +-228 -172 0 +-228 -226 0 +221 223 224 225 228 0 +-173 0 +a 0 +a 56 57 68 69 79 80 99 100 107 108 109 126 127 144 145 154 155 181 182 191 198 208 209 219 227 0 +229 -114 230 179 0 +-229 114 0 +-229 -230 0 +-229 -179 0 +231 -114 -230 -179 0 +-231 114 0 +-231 230 0 +-231 179 0 +232 114 -230 179 0 +-232 -114 0 +-232 230 0 +-232 -179 0 +233 114 230 -179 -234 0 +-233 -235 0 +-233 -114 0 +-233 -230 0 +-233 179 0 +-233 234 0 +236 114 230 179 234 0 +-236 -235 0 +-236 -114 0 +-236 -230 0 +-236 -179 0 +-236 -234 0 +229 231 232 233 236 0 +-181 0 +a 0 +a 56 57 68 69 79 80 99 100 107 108 109 126 127 144 145 154 155 182 191 198 208 209 219 227 235 0 +237 -238 239 97 0 +-237 238 0 +-237 -239 0 +-237 -97 0 +240 -238 -239 -97 0 +-240 238 0 +-240 239 0 +-240 97 0 +241 238 -239 97 0 +-241 -238 0 +-241 239 0 +-241 -97 0 +242 238 239 -97 -243 0 +-242 -244 0 +-242 -238 0 +-242 -239 0 +-242 97 0 +-242 243 0 +245 238 239 97 243 0 +-245 -244 0 +-245 -238 0 +-245 -239 0 +-245 -97 0 +-245 -243 0 +237 240 241 242 245 0 +-99 0 +a 0 +a 56 57 68 69 79 80 100 107 108 109 126 127 144 145 154 155 182 191 198 208 209 219 227 235 244 0 +246 -84 186 54 0 +-246 84 0 +-246 -186 0 +-246 -54 0 +247 -84 -186 -54 0 +-247 84 0 +-247 186 0 +-247 54 0 +248 84 -186 54 0 +-248 -84 0 +-248 186 0 +-248 -54 0 +249 84 186 -54 -250 0 +-249 -251 0 +-249 -84 0 +-249 -186 0 +-249 54 0 +-249 250 0 +252 84 186 54 250 0 +-252 -251 0 +-252 -84 0 +-252 -186 0 +-252 -54 0 +-252 -250 0 +246 247 248 249 252 0 +-56 0 +a 0 +a 57 68 69 79 80 100 107 108 109 126 127 144 145 154 155 182 191 198 208 209 219 227 235 244 251 0 +253 -106 201 180 0 +-253 106 0 +-253 -201 0 +-253 -180 0 +254 -106 -201 -180 0 +-254 106 0 +-254 201 0 +-254 180 0 +255 106 -201 180 0 +-255 -106 0 +-255 201 0 +-255 -180 0 +256 106 201 -180 -257 -258 0 +-256 -259 0 +-256 -260 0 +-256 -106 0 +-256 -201 0 +-256 180 0 +-256 257 0 +-256 258 0 +261 106 201 180 257 0 +-261 -259 0 +-261 -106 0 +-261 -201 0 +-261 -180 0 +-261 -257 0 +262 106 201 180 258 0 +-262 -260 0 +-262 -106 0 +-262 -201 0 +-262 -180 0 +-262 -258 0 +253 254 255 256 261 262 0 +-182 0 +a 0 +a 57 68 69 79 80 100 107 108 109 126 127 144 145 154 155 191 198 208 209 219 227 235 244 251 259 260 0 +263 -61 124 55 0 +-263 61 0 +-263 -124 0 +-263 -55 0 +264 -61 -124 -55 0 +-264 61 0 +-264 124 0 +-264 55 0 +265 61 -124 55 0 +-265 -61 0 +-265 124 0 +-265 -55 0 +266 61 124 -55 -267 -268 0 +-266 -269 0 +-266 -270 0 +-266 -61 0 +-266 -124 0 +-266 55 0 +-266 267 0 +-266 268 0 +271 61 124 55 267 0 +-271 -269 0 +-271 -61 0 +-271 -124 0 +-271 -55 0 +-271 -267 0 +272 61 124 55 268 0 +-272 -270 0 +-272 -61 0 +-272 -124 0 +-272 -55 0 +-272 -268 0 +263 264 265 266 271 272 0 +-57 0 +a 0 +a 68 69 79 80 100 107 108 109 126 127 144 145 154 155 191 198 208 209 219 227 235 244 251 259 260 269 270 0 +273 -214 222 197 0 +-273 214 0 +-273 -222 0 +-273 -197 0 +274 -214 -222 -197 0 +-274 214 0 +-274 222 0 +-274 197 0 +275 214 -222 197 0 +-275 -214 0 +-275 222 0 +-275 -197 0 +276 214 222 -197 -277 0 +-276 -278 0 +-276 -214 0 +-276 -222 0 +-276 197 0 +-276 277 0 +279 214 222 197 277 0 +-279 -278 0 +-279 -214 0 +-279 -222 0 +-279 -197 0 +-279 -277 0 +273 274 275 276 279 0 +-198 0 +a 0 +a 68 69 79 80 100 107 108 109 126 127 144 145 154 155 191 208 209 219 227 235 244 251 259 260 269 270 278 0 +280 -281 -282 283 202 0 +-280 -284 0 +-280 -285 0 +-280 -286 0 +-280 281 0 +-280 282 0 +-280 -283 0 +-280 -202 0 +287 -288 -283 0 +-287 -289 0 +-287 288 0 +-287 283 0 +280 287 0 +-126 0 +a 0 +290 -291 292 218 0 +-290 291 0 +-290 -292 0 +-290 -218 0 +293 -291 -292 -218 0 +-293 291 0 +-293 292 0 +-293 218 0 +294 291 -292 218 0 +-294 -291 0 +-294 292 0 +-294 -218 0 +295 291 292 -218 -296 0 +-295 -297 0 +-295 -291 0 +-295 -292 0 +-295 218 0 +-295 296 0 +298 291 292 218 296 0 +-298 -297 0 +-298 -291 0 +-298 -292 0 +-298 -218 0 +-298 -296 0 +290 293 294 295 298 0 +-219 0 +299 -292 300 226 0 +-299 292 0 +-299 -300 0 +-299 -226 0 +301 -292 -300 -226 0 +-301 292 0 +-301 300 0 +-301 226 0 +302 292 -300 226 0 +-302 -292 0 +-302 300 0 +-302 -226 0 +303 292 300 -226 -304 0 +-303 -305 0 +-303 -292 0 +-303 -300 0 +-303 226 0 +-303 304 0 +306 292 300 226 304 0 +-306 -305 0 +-306 -292 0 +-306 -300 0 +-306 -226 0 +-306 -304 0 +299 301 302 303 306 0 +-227 0 +a 0 +a 68 69 79 80 100 107 108 109 127 144 145 154 155 191 208 209 235 244 251 259 260 269 270 278 284 285 286 289 297 305 0 +307 -168 308 234 0 +-307 168 0 +-307 -308 0 +-307 -234 0 +309 -168 -308 -234 0 +-309 168 0 +-309 308 0 +-309 234 0 +310 168 -308 234 0 +-310 -168 0 +-310 308 0 +-310 -234 0 +311 168 308 -234 -312 0 +-311 -313 0 +-311 -168 0 +-311 -308 0 +-311 234 0 +-311 312 0 +314 168 308 234 312 0 +-314 -313 0 +-314 -168 0 +-314 -308 0 +-314 -234 0 +-314 -312 0 +307 309 310 311 314 0 +-235 0 +a 0 +a 68 69 79 80 100 107 108 109 127 144 145 154 155 191 208 209 244 251 259 260 269 270 278 284 285 286 289 297 305 313 0 +315 -230 316 123 0 +-315 230 0 +-315 -316 0 +-315 -123 0 +317 -230 -316 -123 0 +-317 230 0 +-317 316 0 +-317 123 0 +318 230 -316 123 0 +-318 -230 0 +-318 316 0 +-318 -123 0 +319 230 316 -123 -320 0 +-319 -321 0 +-319 -230 0 +-319 -316 0 +-319 123 0 +-319 320 0 +322 230 316 123 320 0 +-322 -321 0 +-322 -230 0 +-322 -316 0 +-322 -123 0 +-322 -320 0 +315 317 318 319 322 0 +-127 0 +a 0 +a 68 69 79 80 100 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 278 284 285 286 289 297 305 313 321 0 +323 -291 300 277 0 +-323 291 0 +-323 -300 0 +-323 -277 0 +324 -291 -300 -277 0 +-324 291 0 +-324 300 0 +-324 277 0 +325 291 -300 277 0 +-325 -291 0 +-325 300 0 +-325 -277 0 +326 291 300 -277 -327 0 +-326 -328 0 +-326 -291 0 +-326 -300 0 +-326 277 0 +-326 327 0 +329 291 300 277 327 0 +-329 -328 0 +-329 -291 0 +-329 -300 0 +-329 -277 0 +-329 -327 0 +323 324 325 326 329 0 +-278 0 +a 0 +a 68 69 79 80 100 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 284 285 286 289 297 305 313 321 328 0 +330 -33 202 288 0 +-330 33 0 +-330 -202 0 +-330 -288 0 +331 -33 -202 -288 0 +-331 33 0 +-331 202 0 +-331 288 0 +332 33 -202 288 0 +-332 -33 0 +-332 202 0 +-332 -288 0 +333 33 202 -288 -334 -335 0 +-333 -336 0 +-333 -337 0 +-333 -33 0 +-333 -202 0 +-333 288 0 +-333 334 0 +-333 335 0 +338 33 202 288 334 0 +-338 -336 0 +-338 -33 0 +-338 -202 0 +-338 -288 0 +-338 -334 0 +339 33 202 288 335 0 +-339 -337 0 +-339 -33 0 +-339 -202 0 +-339 -288 0 +-339 -335 0 +330 331 332 333 338 339 0 +-289 0 +a 0 +a 68 69 79 80 100 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 284 285 286 297 305 313 321 328 336 337 0 +340 -341 342 296 0 +-340 341 0 +-340 -342 0 +-340 -296 0 +343 -341 -342 -296 0 +-343 341 0 +-343 342 0 +-343 296 0 +344 341 -342 296 0 +-344 -341 0 +-344 342 0 +-344 -296 0 +345 341 342 -296 -346 0 +-345 -347 0 +-345 -341 0 +-345 -342 0 +-345 296 0 +-345 346 0 +348 341 342 296 346 0 +-348 -347 0 +-348 -341 0 +-348 -342 0 +-348 -296 0 +-348 -346 0 +340 343 344 345 348 0 +-297 0 +a 0 +349 -341 350 304 0 +-349 341 0 +-349 -350 0 +-349 -304 0 +351 -341 -350 -304 0 +-351 341 0 +-351 350 0 +-351 304 0 +352 341 -350 304 0 +-352 -341 0 +-352 350 0 +-352 -304 0 +353 341 350 -304 -354 0 +-353 -355 0 +-353 -341 0 +-353 -350 0 +-353 304 0 +-353 354 0 +356 341 350 304 354 0 +-356 -355 0 +-356 -341 0 +-356 -350 0 +-356 -304 0 +-356 -354 0 +349 351 352 353 356 0 +-305 0 +a 0 +a 68 69 79 80 100 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 284 285 286 313 321 328 336 337 347 355 0 +357 -358 359 98 0 +-357 358 0 +-357 -359 0 +-357 -98 0 +360 -358 -359 -98 0 +-360 358 0 +-360 359 0 +-360 98 0 +361 358 -359 98 0 +-361 -358 0 +-361 359 0 +-361 -98 0 +362 358 359 -98 -363 -364 0 +-362 -365 0 +-362 -366 0 +-362 -358 0 +-362 -359 0 +-362 98 0 +-362 363 0 +-362 364 0 +367 358 359 98 363 0 +-367 -365 0 +-367 -358 0 +-367 -359 0 +-367 -98 0 +-367 -363 0 +368 358 359 98 364 0 +-368 -366 0 +-368 -358 0 +-368 -359 0 +-368 -98 0 +-368 -364 0 +357 360 361 362 367 368 0 +-100 0 +a 0 +a 68 69 79 80 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 284 285 286 313 321 328 336 337 347 355 365 366 0 +369 -370 371 281 0 +-369 370 0 +-369 -371 0 +-369 -281 0 +372 -370 -371 -281 0 +-372 370 0 +-372 371 0 +-372 281 0 +373 370 -371 281 0 +-373 -370 0 +-373 371 0 +-373 -281 0 +374 370 371 -281 -375 -376 0 +-374 -377 0 +-374 -378 0 +-374 -370 0 +-374 -371 0 +-374 281 0 +-374 375 0 +-374 376 0 +379 370 371 281 375 0 +-379 -377 0 +-379 -370 0 +-379 -371 0 +-379 -281 0 +-379 -375 0 +380 370 371 281 376 0 +-380 -378 0 +-380 -370 0 +-380 -371 0 +-380 -281 0 +-380 -376 0 +369 372 373 374 379 380 0 +-284 0 +a 0 +a 68 69 79 80 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 285 286 313 321 328 336 337 347 355 365 366 377 378 0 +381 -222 382 312 0 +-381 222 0 +-381 -382 0 +-381 -312 0 +383 -222 -382 -312 0 +-383 222 0 +-383 382 0 +-383 312 0 +384 222 -382 312 0 +-384 -222 0 +-384 382 0 +-384 -312 0 +385 222 382 -312 -386 0 +-385 -387 0 +-385 -222 0 +-385 -382 0 +-385 312 0 +-385 386 0 +388 222 382 312 386 0 +-388 -387 0 +-388 -222 0 +-388 -382 0 +-388 -312 0 +-388 -386 0 +381 383 384 385 388 0 +-313 0 +a 0 +a 68 69 79 80 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 285 286 321 328 336 337 347 355 365 366 377 378 387 0 +389 -342 350 327 0 +-389 342 0 +-389 -350 0 +-389 -327 0 +390 -342 -350 -327 0 +-390 342 0 +-390 350 0 +-390 327 0 +391 342 -350 327 0 +-391 -342 0 +-391 350 0 +-391 -327 0 +392 342 350 -327 -393 0 +-392 -394 0 +-392 -342 0 +-392 -350 0 +-392 327 0 +-392 393 0 +395 342 350 327 393 0 +-395 -394 0 +-395 -342 0 +-395 -350 0 +-395 -327 0 +-395 -393 0 +389 390 391 392 395 0 +-328 0 +a 0 +a 68 69 79 80 107 108 109 144 145 154 155 191 208 209 244 251 259 260 269 270 285 286 321 336 337 347 355 365 366 377 378 387 394 0 +396 -186 239 152 0 +-396 186 0 +-396 -239 0 +-396 -152 0 +397 -186 -239 -152 0 +-397 186 0 +-397 239 0 +-397 152 0 +398 186 -239 152 0 +-398 -186 0 +-398 239 0 +-398 -152 0 +399 186 239 -152 -400 0 +-399 -401 0 +-399 -186 0 +-399 -239 0 +-399 152 0 +-399 400 0 +402 186 239 152 400 0 +-402 -401 0 +-402 -186 0 +-402 -239 0 +-402 -152 0 +-402 -400 0 +396 397 398 399 402 0 +-154 0 +a 0 +a 68 69 79 80 107 108 109 144 145 155 191 208 209 244 251 259 260 269 270 285 286 321 336 337 347 355 365 366 377 378 387 394 401 0 +403 -114 404 334 0 +-403 114 0 +-403 -404 0 +-403 -334 0 +405 -114 -404 -334 0 +-405 114 0 +-405 404 0 +-405 334 0 +406 114 -404 334 0 +-406 -114 0 +-406 404 0 +-406 -334 0 +407 114 404 -334 -408 0 +-407 -409 0 +-407 -114 0 +-407 -404 0 +-407 334 0 +-407 408 0 +410 114 404 334 408 0 +-410 -409 0 +-410 -114 0 +-410 -404 0 +-410 -334 0 +-410 -408 0 +403 405 406 407 410 0 +-336 0 +a 0 +a 68 69 79 80 107 108 109 144 145 155 191 208 209 244 251 259 260 269 270 285 286 321 337 347 355 365 366 377 378 387 394 401 409 0 +411 -124 359 153 0 +-411 124 0 +-411 -359 0 +-411 -153 0 +412 -124 -359 -153 0 +-412 124 0 +-412 359 0 +-412 153 0 +413 124 -359 153 0 +-413 -124 0 +-413 359 0 +-413 -153 0 +414 124 359 -153 -415 -416 0 +-414 -417 0 +-414 -418 0 +-414 -124 0 +-414 -359 0 +-414 153 0 +-414 415 0 +-414 416 0 +419 124 359 153 415 0 +-419 -417 0 +-419 -124 0 +-419 -359 0 +-419 -153 0 +-419 -415 0 +420 124 359 153 416 0 +-420 -418 0 +-420 -124 0 +-420 -359 0 +-420 -153 0 +-420 -416 0 +411 412 413 414 419 420 0 +-155 0 +a 0 +a 68 69 79 80 107 108 109 144 145 191 208 209 244 251 259 260 269 270 285 286 321 337 347 355 365 366 377 378 387 394 401 409 417 418 0 +421 -359 422 104 0 +-421 359 0 +-421 -422 0 +-421 -104 0 +423 -359 -422 -104 0 +-423 359 0 +-423 422 0 +-423 104 0 +424 359 -422 104 0 +-424 -359 0 +-424 422 0 +-424 -104 0 +425 359 422 -104 -426 -427 0 +-425 -428 0 +-425 -429 0 +-425 -359 0 +-425 -422 0 +-425 104 0 +-425 426 0 +-425 427 0 +430 359 422 104 426 0 +-430 -428 0 +-430 -359 0 +-430 -422 0 +-430 -104 0 +-430 -426 0 +431 359 422 104 427 0 +-431 -429 0 +-431 -359 0 +-431 -422 0 +-431 -104 0 +-431 -427 0 +421 423 424 425 430 431 0 +-107 0 +a 0 +a 68 69 79 80 108 109 144 145 191 208 209 244 251 259 260 269 270 285 286 321 337 347 355 365 366 377 378 387 394 401 409 417 418 428 429 0 +432 -106 370 335 0 +-432 106 0 +-432 -370 0 +-432 -335 0 +433 -106 -370 -335 0 +-433 106 0 +-433 370 0 +-433 335 0 +434 106 -370 335 0 +-434 -106 0 +-434 370 0 +-434 -335 0 +435 106 370 -335 -436 -437 0 +-435 -438 0 +-435 -439 0 +-435 -106 0 +-435 -370 0 +-435 335 0 +-435 436 0 +-435 437 0 +440 106 370 335 436 0 +-440 -438 0 +-440 -106 0 +-440 -370 0 +-440 -335 0 +-440 -436 0 +441 106 370 335 437 0 +-441 -439 0 +-441 -106 0 +-441 -370 0 +-441 -335 0 +-441 -437 0 +432 433 434 435 440 441 0 +-337 0 +a 0 +a 68 69 79 80 108 109 144 145 191 208 209 244 251 259 260 269 270 285 286 321 347 355 365 366 377 378 387 394 401 409 417 418 428 429 438 439 0 +442 -443 -444 445 422 0 +-442 -446 0 +-442 -447 0 +-442 -448 0 +-442 443 0 +-442 444 0 +-442 -445 0 +-442 -422 0 +449 -450 -445 0 +-449 -451 0 +-449 450 0 +-449 445 0 +442 449 0 +-108 0 +a 0 +452 -453 454 346 0 +-452 453 0 +-452 -454 0 +-452 -346 0 +455 -453 -454 -346 0 +-455 453 0 +-455 454 0 +-455 346 0 +456 453 -454 346 0 +-456 -453 0 +-456 454 0 +-456 -346 0 +457 453 454 -346 -458 0 +-457 -459 0 +-457 -453 0 +-457 -454 0 +-457 346 0 +-457 458 0 +460 453 454 346 458 0 +-460 -459 0 +-460 -453 0 +-460 -454 0 +-460 -346 0 +-460 -458 0 +452 455 456 457 460 0 +-347 0 +461 -454 462 354 0 +-461 454 0 +-461 -462 0 +-461 -354 0 +463 -454 -462 -354 0 +-463 454 0 +-463 462 0 +-463 354 0 +464 454 -462 354 0 +-464 -454 0 +-464 462 0 +-464 -354 0 +465 454 462 -354 -466 0 +-465 -467 0 +-465 -454 0 +-465 -462 0 +-465 354 0 +-465 466 0 +468 454 462 354 466 0 +-468 -467 0 +-468 -454 0 +-468 -462 0 +-468 -354 0 +-468 -466 0 +461 463 464 465 468 0 +-355 0 +a 0 +a 68 69 79 80 109 144 145 191 208 209 244 251 259 260 269 270 285 286 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 0 +469 -470 -471 472 371 0 +-469 -473 0 +-469 -474 0 +-469 -475 0 +-469 470 0 +-469 471 0 +-469 -472 0 +-469 -371 0 +476 -477 -472 0 +-476 -478 0 +-476 477 0 +-476 472 0 +469 476 0 +-285 0 +a 0 +a 68 69 79 80 109 144 145 191 208 209 244 251 259 260 269 270 286 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 0 +479 -239 480 105 0 +-479 239 0 +-479 -480 0 +-479 -105 0 +481 -239 -480 -105 0 +-481 239 0 +-481 480 0 +-481 105 0 +482 239 -480 105 0 +-482 -239 0 +-482 480 0 +-482 -105 0 +483 239 480 -105 -484 0 +-483 -485 0 +-483 -239 0 +-483 -480 0 +-483 105 0 +-483 484 0 +486 239 480 105 484 0 +-486 -485 0 +-486 -239 0 +-486 -480 0 +-486 -105 0 +-486 -484 0 +479 481 482 483 486 0 +-109 0 +a 0 +a 68 69 79 80 144 145 191 208 209 244 251 259 260 269 270 286 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 0 +487 -404 488 282 0 +-487 404 0 +-487 -488 0 +-487 -282 0 +489 -404 -488 -282 0 +-489 404 0 +-489 488 0 +-489 282 0 +490 404 -488 282 0 +-490 -404 0 +-490 488 0 +-490 -282 0 +491 404 488 -282 -492 0 +-491 -493 0 +-491 -404 0 +-491 -488 0 +-491 282 0 +-491 492 0 +494 404 488 282 492 0 +-494 -493 0 +-494 -404 0 +-494 -488 0 +-494 -282 0 +-494 -492 0 +487 489 490 491 494 0 +-286 0 +a 0 +a 68 69 79 80 144 145 191 208 209 244 251 259 260 269 270 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 493 0 +495 -496 238 66 0 +-495 496 0 +-495 -238 0 +-495 -66 0 +497 -496 -238 -66 0 +-497 496 0 +-497 238 0 +-497 66 0 +498 496 -238 66 0 +-498 -496 0 +-498 238 0 +-498 -66 0 +499 496 238 -66 -500 0 +-499 -501 0 +-499 -496 0 +-499 -238 0 +-499 66 0 +-499 500 0 +502 496 238 66 500 0 +-502 -501 0 +-502 -496 0 +-502 -238 0 +-502 -66 0 +-502 -500 0 +495 497 498 499 502 0 +-68 0 +a 0 +a 69 79 80 144 145 191 208 209 244 251 259 260 269 270 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 493 501 0 +503 -159 504 190 0 +-503 159 0 +-503 -504 0 +-503 -190 0 +505 -159 -504 -190 0 +-505 159 0 +-505 504 0 +-505 190 0 +506 159 -504 190 0 +-506 -159 0 +-506 504 0 +-506 -190 0 +507 159 504 -190 -508 0 +-507 -509 0 +-507 -159 0 +-507 -504 0 +-507 190 0 +-507 508 0 +510 159 504 190 508 0 +-510 -509 0 +-510 -159 0 +-510 -504 0 +-510 -190 0 +-510 -508 0 +503 505 506 507 510 0 +-191 0 +a 0 +a 69 79 80 144 145 208 209 244 251 259 260 269 270 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 493 501 509 0 +511 -496 480 142 0 +-511 496 0 +-511 -480 0 +-511 -142 0 +512 -496 -480 -142 0 +-512 496 0 +-512 480 0 +-512 142 0 +513 496 -480 142 0 +-513 -496 0 +-513 480 0 +-513 -142 0 +514 496 480 -142 -515 0 +-514 -516 0 +-514 -496 0 +-514 -480 0 +-514 142 0 +-514 515 0 +517 496 480 142 515 0 +-517 -516 0 +-517 -496 0 +-517 -480 0 +-517 -142 0 +-517 -515 0 +511 512 513 514 517 0 +-144 0 +a 0 +a 69 79 80 145 208 209 244 251 259 260 269 270 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 493 501 509 516 0 +518 -519 445 143 0 +-518 519 0 +-518 -445 0 +-518 -143 0 +520 -519 -445 -143 0 +-520 519 0 +-520 445 0 +-520 143 0 +521 519 -445 143 0 +-521 -519 0 +-521 445 0 +-521 -143 0 +522 519 445 -143 -523 -524 0 +-522 -525 0 +-522 -526 0 +-522 -519 0 +-522 -445 0 +-522 143 0 +-522 523 0 +-522 524 0 +527 519 445 143 523 0 +-527 -525 0 +-527 -519 0 +-527 -445 0 +-527 -143 0 +-527 -523 0 +528 519 445 143 524 0 +-528 -526 0 +-528 -519 0 +-528 -445 0 +-528 -143 0 +-528 -524 0 +518 520 521 522 527 528 0 +-145 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 387 394 401 409 417 418 428 429 438 439 446 447 448 451 459 467 473 474 475 478 485 493 501 509 516 525 526 0 +529 -453 462 393 0 +-529 453 0 +-529 -462 0 +-529 -393 0 +530 -453 -462 -393 0 +-530 453 0 +-530 462 0 +-530 393 0 +531 453 -462 393 0 +-531 -453 0 +-531 462 0 +-531 -393 0 +532 453 462 -393 -533 0 +-532 -534 0 +-532 -453 0 +-532 -462 0 +-532 393 0 +-532 533 0 +535 453 462 393 533 0 +-535 -534 0 +-535 -453 0 +-535 -462 0 +-535 -393 0 +-535 -533 0 +529 530 531 532 535 0 +-394 0 +a 0 +536 -537 538 458 0 +-536 537 0 +-536 -538 0 +-536 -458 0 +539 -537 -538 -458 0 +-539 537 0 +-539 538 0 +-539 458 0 +540 537 -538 458 0 +-540 -537 0 +-540 538 0 +-540 -458 0 +541 537 538 -458 -542 0 +-541 -543 0 +-541 -537 0 +-541 -538 0 +-541 458 0 +-541 542 0 +544 537 538 458 542 0 +-544 -543 0 +-544 -537 0 +-544 -538 0 +-544 -458 0 +-544 -542 0 +536 539 540 541 544 0 +-459 0 +545 -537 546 466 0 +-545 537 0 +-545 -546 0 +-545 -466 0 +547 -537 -546 -466 0 +-547 537 0 +-547 546 0 +-547 466 0 +548 537 -546 466 0 +-548 -537 0 +-548 546 0 +-548 -466 0 +549 537 546 -466 -550 0 +-549 -551 0 +-549 -537 0 +-549 -546 0 +-549 466 0 +-549 550 0 +552 537 546 466 550 0 +-552 -551 0 +-552 -537 0 +-552 -546 0 +-552 -466 0 +-552 -550 0 +545 547 548 549 552 0 +-467 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 387 401 409 417 418 428 429 438 439 446 447 448 451 473 474 475 478 485 493 501 509 516 525 526 534 543 551 0 +553 -23 422 450 0 +-553 23 0 +-553 -422 0 +-553 -450 0 +554 -23 -422 -450 0 +-554 23 0 +-554 422 0 +-554 450 0 +555 23 -422 450 0 +-555 -23 0 +-555 422 0 +-555 -450 0 +556 23 422 -450 -557 -558 0 +-556 -559 0 +-556 -560 0 +-556 -23 0 +-556 -422 0 +-556 450 0 +-556 557 0 +-556 558 0 +561 23 422 450 557 0 +-561 -559 0 +-561 -23 0 +-561 -422 0 +-561 -450 0 +-561 -557 0 +562 23 422 450 558 0 +-562 -560 0 +-562 -23 0 +-562 -422 0 +-562 -450 0 +-562 -558 0 +553 554 555 556 561 562 0 +-451 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 387 401 409 417 418 428 429 438 439 446 447 448 473 474 475 478 485 493 501 509 516 525 526 534 543 551 559 560 0 +563 -33 371 477 0 +-563 33 0 +-563 -371 0 +-563 -477 0 +564 -33 -371 -477 0 +-564 33 0 +-564 371 0 +-564 477 0 +565 33 -371 477 0 +-565 -33 0 +-565 371 0 +-565 -477 0 +566 33 371 -477 -567 -568 0 +-566 -569 0 +-566 -570 0 +-566 -33 0 +-566 -371 0 +-566 477 0 +-566 567 0 +-566 568 0 +571 33 371 477 567 0 +-571 -569 0 +-571 -33 0 +-571 -371 0 +-571 -477 0 +-571 -567 0 +572 33 371 477 568 0 +-572 -570 0 +-572 -33 0 +-572 -371 0 +-572 -477 0 +-572 -568 0 +563 564 565 566 571 572 0 +-478 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 387 401 409 417 418 428 429 438 439 446 447 448 473 474 475 485 493 501 509 516 525 526 534 543 551 559 560 569 570 0 +573 -300 574 386 0 +-573 300 0 +-573 -574 0 +-573 -386 0 +575 -300 -574 -386 0 +-575 300 0 +-575 574 0 +-575 386 0 +576 300 -574 386 0 +-576 -300 0 +-576 574 0 +-576 -386 0 +577 300 574 -386 -578 0 +-577 -579 0 +-577 -300 0 +-577 -574 0 +-577 386 0 +-577 578 0 +580 300 574 386 578 0 +-580 -579 0 +-580 -300 0 +-580 -574 0 +-580 -386 0 +-580 -578 0 +573 575 576 577 580 0 +-387 0 +a 0 +581 -582 583 470 0 +-581 582 0 +-581 -583 0 +-581 -470 0 +584 -582 -583 -470 0 +-584 582 0 +-584 583 0 +-584 470 0 +585 582 -583 470 0 +-585 -582 0 +-585 583 0 +-585 -470 0 +586 582 583 -470 -587 -588 0 +-586 -589 0 +-586 -590 0 +-586 -582 0 +-586 -583 0 +-586 470 0 +-586 587 0 +-586 588 0 +591 582 583 470 587 0 +-591 -589 0 +-591 -582 0 +-591 -583 0 +-591 -470 0 +-591 -587 0 +592 582 583 470 588 0 +-592 -590 0 +-592 -582 0 +-592 -583 0 +-592 -470 0 +-592 -588 0 +581 584 585 586 591 592 0 +-473 0 +593 -594 -595 596 583 0 +-593 -597 0 +-593 -598 0 +-593 -599 0 +-593 594 0 +-593 595 0 +-593 -596 0 +-593 -583 0 +600 -601 -596 0 +-600 -602 0 +-600 601 0 +-600 596 0 +593 600 0 +-474 0 +603 -604 605 471 0 +-603 604 0 +-603 -605 0 +-603 -471 0 +606 -604 -605 -471 0 +-606 604 0 +-606 605 0 +-606 471 0 +607 604 -605 471 0 +-607 -604 0 +-607 605 0 +-607 -471 0 +608 604 605 -471 -609 0 +-608 -610 0 +-608 -604 0 +-608 -605 0 +-608 471 0 +-608 609 0 +611 604 605 471 609 0 +-611 -610 0 +-611 -604 0 +-611 -605 0 +-611 -471 0 +-611 -609 0 +603 606 607 608 611 0 +-475 0 +612 -106 582 568 0 +-612 106 0 +-612 -582 0 +-612 -568 0 +613 -106 -582 -568 0 +-613 106 0 +-613 582 0 +-613 568 0 +614 106 -582 568 0 +-614 -106 0 +-614 582 0 +-614 -568 0 +615 106 582 -568 -616 -617 0 +-615 -618 0 +-615 -619 0 +-615 -106 0 +-615 -582 0 +-615 568 0 +-615 616 0 +-615 617 0 +620 106 582 568 616 0 +-620 -618 0 +-620 -106 0 +-620 -582 0 +-620 -568 0 +-620 -616 0 +621 106 582 568 617 0 +-621 -619 0 +-621 -106 0 +-621 -582 0 +-621 -568 0 +-621 -617 0 +612 613 614 615 620 621 0 +-570 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 401 409 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 0 +622 -168 623 408 0 +-622 168 0 +-622 -623 0 +-622 -408 0 +624 -168 -623 -408 0 +-624 168 0 +-624 623 0 +-624 408 0 +625 168 -623 408 0 +-625 -168 0 +-625 623 0 +-625 -408 0 +626 168 623 -408 -627 0 +-626 -628 0 +-626 -168 0 +-626 -623 0 +-626 408 0 +-626 627 0 +629 168 623 408 627 0 +-629 -628 0 +-629 -168 0 +-629 -623 0 +-629 -408 0 +-629 -627 0 +622 624 625 626 629 0 +-409 0 +a 0 +a 69 79 80 208 209 244 251 259 260 269 270 321 365 366 377 378 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 0 +630 -404 631 206 0 +-630 404 0 +-630 -631 0 +-630 -206 0 +632 -404 -631 -206 0 +-632 404 0 +-632 631 0 +-632 206 0 +633 404 -631 206 0 +-633 -404 0 +-633 631 0 +-633 -206 0 +634 404 631 -206 -635 0 +-634 -636 0 +-634 -404 0 +-634 -631 0 +-634 206 0 +-634 635 0 +637 404 631 206 635 0 +-637 -636 0 +-637 -404 0 +-637 -631 0 +-637 -206 0 +-637 -635 0 +630 632 633 634 637 0 +-208 0 +a 0 +a 69 79 80 209 244 251 259 260 269 270 321 365 366 377 378 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 0 +638 -370 639 207 0 +-638 370 0 +-638 -639 0 +-638 -207 0 +640 -370 -639 -207 0 +-640 370 0 +-640 639 0 +-640 207 0 +641 370 -639 207 0 +-641 -370 0 +-641 639 0 +-641 -207 0 +642 370 639 -207 -643 -644 0 +-642 -645 0 +-642 -646 0 +-642 -370 0 +-642 -639 0 +-642 207 0 +-642 643 0 +-642 644 0 +647 370 639 207 643 0 +-647 -645 0 +-647 -370 0 +-647 -639 0 +-647 -207 0 +-647 -643 0 +648 370 639 207 644 0 +-648 -646 0 +-648 -370 0 +-648 -639 0 +-648 -207 0 +-648 -644 0 +638 640 641 642 647 648 0 +-209 0 +a 0 +a 69 79 80 244 251 259 260 269 270 321 365 366 377 378 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 0 +649 -604 650 375 0 +-649 604 0 +-649 -650 0 +-649 -375 0 +651 -604 -650 -375 0 +-651 604 0 +-651 650 0 +-651 375 0 +652 604 -650 375 0 +-652 -604 0 +-652 650 0 +-652 -375 0 +653 604 650 -375 -654 0 +-653 -655 0 +-653 -604 0 +-653 -650 0 +-653 375 0 +-653 654 0 +656 604 650 375 654 0 +-656 -655 0 +-656 -604 0 +-656 -650 0 +-656 -375 0 +-656 -654 0 +649 651 652 653 656 0 +-377 0 +a 0 +a 69 79 80 244 251 259 260 269 270 321 365 366 378 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 655 0 +657 -582 658 376 0 +-657 582 0 +-657 -658 0 +-657 -376 0 +659 -582 -658 -376 0 +-659 582 0 +-659 658 0 +-659 376 0 +660 582 -658 376 0 +-660 -582 0 +-660 658 0 +-660 -376 0 +661 582 658 -376 -662 -663 0 +-661 -664 0 +-661 -665 0 +-661 -582 0 +-661 -658 0 +-661 376 0 +-661 662 0 +-661 663 0 +666 582 658 376 662 0 +-666 -664 0 +-666 -582 0 +-666 -658 0 +-666 -376 0 +-666 -662 0 +667 582 658 376 663 0 +-667 -665 0 +-667 -582 0 +-667 -658 0 +-667 -376 0 +-667 -663 0 +657 659 660 661 666 667 0 +-378 0 +a 0 +a 69 79 80 244 251 259 260 269 270 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 655 664 665 0 +668 -160 504 250 0 +-668 160 0 +-668 -504 0 +-668 -250 0 +669 -160 -504 -250 0 +-669 160 0 +-669 504 0 +-669 250 0 +670 160 -504 250 0 +-670 -160 0 +-670 504 0 +-670 -250 0 +671 160 504 -250 -672 0 +-671 -673 0 +-671 -160 0 +-671 -504 0 +-671 250 0 +-671 672 0 +674 160 504 250 672 0 +-674 -673 0 +-674 -160 0 +-674 -504 0 +-674 -250 0 +-674 -672 0 +668 669 670 671 674 0 +-251 0 +a 0 +a 69 79 80 244 259 260 269 270 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 509 516 525 526 534 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 655 664 665 673 0 +675 -538 546 533 0 +-675 538 0 +-675 -546 0 +-675 -533 0 +676 -538 -546 -533 0 +-676 538 0 +-676 546 0 +-676 533 0 +677 538 -546 533 0 +-677 -538 0 +-677 546 0 +-677 -533 0 +678 538 546 -533 -679 0 +-678 -680 0 +-678 -538 0 +-678 -546 0 +-678 533 0 +-678 679 0 +681 538 546 533 679 0 +-681 -680 0 +-681 -538 0 +-681 -546 0 +-681 -533 0 +-681 -679 0 +675 676 677 678 681 0 +-534 0 +a 0 +682 -214 683 508 0 +-682 214 0 +-682 -683 0 +-682 -508 0 +684 -214 -683 -508 0 +-684 214 0 +-684 683 0 +-684 508 0 +685 214 -683 508 0 +-685 -214 0 +-685 683 0 +-685 -508 0 +686 214 683 -508 -687 0 +-686 -688 0 +-686 -214 0 +-686 -683 0 +-686 508 0 +-686 687 0 +689 214 683 508 687 0 +-689 -688 0 +-689 -214 0 +-689 -683 0 +-689 -508 0 +-689 -687 0 +682 684 685 686 689 0 +-509 0 +a 0 +690 -291 691 687 0 +-690 291 0 +-690 -691 0 +-690 -687 0 +692 -291 -691 -687 0 +-692 291 0 +-692 691 0 +-692 687 0 +693 291 -691 687 0 +-693 -291 0 +-693 691 0 +-693 -687 0 +694 291 691 -687 -695 0 +-694 -696 0 +-694 -291 0 +-694 -691 0 +-694 687 0 +-694 695 0 +697 291 691 687 695 0 +-697 -696 0 +-697 -291 0 +-697 -691 0 +-697 -687 0 +-697 -695 0 +690 692 693 694 697 0 +-688 0 +a 0 +a 69 79 80 244 259 260 269 270 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 655 664 665 673 680 696 0 +698 -496 230 77 0 +-698 496 0 +-698 -230 0 +-698 -77 0 +699 -496 -230 -77 0 +-699 496 0 +-699 230 0 +-699 77 0 +700 496 -230 77 0 +-700 -496 0 +-700 230 0 +-700 -77 0 +701 496 230 -77 -702 0 +-701 -703 0 +-701 -496 0 +-701 -230 0 +-701 77 0 +-701 702 0 +704 496 230 77 702 0 +-704 -703 0 +-704 -496 0 +-704 -230 0 +-704 -77 0 +-704 -702 0 +698 699 700 701 704 0 +-79 0 +a 0 +705 -519 201 78 0 +-705 519 0 +-705 -201 0 +-705 -78 0 +706 -519 -201 -78 0 +-706 519 0 +-706 201 0 +-706 78 0 +707 519 -201 78 0 +-707 -519 0 +-707 201 0 +-707 -78 0 +708 519 201 -78 -709 -710 0 +-708 -711 0 +-708 -712 0 +-708 -519 0 +-708 -201 0 +-708 78 0 +-708 709 0 +-708 710 0 +713 519 201 78 709 0 +-713 -711 0 +-713 -519 0 +-713 -201 0 +-713 -78 0 +-713 -709 0 +714 519 201 78 710 0 +-714 -712 0 +-714 -519 0 +-714 -201 0 +-714 -78 0 +-714 -710 0 +705 706 707 708 713 714 0 +-80 0 +a 0 +a 69 244 259 260 269 270 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 628 636 645 646 655 664 665 673 680 696 703 711 712 0 +715 -445 639 258 0 +-715 445 0 +-715 -639 0 +-715 -258 0 +716 -445 -639 -258 0 +-716 445 0 +-716 639 0 +-716 258 0 +717 445 -639 258 0 +-717 -445 0 +-717 639 0 +-717 -258 0 +718 445 639 -258 -719 -720 0 +-718 -721 0 +-718 -722 0 +-718 -445 0 +-718 -639 0 +-718 258 0 +-718 719 0 +-718 720 0 +723 445 639 258 719 0 +-723 -721 0 +-723 -445 0 +-723 -639 0 +-723 -258 0 +-723 -719 0 +724 445 639 258 720 0 +-724 -722 0 +-724 -445 0 +-724 -639 0 +-724 -258 0 +-724 -720 0 +715 716 717 718 723 724 0 +-260 0 +a 0 +725 -222 726 627 0 +-725 222 0 +-725 -726 0 +-725 -627 0 +727 -222 -726 -627 0 +-727 222 0 +-727 726 0 +-727 627 0 +728 222 -726 627 0 +-728 -222 0 +-728 726 0 +-728 -627 0 +729 222 726 -627 -730 0 +-729 -731 0 +-729 -222 0 +-729 -726 0 +-729 627 0 +-729 730 0 +732 222 726 627 730 0 +-732 -731 0 +-732 -222 0 +-732 -726 0 +-732 -627 0 +-732 -730 0 +725 727 728 729 732 0 +-628 0 +a 0 +733 -300 734 730 0 +-733 300 0 +-733 -734 0 +-733 -730 0 +735 -300 -734 -730 0 +-735 300 0 +-735 734 0 +-735 730 0 +736 300 -734 730 0 +-736 -300 0 +-736 734 0 +-736 -730 0 +737 300 734 -730 -738 0 +-737 -739 0 +-737 -300 0 +-737 -734 0 +-737 730 0 +-737 738 0 +740 300 734 730 738 0 +-740 -739 0 +-740 -300 0 +-740 -734 0 +-740 -730 0 +-740 -738 0 +733 735 736 737 740 0 +-731 0 +a 0 +741 -350 742 738 0 +-741 350 0 +-741 -742 0 +-741 -738 0 +743 -350 -742 -738 0 +-743 350 0 +-743 742 0 +-743 738 0 +744 350 -742 738 0 +-744 -350 0 +-744 742 0 +-744 -738 0 +745 350 742 -738 -746 0 +-745 -747 0 +-745 -350 0 +-745 -742 0 +-745 738 0 +-745 746 0 +748 350 742 738 746 0 +-748 -747 0 +-748 -350 0 +-748 -742 0 +-748 -738 0 +-748 -746 0 +741 743 744 745 748 0 +-739 0 +a 0 +749 -462 750 746 0 +-749 462 0 +-749 -750 0 +-749 -746 0 +751 -462 -750 -746 0 +-751 462 0 +-751 750 0 +-751 746 0 +752 462 -750 746 0 +-752 -462 0 +-752 750 0 +-752 -746 0 +753 462 750 -746 -754 0 +-753 -755 0 +-753 -462 0 +-753 -750 0 +-753 746 0 +-753 754 0 +756 462 750 746 754 0 +-756 -755 0 +-756 -462 0 +-756 -750 0 +-756 -746 0 +-756 -754 0 +749 751 752 753 756 0 +-747 0 +a 0 +757 -546 758 754 0 +-757 546 0 +-757 -758 0 +-757 -754 0 +759 -546 -758 -754 0 +-759 546 0 +-759 758 0 +-759 754 0 +760 546 -758 754 0 +-760 -546 0 +-760 758 0 +-760 -754 0 +761 546 758 -754 -762 0 +-761 -763 0 +-761 -546 0 +-761 -758 0 +-761 754 0 +-761 762 0 +764 546 758 754 762 0 +-764 -763 0 +-764 -546 0 +-764 -758 0 +-764 -754 0 +-764 -762 0 +757 759 760 761 764 0 +-755 0 +a 0 +a 69 244 259 269 270 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 0 +765 -358 283 268 0 +-765 358 0 +-765 -283 0 +-765 -268 0 +766 -358 -283 -268 0 +-766 358 0 +-766 283 0 +-766 268 0 +767 358 -283 268 0 +-767 -358 0 +-767 283 0 +-767 -268 0 +768 358 283 -268 -769 -770 0 +-768 -771 0 +-768 -772 0 +-768 -358 0 +-768 -283 0 +-768 268 0 +-768 769 0 +-768 770 0 +773 358 283 268 769 0 +-773 -771 0 +-773 -358 0 +-773 -283 0 +-773 -268 0 +-773 -769 0 +774 358 283 268 770 0 +-774 -772 0 +-774 -358 0 +-774 -283 0 +-774 -268 0 +-774 -770 0 +765 766 767 768 773 774 0 +-270 0 +a 0 +a 69 244 259 269 321 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 0 +775 -308 776 320 0 +-775 308 0 +-775 -776 0 +-775 -320 0 +777 -308 -776 -320 0 +-777 308 0 +-777 776 0 +-777 320 0 +778 308 -776 320 0 +-778 -308 0 +-778 776 0 +-778 -320 0 +779 308 776 -320 -780 0 +-779 -781 0 +-779 -308 0 +-779 -776 0 +-779 320 0 +-779 780 0 +782 308 776 320 780 0 +-782 -781 0 +-782 -308 0 +-782 -776 0 +-782 -320 0 +-782 -780 0 +775 777 778 779 782 0 +-321 0 +a 0 +a 69 244 259 269 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 0 +783 -480 631 257 0 +-783 480 0 +-783 -631 0 +-783 -257 0 +784 -480 -631 -257 0 +-784 480 0 +-784 631 0 +-784 257 0 +785 480 -631 257 0 +-785 -480 0 +-785 631 0 +-785 -257 0 +786 480 631 -257 -787 0 +-786 -788 0 +-786 -480 0 +-786 -631 0 +-786 257 0 +-786 787 0 +789 480 631 257 787 0 +-789 -788 0 +-789 -480 0 +-789 -631 0 +-789 -257 0 +-789 -787 0 +783 784 785 786 789 0 +-259 0 +a 0 +a 69 244 269 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 0 +790 -238 316 267 0 +-790 238 0 +-790 -316 0 +-790 -267 0 +791 -238 -316 -267 0 +-791 238 0 +-791 316 0 +-791 267 0 +792 238 -316 267 0 +-792 -238 0 +-792 316 0 +-792 -267 0 +793 238 316 -267 -794 0 +-793 -795 0 +-793 -238 0 +-793 -316 0 +-793 267 0 +-793 794 0 +796 238 316 267 794 0 +-796 -795 0 +-796 -238 0 +-796 -316 0 +-796 -267 0 +-796 -794 0 +790 791 792 793 796 0 +-269 0 +a 0 +a 69 244 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 0 +797 -519 358 67 0 +-797 519 0 +-797 -358 0 +-797 -67 0 +798 -519 -358 -67 0 +-798 519 0 +-798 358 0 +-798 67 0 +799 519 -358 67 0 +-799 -519 0 +-799 358 0 +-799 -67 0 +800 519 358 -67 -801 -802 0 +-800 -803 0 +-800 -804 0 +-800 -519 0 +-800 -358 0 +-800 67 0 +-800 801 0 +-800 802 0 +805 519 358 67 801 0 +-805 -803 0 +-805 -519 0 +-805 -358 0 +-805 -67 0 +-805 -801 0 +806 519 358 67 802 0 +-806 -804 0 +-806 -519 0 +-806 -358 0 +-806 -67 0 +-806 -802 0 +797 798 799 800 805 806 0 +-69 0 +a 0 +a 244 365 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 0 +807 -808 809 363 0 +-807 808 0 +-807 -809 0 +-807 -363 0 +810 -808 -809 -363 0 +-810 808 0 +-810 809 0 +-810 363 0 +811 808 -809 363 0 +-811 -808 0 +-811 809 0 +-811 -363 0 +812 808 809 -363 -813 0 +-812 -814 0 +-812 -808 0 +-812 -809 0 +-812 363 0 +-812 813 0 +815 808 809 363 813 0 +-815 -814 0 +-815 -808 0 +-815 -809 0 +-815 -363 0 +-815 -813 0 +807 810 811 812 815 0 +-365 0 +a 0 +a 244 366 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 0 +816 -817 818 364 0 +-816 817 0 +-816 -818 0 +-816 -364 0 +819 -817 -818 -364 0 +-819 817 0 +-819 818 0 +-819 364 0 +820 817 -818 364 0 +-820 -817 0 +-820 818 0 +-820 -364 0 +821 817 818 -364 -822 -823 0 +-821 -824 0 +-821 -825 0 +-821 -817 0 +-821 -818 0 +-821 364 0 +-821 822 0 +-821 823 0 +826 817 818 364 822 0 +-826 -824 0 +-826 -817 0 +-826 -818 0 +-826 -364 0 +-826 -822 0 +827 817 818 364 823 0 +-827 -825 0 +-827 -817 0 +-827 -818 0 +-827 -364 0 +-827 -823 0 +816 819 820 821 826 827 0 +-366 0 +a 0 +a 244 401 417 418 428 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 0 +828 -808 829 426 0 +-828 808 0 +-828 -829 0 +-828 -426 0 +830 -808 -829 -426 0 +-830 808 0 +-830 829 0 +-830 426 0 +831 808 -829 426 0 +-831 -808 0 +-831 829 0 +-831 -426 0 +832 808 829 -426 -833 0 +-832 -834 0 +-832 -808 0 +-832 -829 0 +-832 426 0 +-832 833 0 +835 808 829 426 833 0 +-835 -834 0 +-835 -808 0 +-835 -829 0 +-835 -426 0 +-835 -833 0 +828 830 831 832 835 0 +-428 0 +a 0 +a 244 401 417 418 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 0 +836 -837 838 243 0 +-836 837 0 +-836 -838 0 +-836 -243 0 +839 -837 -838 -243 0 +-839 837 0 +-839 838 0 +-839 243 0 +840 837 -838 243 0 +-840 -837 0 +-840 838 0 +-840 -243 0 +841 837 838 -243 -842 0 +-841 -843 0 +-841 -837 0 +-841 -838 0 +-841 243 0 +-841 842 0 +844 837 838 243 842 0 +-844 -843 0 +-844 -837 0 +-844 -838 0 +-844 -243 0 +-844 -842 0 +836 839 840 841 844 0 +-244 0 +a 0 +a 401 417 418 429 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 0 +845 -817 846 427 0 +-845 817 0 +-845 -846 0 +-845 -427 0 +847 -817 -846 -427 0 +-847 817 0 +-847 846 0 +-847 427 0 +848 817 -846 427 0 +-848 -817 0 +-848 846 0 +-848 -427 0 +849 817 846 -427 -850 -851 0 +-849 -852 0 +-849 -853 0 +-849 -817 0 +-849 -846 0 +-849 427 0 +-849 850 0 +-849 851 0 +854 817 846 427 850 0 +-854 -852 0 +-854 -817 0 +-854 -846 0 +-854 -427 0 +-854 -850 0 +855 817 846 427 851 0 +-855 -853 0 +-855 -817 0 +-855 -846 0 +-855 -427 0 +-855 -851 0 +845 847 848 849 854 855 0 +-429 0 +a 0 +a 401 417 418 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 0 +856 -316 808 415 0 +-856 316 0 +-856 -808 0 +-856 -415 0 +857 -316 -808 -415 0 +-857 316 0 +-857 808 0 +-857 415 0 +858 316 -808 415 0 +-858 -316 0 +-858 808 0 +-858 -415 0 +859 316 808 -415 -860 0 +-859 -861 0 +-859 -316 0 +-859 -808 0 +-859 415 0 +-859 860 0 +862 316 808 415 860 0 +-862 -861 0 +-862 -316 0 +-862 -808 0 +-862 -415 0 +-862 -860 0 +856 857 858 859 862 0 +-417 0 +a 0 +a 401 418 438 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 0 +863 -480 650 436 0 +-863 480 0 +-863 -650 0 +-863 -436 0 +864 -480 -650 -436 0 +-864 480 0 +-864 650 0 +-864 436 0 +865 480 -650 436 0 +-865 -480 0 +-865 650 0 +-865 -436 0 +866 480 650 -436 -867 0 +-866 -868 0 +-866 -480 0 +-866 -650 0 +-866 436 0 +-866 867 0 +869 480 650 436 867 0 +-869 -868 0 +-869 -480 0 +-869 -650 0 +-869 -436 0 +-869 -867 0 +863 864 865 866 869 0 +-438 0 +a 0 +a 401 418 439 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 0 +870 -445 658 437 0 +-870 445 0 +-870 -658 0 +-870 -437 0 +871 -445 -658 -437 0 +-871 445 0 +-871 658 0 +-871 437 0 +872 445 -658 437 0 +-872 -445 0 +-872 658 0 +-872 -437 0 +873 445 658 -437 -874 -875 0 +-873 -876 0 +-873 -877 0 +-873 -445 0 +-873 -658 0 +-873 437 0 +-873 874 0 +-873 875 0 +878 445 658 437 874 0 +-878 -876 0 +-878 -445 0 +-878 -658 0 +-878 -437 0 +-878 -874 0 +879 445 658 437 875 0 +-879 -877 0 +-879 -445 0 +-879 -658 0 +-879 -437 0 +-879 -875 0 +870 871 872 873 878 879 0 +-439 0 +a 0 +a 401 418 446 447 448 485 493 501 516 525 526 543 551 559 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 876 877 0 +880 -186 829 557 0 +-880 186 0 +-880 -829 0 +-880 -557 0 +881 -186 -829 -557 0 +-881 186 0 +-881 829 0 +-881 557 0 +882 186 -829 557 0 +-882 -186 0 +-882 829 0 +-882 -557 0 +883 186 829 -557 -884 0 +-883 -885 0 +-883 -186 0 +-883 -829 0 +-883 557 0 +-883 884 0 +886 186 829 557 884 0 +-886 -885 0 +-886 -186 0 +-886 -829 0 +-886 -557 0 +-886 -884 0 +880 881 882 883 886 0 +-559 0 +a 0 +a 401 418 446 447 448 485 493 501 516 525 526 543 551 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 876 877 885 0 +887 -504 837 400 0 +-887 504 0 +-887 -837 0 +-887 -400 0 +888 -504 -837 -400 0 +-888 504 0 +-888 837 0 +-888 400 0 +889 504 -837 400 0 +-889 -504 0 +-889 837 0 +-889 -400 0 +890 504 837 -400 -891 0 +-890 -892 0 +-890 -504 0 +-890 -837 0 +-890 400 0 +-890 891 0 +893 504 837 400 891 0 +-893 -892 0 +-893 -504 0 +-893 -837 0 +-893 -400 0 +-893 -891 0 +887 888 889 890 893 0 +-401 0 +a 0 +a 418 446 447 448 485 493 501 516 525 526 543 551 560 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 876 877 885 892 0 +894 -124 846 558 0 +-894 124 0 +-894 -846 0 +-894 -558 0 +895 -124 -846 -558 0 +-895 124 0 +-895 846 0 +-895 558 0 +896 124 -846 558 0 +-896 -124 0 +-896 846 0 +-896 -558 0 +897 124 846 -558 -898 -899 0 +-897 -900 0 +-897 -901 0 +-897 -124 0 +-897 -846 0 +-897 558 0 +-897 898 0 +-897 899 0 +902 124 846 558 898 0 +-902 -900 0 +-902 -124 0 +-902 -846 0 +-902 -558 0 +-902 -898 0 +903 124 846 558 899 0 +-903 -901 0 +-903 -124 0 +-903 -846 0 +-903 -558 0 +-903 -899 0 +894 895 896 897 902 903 0 +-560 0 +a 0 +a 418 446 447 448 485 493 501 516 525 526 543 551 569 579 589 590 597 598 599 602 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 876 877 885 892 900 901 0 +904 -33 583 601 0 +-904 33 0 +-904 -583 0 +-904 -601 0 +905 -33 -583 -601 0 +-905 33 0 +-905 583 0 +-905 601 0 +906 33 -583 601 0 +-906 -33 0 +-906 583 0 +-906 -601 0 +907 33 583 -601 -908 -909 0 +-907 -910 0 +-907 -911 0 +-907 -33 0 +-907 -583 0 +-907 601 0 +-907 908 0 +-907 909 0 +912 33 583 601 908 0 +-912 -910 0 +-912 -33 0 +-912 -583 0 +-912 -601 0 +-912 -908 0 +913 33 583 601 909 0 +-913 -911 0 +-913 -33 0 +-913 -583 0 +-913 -601 0 +-913 -909 0 +904 905 906 907 912 913 0 +-602 0 +a 0 +a 418 446 447 448 485 493 501 516 525 526 543 551 569 579 589 590 597 598 599 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 843 852 853 861 868 876 877 885 892 900 901 910 911 0 +914 -838 915 500 0 +-914 838 0 +-914 -915 0 +-914 -500 0 +916 -838 -915 -500 0 +-916 838 0 +-916 915 0 +-916 500 0 +917 838 -915 500 0 +-917 -838 0 +-917 915 0 +-917 -500 0 +918 838 915 -500 -919 0 +-918 -920 0 +-918 -838 0 +-918 -915 0 +-918 500 0 +-918 919 0 +921 838 915 500 919 0 +-921 -920 0 +-921 -838 0 +-921 -915 0 +-921 -500 0 +-921 -919 0 +914 916 917 918 921 0 +-501 0 +a 0 +922 -923 924 587 0 +-922 923 0 +-922 -924 0 +-922 -587 0 +925 -923 -924 -587 0 +-925 923 0 +-925 924 0 +-925 587 0 +926 923 -924 587 0 +-926 -923 0 +-926 924 0 +-926 -587 0 +927 923 924 -587 -928 0 +-927 -929 0 +-927 -923 0 +-927 -924 0 +-927 587 0 +-927 928 0 +930 923 924 587 928 0 +-930 -929 0 +-930 -923 0 +-930 -924 0 +-930 -587 0 +-930 -928 0 +922 925 926 927 930 0 +-589 0 +931 -932 933 588 0 +-931 932 0 +-931 -933 0 +-931 -588 0 +934 -932 -933 -588 0 +-934 932 0 +-934 933 0 +-934 588 0 +935 932 -933 588 0 +-935 -932 0 +-935 933 0 +-935 -588 0 +936 932 933 -588 -937 -938 0 +-936 -939 0 +-936 -940 0 +-936 -932 0 +-936 -933 0 +-936 588 0 +-936 937 0 +-936 938 0 +941 932 933 588 937 0 +-941 -939 0 +-941 -932 0 +-941 -933 0 +-941 -588 0 +-941 -937 0 +942 932 933 588 938 0 +-942 -940 0 +-942 -932 0 +-942 -933 0 +-942 -588 0 +-942 -938 0 +931 934 935 936 941 942 0 +-590 0 +943 -944 945 842 0 +-943 944 0 +-943 -945 0 +-943 -842 0 +946 -944 -945 -842 0 +-946 944 0 +-946 945 0 +-946 842 0 +947 944 -945 842 0 +-947 -944 0 +-947 945 0 +-947 -842 0 +948 944 945 -842 -949 0 +-948 -950 0 +-948 -944 0 +-948 -945 0 +-948 842 0 +-948 949 0 +951 944 945 842 949 0 +-951 -950 0 +-951 -944 0 +-951 -945 0 +-951 -842 0 +-951 -949 0 +943 946 947 948 951 0 +-843 0 +952 -683 945 891 0 +-952 683 0 +-952 -945 0 +-952 -891 0 +953 -683 -945 -891 0 +-953 683 0 +-953 945 0 +-953 891 0 +954 683 -945 891 0 +-954 -683 0 +-954 945 0 +-954 -891 0 +955 683 945 -891 -956 0 +-955 -957 0 +-955 -683 0 +-955 -945 0 +-955 891 0 +-955 956 0 +958 683 945 891 956 0 +-958 -957 0 +-958 -683 0 +-958 -945 0 +-958 -891 0 +-958 -956 0 +952 953 954 955 958 0 +-892 0 +a 0 +959 -960 961 949 0 +-959 960 0 +-959 -961 0 +-959 -949 0 +962 -960 -961 -949 0 +-962 960 0 +-962 961 0 +-962 949 0 +963 960 -961 949 0 +-963 -960 0 +-963 961 0 +-963 -949 0 +964 960 961 -949 -965 0 +-964 -966 0 +-964 -960 0 +-964 -961 0 +-964 949 0 +-964 965 0 +967 960 961 949 965 0 +-967 -966 0 +-967 -960 0 +-967 -961 0 +-967 -949 0 +-967 -965 0 +959 962 963 964 967 0 +-950 0 +968 -691 960 956 0 +-968 691 0 +-968 -960 0 +-968 -956 0 +969 -691 -960 -956 0 +-969 691 0 +-969 960 0 +-969 956 0 +970 691 -960 956 0 +-970 -691 0 +-970 960 0 +-970 -956 0 +971 691 960 -956 -972 0 +-971 -973 0 +-971 -691 0 +-971 -960 0 +-971 956 0 +-971 972 0 +974 691 960 956 972 0 +-974 -973 0 +-974 -691 0 +-974 -960 0 +-974 -956 0 +-974 -972 0 +968 969 970 971 974 0 +-957 0 +a 0 +a 418 446 447 448 485 493 516 525 526 543 551 569 579 597 598 599 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 0 +975 -623 976 492 0 +-975 623 0 +-975 -976 0 +-975 -492 0 +977 -623 -976 -492 0 +-977 623 0 +-977 976 0 +-977 492 0 +978 623 -976 492 0 +-978 -623 0 +-978 976 0 +-978 -492 0 +979 623 976 -492 -980 0 +-979 -981 0 +-979 -623 0 +-979 -976 0 +-979 492 0 +-979 980 0 +982 623 976 492 980 0 +-982 -981 0 +-982 -623 0 +-982 -976 0 +-982 -492 0 +-982 -980 0 +975 977 978 979 982 0 +-493 0 +a 0 +a 418 446 447 448 485 516 525 526 543 551 569 579 597 598 599 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 0 +983 -846 984 443 0 +-983 846 0 +-983 -984 0 +-983 -443 0 +985 -846 -984 -443 0 +-985 846 0 +-985 984 0 +-985 443 0 +986 846 -984 443 0 +-986 -846 0 +-986 984 0 +-986 -443 0 +987 846 984 -443 -988 -989 0 +-987 -990 0 +-987 -991 0 +-987 -846 0 +-987 -984 0 +-987 443 0 +-987 988 0 +-987 989 0 +992 846 984 443 988 0 +-992 -990 0 +-992 -846 0 +-992 -984 0 +-992 -443 0 +-992 -988 0 +993 846 984 443 989 0 +-993 -991 0 +-993 -846 0 +-993 -984 0 +-993 -443 0 +-993 -989 0 +983 985 986 987 992 993 0 +-446 0 +a 0 +a 418 447 448 485 516 525 526 543 551 569 579 597 598 599 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 990 991 0 +994 -350 995 578 0 +-994 350 0 +-994 -995 0 +-994 -578 0 +996 -350 -995 -578 0 +-996 350 0 +-996 995 0 +-996 578 0 +997 350 -995 578 0 +-997 -350 0 +-997 995 0 +-997 -578 0 +998 350 995 -578 -999 0 +-998 -1000 0 +-998 -350 0 +-998 -995 0 +-998 578 0 +-998 999 0 +1001 350 995 578 999 0 +-1001 -1000 0 +-1001 -350 0 +-1001 -995 0 +-1001 -578 0 +-1001 -999 0 +994 996 997 998 1001 0 +-579 0 +a 0 +a 418 447 448 485 516 525 526 543 551 569 597 598 599 610 618 619 636 645 646 655 664 665 673 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 0 +1002 -1003 -1004 1005 984 0 +-1002 -1006 0 +-1002 -1007 0 +-1002 -1008 0 +-1002 1003 0 +-1002 1004 0 +-1002 -1005 0 +-1002 -984 0 +1009 -1010 -1005 0 +-1009 -1011 0 +-1009 1010 0 +-1009 1005 0 +1002 1009 0 +-447 0 +a 0 +1012 -213 683 672 0 +-1012 213 0 +-1012 -683 0 +-1012 -672 0 +1013 -213 -683 -672 0 +-1013 213 0 +-1013 683 0 +-1013 672 0 +1014 213 -683 672 0 +-1014 -213 0 +-1014 683 0 +-1014 -672 0 +1015 213 683 -672 -1016 0 +-1015 -1017 0 +-1015 -213 0 +-1015 -683 0 +-1015 672 0 +-1015 1016 0 +1018 213 683 672 1016 0 +-1018 -1017 0 +-1018 -213 0 +-1018 -683 0 +-1018 -672 0 +-1018 -1016 0 +1012 1013 1014 1015 1018 0 +-673 0 +a 0 +a 418 448 485 516 525 526 543 551 569 597 598 599 610 618 619 636 645 646 655 664 665 680 696 703 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 0 +1019 -308 915 702 0 +-1019 308 0 +-1019 -915 0 +-1019 -702 0 +1020 -308 -915 -702 0 +-1020 308 0 +-1020 915 0 +-1020 702 0 +1021 308 -915 702 0 +-1021 -308 0 +-1021 915 0 +-1021 -702 0 +1022 308 915 -702 -1023 0 +-1022 -1024 0 +-1022 -308 0 +-1022 -915 0 +-1022 702 0 +-1022 1023 0 +1025 308 915 702 1023 0 +-1025 -1024 0 +-1025 -308 0 +-1025 -915 0 +-1025 -702 0 +-1025 -1023 0 +1019 1020 1021 1022 1025 0 +-703 0 +a 0 +a 418 448 485 516 525 526 543 551 569 597 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 0 +1026 -932 1027 594 0 +-1026 932 0 +-1026 -1027 0 +-1026 -594 0 +1028 -932 -1027 -594 0 +-1028 932 0 +-1028 1027 0 +-1028 594 0 +1029 932 -1027 594 0 +-1029 -932 0 +-1029 1027 0 +-1029 -594 0 +1030 932 1027 -594 -1031 -1032 0 +-1030 -1033 0 +-1030 -1034 0 +-1030 -932 0 +-1030 -1027 0 +-1030 594 0 +-1030 1031 0 +-1030 1032 0 +1035 932 1027 594 1031 0 +-1035 -1033 0 +-1035 -932 0 +-1035 -1027 0 +-1035 -594 0 +-1035 -1031 0 +1036 932 1027 594 1032 0 +-1036 -1034 0 +-1036 -932 0 +-1036 -1027 0 +-1036 -594 0 +-1036 -1032 0 +1026 1028 1029 1030 1035 1036 0 +-597 0 +a 0 +a 418 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 885 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 0 +1037 -504 1038 884 0 +-1037 504 0 +-1037 -1038 0 +-1037 -884 0 +1039 -504 -1038 -884 0 +-1039 504 0 +-1039 1038 0 +-1039 884 0 +1040 504 -1038 884 0 +-1040 -504 0 +-1040 1038 0 +-1040 -884 0 +1041 504 1038 -884 -1042 0 +-1041 -1043 0 +-1041 -504 0 +-1041 -1038 0 +-1041 884 0 +-1041 1042 0 +1044 504 1038 884 1042 0 +-1044 -1043 0 +-1044 -504 0 +-1044 -1038 0 +-1044 -884 0 +-1044 -1042 0 +1037 1039 1040 1041 1044 0 +-885 0 +a 0 +a 418 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 0 +1045 -283 817 416 0 +-1045 283 0 +-1045 -817 0 +-1045 -416 0 +1046 -283 -817 -416 0 +-1046 283 0 +-1046 817 0 +-1046 416 0 +1047 283 -817 416 0 +-1047 -283 0 +-1047 817 0 +-1047 -416 0 +1048 283 817 -416 -1049 -1050 0 +-1048 -1051 0 +-1048 -1052 0 +-1048 -283 0 +-1048 -817 0 +-1048 416 0 +-1048 1049 0 +-1048 1050 0 +1053 283 817 416 1049 0 +-1053 -1051 0 +-1053 -283 0 +-1053 -817 0 +-1053 -416 0 +-1053 -1049 0 +1054 283 817 416 1050 0 +-1054 -1052 0 +-1054 -283 0 +-1054 -817 0 +-1054 -416 0 +-1054 -1050 0 +1045 1046 1047 1048 1053 1054 0 +-418 0 +a 0 +a 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 900 901 910 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 0 +1055 -114 923 908 0 +-1055 114 0 +-1055 -923 0 +-1055 -908 0 +1056 -114 -923 -908 0 +-1056 114 0 +-1056 923 0 +-1056 908 0 +1057 114 -923 908 0 +-1057 -114 0 +-1057 923 0 +-1057 -908 0 +1058 114 923 -908 -1059 0 +-1058 -1060 0 +-1058 -114 0 +-1058 -923 0 +-1058 908 0 +-1058 1059 0 +1061 114 923 908 1059 0 +-1061 -1060 0 +-1061 -114 0 +-1061 -923 0 +-1061 -908 0 +-1061 -1059 0 +1055 1056 1057 1058 1061 0 +-910 0 +a 0 +a 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 795 803 804 814 824 825 834 852 853 861 868 876 877 900 901 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 0 +1062 -838 776 794 0 +-1062 838 0 +-1062 -776 0 +-1062 -794 0 +1063 -838 -776 -794 0 +-1063 838 0 +-1063 776 0 +-1063 794 0 +1064 838 -776 794 0 +-1064 -838 0 +-1064 776 0 +-1064 -794 0 +1065 838 776 -794 -1066 0 +-1065 -1067 0 +-1065 -838 0 +-1065 -776 0 +-1065 794 0 +-1065 1066 0 +1068 838 776 794 1066 0 +-1068 -1067 0 +-1068 -838 0 +-1068 -776 0 +-1068 -794 0 +-1068 -1066 0 +1062 1063 1064 1065 1068 0 +-795 0 +a 0 +a 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 781 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 0 +1069 -382 1070 780 0 +-1069 382 0 +-1069 -1070 0 +-1069 -780 0 +1071 -382 -1070 -780 0 +-1071 382 0 +-1071 1070 0 +-1071 780 0 +1072 382 -1070 780 0 +-1072 -382 0 +-1072 1070 0 +-1072 -780 0 +1073 382 1070 -780 -1074 0 +-1073 -1075 0 +-1073 -382 0 +-1073 -1070 0 +-1073 780 0 +-1073 1074 0 +1076 382 1070 780 1074 0 +-1076 -1075 0 +-1076 -382 0 +-1076 -1070 0 +-1076 -780 0 +-1076 -1074 0 +1069 1071 1072 1073 1076 0 +-781 0 +a 0 +a 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 911 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 0 +1077 -106 932 909 0 +-1077 106 0 +-1077 -932 0 +-1077 -909 0 +1078 -106 -932 -909 0 +-1078 106 0 +-1078 932 0 +-1078 909 0 +1079 106 -932 909 0 +-1079 -106 0 +-1079 932 0 +-1079 -909 0 +1080 106 932 -909 -1081 -1082 0 +-1080 -1083 0 +-1080 -1084 0 +-1080 -106 0 +-1080 -932 0 +-1080 909 0 +-1080 1081 0 +-1080 1082 0 +1085 106 932 909 1081 0 +-1085 -1083 0 +-1085 -106 0 +-1085 -932 0 +-1085 -909 0 +-1085 -1081 0 +1086 106 932 909 1082 0 +-1086 -1084 0 +-1086 -106 0 +-1086 -932 0 +-1086 -909 0 +-1086 -1082 0 +1077 1078 1079 1080 1085 1086 0 +-911 0 +a 0 +a 448 485 516 525 526 543 551 569 598 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 920 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 0 +1087 -1088 -1089 1090 1027 0 +-1087 -1091 0 +-1087 -1092 0 +-1087 -1093 0 +-1087 1088 0 +-1087 1089 0 +-1087 -1090 0 +-1087 -1027 0 +1094 -1095 -1090 0 +-1094 -1096 0 +-1094 1095 0 +-1094 1090 0 +1087 1094 0 +-598 0 +a 0 +1097 -944 1098 919 0 +-1097 944 0 +-1097 -1098 0 +-1097 -919 0 +1099 -944 -1098 -919 0 +-1099 944 0 +-1099 1098 0 +-1099 919 0 +1100 944 -1098 919 0 +-1100 -944 0 +-1100 1098 0 +-1100 -919 0 +1101 944 1098 -919 -1102 0 +-1101 -1103 0 +-1101 -944 0 +-1101 -1098 0 +-1101 919 0 +-1101 1102 0 +1104 944 1098 919 1102 0 +-1104 -1103 0 +-1104 -944 0 +-1104 -1098 0 +-1104 -919 0 +-1104 -1102 0 +1097 1099 1100 1101 1104 0 +-920 0 +a 0 +a 448 485 516 525 526 543 551 569 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 929 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 0 +1105 -1106 1107 928 0 +-1105 1106 0 +-1105 -1107 0 +-1105 -928 0 +1108 -1106 -1107 -928 0 +-1108 1106 0 +-1108 1107 0 +-1108 928 0 +1109 1106 -1107 928 0 +-1109 -1106 0 +-1109 1107 0 +-1109 -928 0 +1110 1106 1107 -928 -1111 0 +-1110 -1112 0 +-1110 -1106 0 +-1110 -1107 0 +-1110 928 0 +-1110 1111 0 +1113 1106 1107 928 1111 0 +-1113 -1112 0 +-1113 -1106 0 +-1113 -1107 0 +-1113 -928 0 +-1113 -1111 0 +1105 1108 1109 1110 1113 0 +-929 0 +a 0 +a 448 485 516 525 526 543 551 569 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 0 +1114 -829 1115 444 0 +-1114 829 0 +-1114 -1115 0 +-1114 -444 0 +1116 -829 -1115 -444 0 +-1116 829 0 +-1116 1115 0 +-1116 444 0 +1117 829 -1115 444 0 +-1117 -829 0 +-1117 1115 0 +-1117 -444 0 +1118 829 1115 -444 -1119 0 +-1118 -1120 0 +-1118 -829 0 +-1118 -1115 0 +-1118 444 0 +-1118 1119 0 +1121 829 1115 444 1119 0 +-1121 -1120 0 +-1121 -829 0 +-1121 -1115 0 +-1121 -444 0 +-1121 -1119 0 +1114 1116 1117 1118 1121 0 +-448 0 +a 0 +a 485 516 525 526 543 551 569 599 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 0 +1122 -923 1123 595 0 +-1122 923 0 +-1122 -1123 0 +-1122 -595 0 +1124 -923 -1123 -595 0 +-1124 923 0 +-1124 1123 0 +-1124 595 0 +1125 923 -1123 595 0 +-1125 -923 0 +-1125 1123 0 +-1125 -595 0 +1126 923 1123 -595 -1127 0 +-1126 -1128 0 +-1126 -923 0 +-1126 -1123 0 +-1126 595 0 +-1126 1127 0 +1129 923 1123 595 1127 0 +-1129 -1128 0 +-1129 -923 0 +-1129 -1123 0 +-1129 -595 0 +-1129 -1127 0 +1122 1124 1125 1126 1129 0 +-599 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1000 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 0 +1130 -462 1131 999 0 +-1130 462 0 +-1130 -1131 0 +-1130 -999 0 +1132 -462 -1131 -999 0 +-1132 462 0 +-1132 1131 0 +-1132 999 0 +1133 462 -1131 999 0 +-1133 -462 0 +-1133 1131 0 +-1133 -999 0 +1134 462 1131 -999 -1135 0 +-1134 -1136 0 +-1134 -462 0 +-1134 -1131 0 +-1134 999 0 +-1134 1135 0 +1137 462 1131 999 1135 0 +-1137 -1136 0 +-1137 -462 0 +-1137 -1131 0 +-1137 -999 0 +-1137 -1135 0 +1130 1132 1133 1134 1137 0 +-1000 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1006 1007 1008 1011 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 0 +1138 -23 984 1010 0 +-1138 23 0 +-1138 -984 0 +-1138 -1010 0 +1139 -23 -984 -1010 0 +-1139 23 0 +-1139 984 0 +-1139 1010 0 +1140 23 -984 1010 0 +-1140 -23 0 +-1140 984 0 +-1140 -1010 0 +1141 23 984 -1010 -1142 -1143 0 +-1141 -1144 0 +-1141 -1145 0 +-1141 -23 0 +-1141 -984 0 +-1141 1010 0 +-1141 1142 0 +-1141 1143 0 +1146 23 984 1010 1142 0 +-1146 -1144 0 +-1146 -23 0 +-1146 -984 0 +-1146 -1010 0 +-1146 -1142 0 +1147 23 984 1010 1143 0 +-1147 -1145 0 +-1147 -23 0 +-1147 -984 0 +-1147 -1010 0 +-1147 -1143 0 +1138 1139 1140 1141 1146 1147 0 +-1011 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 636 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1006 1007 1008 1017 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 0 +1148 -623 1149 635 0 +-1148 623 0 +-1148 -1149 0 +-1148 -635 0 +1150 -623 -1149 -635 0 +-1150 623 0 +-1150 1149 0 +-1150 635 0 +1151 623 -1149 635 0 +-1151 -623 0 +-1151 1149 0 +-1151 -635 0 +1152 623 1149 -635 -1153 0 +-1152 -1154 0 +-1152 -623 0 +-1152 -1149 0 +-1152 635 0 +-1152 1153 0 +1155 623 1149 635 1153 0 +-1155 -1154 0 +-1155 -623 0 +-1155 -1149 0 +-1155 -635 0 +-1155 -1153 0 +1148 1150 1151 1152 1155 0 +-636 0 +a 0 +1156 -292 691 1016 0 +-1156 292 0 +-1156 -691 0 +-1156 -1016 0 +1157 -292 -691 -1016 0 +-1157 292 0 +-1157 691 0 +-1157 1016 0 +1158 292 -691 1016 0 +-1158 -292 0 +-1158 691 0 +-1158 -1016 0 +1159 292 691 -1016 -1160 0 +-1159 -1161 0 +-1159 -292 0 +-1159 -691 0 +-1159 1016 0 +-1159 1160 0 +1162 292 691 1016 1160 0 +-1162 -1161 0 +-1162 -292 0 +-1162 -691 0 +-1162 -1016 0 +-1162 -1160 0 +1156 1157 1158 1159 1162 0 +-1017 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1006 1007 1008 1024 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 0 +1163 -382 1098 1023 0 +-1163 382 0 +-1163 -1098 0 +-1163 -1023 0 +1164 -382 -1098 -1023 0 +-1164 382 0 +-1164 1098 0 +-1164 1023 0 +1165 382 -1098 1023 0 +-1165 -382 0 +-1165 1098 0 +-1165 -1023 0 +1166 382 1098 -1023 -1167 0 +-1166 -1168 0 +-1166 -382 0 +-1166 -1098 0 +-1166 1023 0 +-1166 1167 0 +1169 382 1098 1023 1167 0 +-1169 -1168 0 +-1169 -382 0 +-1169 -1098 0 +-1169 -1023 0 +-1169 -1167 0 +1163 1164 1165 1166 1169 0 +-1024 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1006 1007 1008 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 0 +1170 -1171 1172 1003 0 +-1170 1171 0 +-1170 -1172 0 +-1170 -1003 0 +1173 -1171 -1172 -1003 0 +-1173 1171 0 +-1173 1172 0 +-1173 1003 0 +1174 1171 -1172 1003 0 +-1174 -1171 0 +-1174 1172 0 +-1174 -1003 0 +1175 1171 1172 -1003 -1176 -1177 0 +-1175 -1178 0 +-1175 -1179 0 +-1175 -1171 0 +-1175 -1172 0 +-1175 1003 0 +-1175 1176 0 +-1175 1177 0 +1180 1171 1172 1003 1176 0 +-1180 -1178 0 +-1180 -1171 0 +-1180 -1172 0 +-1180 -1003 0 +-1180 -1176 0 +1181 1171 1172 1003 1177 0 +-1181 -1179 0 +-1181 -1171 0 +-1181 -1172 0 +-1181 -1003 0 +-1181 -1177 0 +1170 1173 1174 1175 1180 1181 0 +-1006 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 711 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 0 +1182 -631 1183 709 0 +-1182 631 0 +-1182 -1183 0 +-1182 -709 0 +1184 -631 -1183 -709 0 +-1184 631 0 +-1184 1183 0 +-1184 709 0 +1185 631 -1183 709 0 +-1185 -631 0 +-1185 1183 0 +-1185 -709 0 +1186 631 1183 -709 -1187 0 +-1186 -1188 0 +-1186 -631 0 +-1186 -1183 0 +-1186 709 0 +-1186 1187 0 +1189 631 1183 709 1187 0 +-1189 -1188 0 +-1189 -631 0 +-1189 -1183 0 +-1189 -709 0 +-1189 -1187 0 +1182 1184 1185 1186 1189 0 +-711 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1043 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 0 +1190 -683 1191 1042 0 +-1190 683 0 +-1190 -1191 0 +-1190 -1042 0 +1192 -683 -1191 -1042 0 +-1192 683 0 +-1192 1191 0 +-1192 1042 0 +1193 683 -1191 1042 0 +-1193 -683 0 +-1193 1191 0 +-1193 -1042 0 +1194 683 1191 -1042 -1195 0 +-1194 -1196 0 +-1194 -683 0 +-1194 -1191 0 +-1194 1042 0 +-1194 1195 0 +1197 683 1191 1042 1195 0 +-1197 -1196 0 +-1197 -683 0 +-1197 -1191 0 +-1197 -1042 0 +-1197 -1195 0 +1190 1192 1193 1194 1197 0 +-1043 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 712 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 0 +1198 -639 1199 710 0 +-1198 639 0 +-1198 -1199 0 +-1198 -710 0 +1200 -639 -1199 -710 0 +-1200 639 0 +-1200 1199 0 +-1200 710 0 +1201 639 -1199 710 0 +-1201 -639 0 +-1201 1199 0 +-1201 -710 0 +1202 639 1199 -710 -1203 -1204 0 +-1202 -1205 0 +-1202 -1206 0 +-1202 -639 0 +-1202 -1199 0 +-1202 710 0 +-1202 1203 0 +-1202 1204 0 +1207 639 1199 710 1203 0 +-1207 -1205 0 +-1207 -639 0 +-1207 -1199 0 +-1207 -710 0 +-1207 -1203 0 +1208 639 1199 710 1204 0 +-1208 -1206 0 +-1208 -639 0 +-1208 -1199 0 +-1208 -710 0 +-1208 -1204 0 +1198 1200 1201 1202 1207 1208 0 +-712 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1096 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 0 +1209 -33 1027 1095 0 +-1209 33 0 +-1209 -1027 0 +-1209 -1095 0 +1210 -33 -1027 -1095 0 +-1210 33 0 +-1210 1027 0 +-1210 1095 0 +1211 33 -1027 1095 0 +-1211 -33 0 +-1211 1027 0 +-1211 -1095 0 +1212 33 1027 -1095 -1213 -1214 0 +-1212 -1215 0 +-1212 -1216 0 +-1212 -33 0 +-1212 -1027 0 +-1212 1095 0 +-1212 1213 0 +-1212 1214 0 +1217 33 1027 1095 1213 0 +-1217 -1215 0 +-1217 -33 0 +-1217 -1027 0 +-1217 -1095 0 +-1217 -1213 0 +1218 33 1027 1095 1214 0 +-1218 -1216 0 +-1218 -33 0 +-1218 -1027 0 +-1218 -1095 0 +-1218 -1214 0 +1209 1210 1211 1212 1217 1218 0 +-1096 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1060 1067 1075 1083 1084 1091 1092 1093 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1215 1216 0 +1219 -168 1107 1059 0 +-1219 168 0 +-1219 -1107 0 +-1219 -1059 0 +1220 -168 -1107 -1059 0 +-1220 168 0 +-1220 1107 0 +-1220 1059 0 +1221 168 -1107 1059 0 +-1221 -168 0 +-1221 1107 0 +-1221 -1059 0 +1222 168 1107 -1059 -1223 0 +-1222 -1224 0 +-1222 -168 0 +-1222 -1107 0 +-1222 1059 0 +-1222 1223 0 +1225 168 1107 1059 1223 0 +-1225 -1224 0 +-1225 -168 0 +-1225 -1107 0 +-1225 -1059 0 +-1225 -1223 0 +1219 1220 1221 1222 1225 0 +-1060 0 +a 0 +1226 -114 1227 1213 0 +-1226 114 0 +-1226 -1227 0 +-1226 -1213 0 +1228 -114 -1227 -1213 0 +-1228 114 0 +-1228 1227 0 +-1228 1213 0 +1229 114 -1227 1213 0 +-1229 -114 0 +-1229 1227 0 +-1229 -1213 0 +1230 114 1227 -1213 -1231 0 +-1230 -1232 0 +-1230 -114 0 +-1230 -1227 0 +-1230 1213 0 +-1230 1231 0 +1233 114 1227 1213 1231 0 +-1233 -1232 0 +-1233 -114 0 +-1233 -1227 0 +-1233 -1213 0 +-1233 -1231 0 +1226 1228 1229 1230 1233 0 +-1215 0 +1234 -106 1235 1214 0 +-1234 106 0 +-1234 -1235 0 +-1234 -1214 0 +1236 -106 -1235 -1214 0 +-1236 106 0 +-1236 1235 0 +-1236 1214 0 +1237 106 -1235 1214 0 +-1237 -106 0 +-1237 1235 0 +-1237 -1214 0 +1238 106 1235 -1214 -1239 -1240 0 +-1238 -1241 0 +-1238 -1242 0 +-1238 -106 0 +-1238 -1235 0 +-1238 1214 0 +-1238 1239 0 +-1238 1240 0 +1243 106 1235 1214 1239 0 +-1243 -1241 0 +-1243 -106 0 +-1243 -1235 0 +-1243 -1214 0 +-1243 -1239 0 +1244 106 1235 1214 1240 0 +-1244 -1242 0 +-1244 -106 0 +-1244 -1235 0 +-1244 -1214 0 +-1244 -1240 0 +1234 1236 1237 1238 1243 1244 0 +-1216 0 +a 0 +1245 -168 1246 1231 0 +-1245 168 0 +-1245 -1246 0 +-1245 -1231 0 +1247 -168 -1246 -1231 0 +-1247 168 0 +-1247 1246 0 +-1247 1231 0 +1248 168 -1246 1231 0 +-1248 -168 0 +-1248 1246 0 +-1248 -1231 0 +1249 168 1246 -1231 -1250 0 +-1249 -1251 0 +-1249 -168 0 +-1249 -1246 0 +-1249 1231 0 +-1249 1250 0 +1252 168 1246 1231 1250 0 +-1252 -1251 0 +-1252 -168 0 +-1252 -1246 0 +-1252 -1231 0 +-1252 -1250 0 +1245 1247 1248 1249 1252 0 +-1232 0 +a 0 +1253 -222 1254 1250 0 +-1253 222 0 +-1253 -1254 0 +-1253 -1250 0 +1255 -222 -1254 -1250 0 +-1255 222 0 +-1255 1254 0 +-1255 1250 0 +1256 222 -1254 1250 0 +-1256 -222 0 +-1256 1254 0 +-1256 -1250 0 +1257 222 1254 -1250 -1258 0 +-1257 -1259 0 +-1257 -222 0 +-1257 -1254 0 +-1257 1250 0 +-1257 1258 0 +1260 222 1254 1250 1258 0 +-1260 -1259 0 +-1260 -222 0 +-1260 -1254 0 +-1260 -1250 0 +-1260 -1258 0 +1253 1255 1256 1257 1260 0 +-1251 0 +a 0 +1261 -300 1262 1258 0 +-1261 300 0 +-1261 -1262 0 +-1261 -1258 0 +1263 -300 -1262 -1258 0 +-1263 300 0 +-1263 1262 0 +-1263 1258 0 +1264 300 -1262 1258 0 +-1264 -300 0 +-1264 1262 0 +-1264 -1258 0 +1265 300 1262 -1258 -1266 0 +-1265 -1267 0 +-1265 -300 0 +-1265 -1262 0 +-1265 1258 0 +-1265 1266 0 +1268 300 1262 1258 1266 0 +-1268 -1267 0 +-1268 -300 0 +-1268 -1262 0 +-1268 -1258 0 +-1268 -1266 0 +1261 1263 1264 1265 1268 0 +-1259 0 +a 0 +1269 -350 1270 1266 0 +-1269 350 0 +-1269 -1270 0 +-1269 -1266 0 +1271 -350 -1270 -1266 0 +-1271 350 0 +-1271 1270 0 +-1271 1266 0 +1272 350 -1270 1266 0 +-1272 -350 0 +-1272 1270 0 +-1272 -1266 0 +1273 350 1270 -1266 -1274 0 +-1273 -1275 0 +-1273 -350 0 +-1273 -1270 0 +-1273 1266 0 +-1273 1274 0 +1276 350 1270 1266 1274 0 +-1276 -1275 0 +-1276 -350 0 +-1276 -1270 0 +-1276 -1266 0 +-1276 -1274 0 +1269 1271 1272 1273 1276 0 +-1267 0 +a 0 +1277 -462 1278 1274 0 +-1277 462 0 +-1277 -1278 0 +-1277 -1274 0 +1279 -462 -1278 -1274 0 +-1279 462 0 +-1279 1278 0 +-1279 1274 0 +1280 462 -1278 1274 0 +-1280 -462 0 +-1280 1278 0 +-1280 -1274 0 +1281 462 1278 -1274 -1282 0 +-1281 -1283 0 +-1281 -462 0 +-1281 -1278 0 +-1281 1274 0 +-1281 1282 0 +1284 462 1278 1274 1282 0 +-1284 -1283 0 +-1284 -462 0 +-1284 -1278 0 +-1284 -1274 0 +-1284 -1282 0 +1277 1279 1280 1281 1284 0 +-1275 0 +a 0 +1285 -546 1286 1282 0 +-1285 546 0 +-1285 -1286 0 +-1285 -1282 0 +1287 -546 -1286 -1282 0 +-1287 546 0 +-1287 1286 0 +-1287 1282 0 +1288 546 -1286 1282 0 +-1288 -546 0 +-1288 1286 0 +-1288 -1282 0 +1289 546 1286 -1282 -1290 0 +-1289 -1291 0 +-1289 -546 0 +-1289 -1286 0 +-1289 1282 0 +-1289 1290 0 +1292 546 1286 1282 1290 0 +-1292 -1291 0 +-1292 -546 0 +-1292 -1286 0 +-1292 -1282 0 +-1292 -1290 0 +1285 1287 1288 1289 1292 0 +-1283 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1091 1092 1093 1103 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 0 +1293 -961 1294 1102 0 +-1293 961 0 +-1293 -1294 0 +-1293 -1102 0 +1295 -961 -1294 -1102 0 +-1295 961 0 +-1295 1294 0 +-1295 1102 0 +1296 961 -1294 1102 0 +-1296 -961 0 +-1296 1294 0 +-1296 -1102 0 +1297 961 1294 -1102 -1298 0 +-1297 -1299 0 +-1297 -961 0 +-1297 -1294 0 +-1297 1102 0 +-1297 1298 0 +1300 961 1294 1102 1298 0 +-1300 -1299 0 +-1300 -961 0 +-1300 -1294 0 +-1300 -1102 0 +-1300 -1298 0 +1293 1295 1296 1297 1300 0 +-1103 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1091 1092 1093 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 0 +1301 -1235 1302 1088 0 +-1301 1235 0 +-1301 -1302 0 +-1301 -1088 0 +1303 -1235 -1302 -1088 0 +-1303 1235 0 +-1303 1302 0 +-1303 1088 0 +1304 1235 -1302 1088 0 +-1304 -1235 0 +-1304 1302 0 +-1304 -1088 0 +1305 1235 1302 -1088 -1306 -1307 0 +-1305 -1308 0 +-1305 -1309 0 +-1305 -1235 0 +-1305 -1302 0 +-1305 1088 0 +-1305 1306 0 +-1305 1307 0 +1310 1235 1302 1088 1306 0 +-1310 -1308 0 +-1310 -1235 0 +-1310 -1302 0 +-1310 -1088 0 +-1310 -1306 0 +1311 1235 1302 1088 1307 0 +-1311 -1309 0 +-1311 -1235 0 +-1311 -1302 0 +-1311 -1088 0 +-1311 -1307 0 +1301 1303 1304 1305 1310 1311 0 +-1091 0 +a 0 +a 485 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 0 +1312 -837 1313 484 0 +-1312 837 0 +-1312 -1313 0 +-1312 -484 0 +1314 -837 -1313 -484 0 +-1314 837 0 +-1314 1313 0 +-1314 484 0 +1315 837 -1313 484 0 +-1315 -837 0 +-1315 1313 0 +-1315 -484 0 +1316 837 1313 -484 -1317 0 +-1316 -1318 0 +-1316 -837 0 +-1316 -1313 0 +-1316 484 0 +-1316 1317 0 +1319 837 1313 484 1317 0 +-1319 -1318 0 +-1319 -837 0 +-1319 -1313 0 +-1319 -484 0 +-1319 -1317 0 +1312 1314 1315 1316 1319 0 +-485 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1112 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 0 +1320 -1321 1322 1111 0 +-1320 1321 0 +-1320 -1322 0 +-1320 -1111 0 +1323 -1321 -1322 -1111 0 +-1323 1321 0 +-1323 1322 0 +-1323 1111 0 +1324 1321 -1322 1111 0 +-1324 -1321 0 +-1324 1322 0 +-1324 -1111 0 +1325 1321 1322 -1111 -1326 0 +-1325 -1327 0 +-1325 -1321 0 +-1325 -1322 0 +-1325 1111 0 +-1325 1326 0 +1328 1321 1322 1111 1326 0 +-1328 -1327 0 +-1328 -1321 0 +-1328 -1322 0 +-1328 -1111 0 +-1328 -1326 0 +1320 1323 1324 1325 1328 0 +-1112 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1120 1128 1136 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 1327 0 +1329 -546 1330 1135 0 +-1329 546 0 +-1329 -1330 0 +-1329 -1135 0 +1331 -546 -1330 -1135 0 +-1331 546 0 +-1331 1330 0 +-1331 1135 0 +1332 546 -1330 1135 0 +-1332 -546 0 +-1332 1330 0 +-1332 -1135 0 +1333 546 1330 -1135 -1334 0 +-1333 -1335 0 +-1333 -546 0 +-1333 -1330 0 +-1333 1135 0 +-1333 1334 0 +1336 546 1330 1135 1334 0 +-1336 -1335 0 +-1336 -546 0 +-1336 -1330 0 +-1336 -1135 0 +-1336 -1334 0 +1329 1331 1332 1333 1336 0 +-1136 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 696 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1120 1128 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 1327 1335 0 +1337 -342 1338 695 0 +-1337 342 0 +-1337 -1338 0 +-1337 -695 0 +1339 -342 -1338 -695 0 +-1339 342 0 +-1339 1338 0 +-1339 695 0 +1340 342 -1338 695 0 +-1340 -342 0 +-1340 1338 0 +-1340 -695 0 +1341 342 1338 -695 -1342 0 +-1341 -1343 0 +-1341 -342 0 +-1341 -1338 0 +-1341 695 0 +-1341 1342 0 +1344 342 1338 695 1342 0 +-1344 -1343 0 +-1344 -342 0 +-1344 -1338 0 +-1344 -695 0 +-1344 -1342 0 +1337 1339 1340 1341 1344 0 +-696 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1120 1128 1144 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 1327 1335 1343 0 +1345 -186 1346 1142 0 +-1345 186 0 +-1345 -1346 0 +-1345 -1142 0 +1347 -186 -1346 -1142 0 +-1347 186 0 +-1347 1346 0 +-1347 1142 0 +1348 186 -1346 1142 0 +-1348 -186 0 +-1348 1346 0 +-1348 -1142 0 +1349 186 1346 -1142 -1350 0 +-1349 -1351 0 +-1349 -186 0 +-1349 -1346 0 +-1349 1142 0 +-1349 1350 0 +1352 186 1346 1142 1350 0 +-1352 -1351 0 +-1352 -186 0 +-1352 -1346 0 +-1352 -1142 0 +-1352 -1350 0 +1345 1347 1348 1349 1352 0 +-1144 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1120 1128 1145 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 1327 1335 1343 1351 0 +1353 -124 1171 1143 0 +-1353 124 0 +-1353 -1171 0 +-1353 -1143 0 +1354 -124 -1171 -1143 0 +-1354 124 0 +-1354 1171 0 +-1354 1143 0 +1355 124 -1171 1143 0 +-1355 -124 0 +-1355 1171 0 +-1355 -1143 0 +1356 124 1171 -1143 -1357 -1358 0 +-1356 -1359 0 +-1356 -1360 0 +-1356 -124 0 +-1356 -1171 0 +-1356 1143 0 +-1356 1357 0 +-1356 1358 0 +1361 124 1171 1143 1357 0 +-1361 -1359 0 +-1361 -124 0 +-1361 -1171 0 +-1361 -1143 0 +-1361 -1357 0 +1362 124 1171 1143 1358 0 +-1362 -1360 0 +-1362 -124 0 +-1362 -1171 0 +-1362 -1143 0 +-1362 -1358 0 +1353 1354 1355 1356 1361 1362 0 +-1145 0 +a 0 +a 516 525 526 543 551 569 610 618 619 645 646 655 664 665 680 721 722 763 771 772 788 803 804 814 824 825 834 852 853 861 868 876 877 900 901 939 940 966 973 981 990 991 1007 1008 1033 1034 1051 1052 1067 1075 1083 1084 1092 1093 1120 1128 1154 1161 1168 1178 1179 1188 1196 1205 1206 1224 1241 1242 1291 1299 1308 1309 1318 1327 1335 1343 1351 1359 1360 0 From 1136416a7c94af177678eea84b9e3ac2b6aa7694 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 18 Jan 2019 23:30:01 -0600 Subject: [PATCH 061/182] wip: remove push/pop --- src/core/Internal.ml | 304 ++++++++++++++-------------------------- src/core/Msat.ml | 4 +- src/core/Solver_intf.ml | 28 +--- 3 files changed, 113 insertions(+), 223 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 8cc01a1f..8220f87c 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -54,7 +54,6 @@ module Make(Plugin : PLUGIN) and clause = { name : int; - tag : int option; (* TODO remove *) atoms : atom array; mutable cpremise : premise; mutable activity : float; @@ -71,7 +70,6 @@ module Make(Plugin : PLUGIN) for pure SAT, [reason] is sufficient *) and premise = | Hyp - | Local | Lemma of lemma | History of clause list @@ -114,7 +112,6 @@ module Make(Plugin : PLUGIN) let name_of_clause c = match c.cpremise with | Hyp -> "H" ^ string_of_int c.name - | Local -> "L" ^ string_of_int c.name | Lemma _ -> "T" ^ string_of_int c.name | History _ -> "C" ^ string_of_int c.name @@ -333,25 +330,23 @@ module Make(Plugin : PLUGIN) let make_a = let n = ref 0 in - fun ?tag atoms premise -> + fun atoms premise -> let name = !n in incr n; { name; - tag = tag; atoms = atoms; visited = false; attached = false; activity = 0.; cpremise = premise} - let make ?tag l premise = make_a ?tag (Array.of_list l) premise + let make l premise = make_a (Array.of_list l) premise let empty = make [] (History []) let name = name_of_clause let[@inline] equal c1 c2 = c1==c2 let[@inline] atoms c = c.atoms let[@inline] atoms_l c = Array.to_list c.atoms - let[@inline] tag c = c.tag let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms let[@inline] premise c = c.cpremise @@ -377,7 +372,6 @@ module Make(Plugin : PLUGIN) let debug_premise out = function | Hyp -> Format.fprintf out "hyp" - | Local -> Format.fprintf out "local" | Lemma _ -> Format.fprintf out "th_lemma" | History v -> List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v @@ -551,8 +545,6 @@ module Make(Plugin : PLUGIN) {conclusion; step = Lemma l; } | Hyp -> { conclusion; step = Hypothesis; } - | Local -> - { conclusion; step = Assumption; } | History [] -> Log.debugf 0 (fun k -> k "Empty history for clause: %a" Clause.debug conclusion); raise (Resolution_error "Empty history") @@ -602,7 +594,7 @@ module Make(Plugin : PLUGIN) if not c.visited then ( c.visited <- true; match c.cpremise with - | Hyp | Local | Lemma _ -> aux (c :: res) acc r + | Hyp | Lemma _ -> aux (c :: res) acc r | History h -> let l = List.fold_left (fun acc c -> if not c.visited then c :: acc else acc) r h in @@ -662,8 +654,15 @@ module Make(Plugin : PLUGIN) let set_idx = Elt.set_idx end) + (* cause of "unsat", possibly conditional to local assumptions *) + type unsat_cause = + | US_local of { + core: atom list; + } + | US_false of clause (* true unsat *) + exception E_sat - exception E_unsat + exception E_unsat of unsat_cause exception UndecidedLit exception Restart exception Conflict of clause @@ -699,19 +698,13 @@ module Make(Plugin : PLUGIN) (* clauses added by the user *) clauses_learnt : clause Vec.t; (* learnt clauses (tautologies true at any time, whatever the user level) *) - (* TODO: remove, replace by vec of assumptions *) - clauses_temp : clause Vec.t; - (* Temp clauses, corresponding to the local assumptions. This vec is used - only to have an efficient way to access the list of local assumptions. *) - clauses_root : clause Stack.t; - (* Clauses that should propagate at level 0, but couldn't *) clauses_to_add : clause Stack.t; (* Clauses either assumed or pushed by the theory, waiting to be added. *) + mutable unsat_at_0: clause option; + (* conflict at level 0, if any *) - mutable unsat_conflict : clause option; - (* conflict clause at [base_level], if any *) mutable next_decision : atom option; (* When the last conflict was a semantic one, this stores the next decision to make *) @@ -723,9 +716,8 @@ module Make(Plugin : PLUGIN) th_levels : Plugin.level Vec.t; (* theory states corresponding to elt_levels *) - (* TODO: remove *) - user_levels : int Vec.t; - (* user levels in [clauses_temp] *) + mutable assumptions: atom Vec.t; + (* current assumptions *) mutable th_head : int; (* Start offset in the queue {!trail} of @@ -758,25 +750,18 @@ module Make(Plugin : PLUGIN) mutable learntsize_factor : float; (* initial limit for the number of learnt clauses, 1/3 of initial number of clauses by default *) - - (* TODO: remove *) - mutable dirty: bool; - (* is there a [pop()] on top of the stack for examining - current model/proof? *) } type solver = t (* Starting environment. *) let create_ ~st (th:theory) : t = { st; th; - unsat_conflict = None; + unsat_at_0=None; next_decision = None; clauses_hyps = Vec.create(); clauses_learnt = Vec.create(); - clauses_temp = Vec.create(); - clauses_root = Stack.create (); clauses_to_add = Stack.create (); th_head = 0; @@ -785,7 +770,7 @@ module Make(Plugin : PLUGIN) trail = Vec.create (); elt_levels = Vec.create(); th_levels = Vec.create(); - user_levels = Vec.create(); + assumptions= Vec.create(); order = H.create(); @@ -795,7 +780,6 @@ module Make(Plugin : PLUGIN) restart_first = 100; learntsize_factor = 1. /. 3. ; - dirty=false; } let create ?(size=`Big) (th:theory) : t = @@ -811,13 +795,12 @@ module Make(Plugin : PLUGIN) (* let nb_vars () = St.nb_elt () *) let[@inline] decision_level st = Vec.size st.elt_levels - let[@inline] base_level st = Vec.size st.user_levels - (* Are the assumptions currently unsat ? *) - let[@inline] is_unsat st = - match st.unsat_conflict with - | Some _ -> true - | None -> false + (* Do we have a level-0 empty clause? *) + let[@inline] check_unsat_ st = + match st.unsat_at_0 with + | Some c -> raise (E_unsat (US_false c)) + | None -> () (* Iteration over subterms. When incrementing activity, we want to be able to iterate over @@ -875,7 +858,7 @@ module Make(Plugin : PLUGIN) let l = Lit.make st.st t in insert_var_order st (E_lit l) - let make_atom_ st (p:formula) : atom = + let make_atom st (p:formula) : atom = let a = mk_atom st p in insert_var_order st (E_var a.var); a @@ -1056,7 +1039,7 @@ module Make(Plugin : PLUGIN) i.e we want to go back to the state the solver was in when decision level [lvl] was created. *) let cancel_until st lvl = - assert (lvl >= base_level st); + assert (lvl >= 0); (* Nothing to do if we try to backtrack to a non-existent level. *) if decision_level st <= lvl then ( Log.debugf debug (fun k -> k "Already at level <= %d" lvl) @@ -1111,12 +1094,22 @@ module Make(Plugin : PLUGIN) assert (Vec.size st.elt_levels = Vec.size st.th_levels); () + let pp_unsat_cause out = function + | US_local {core} -> + Format.fprintf out "false assumptions (@[core %a@])" + (Format.pp_print_list Atom.pp) core + | US_false c -> + Format.fprintf out "false %a" Clause.debug c + (* Unsatisfiability is signaled through an exception, since it can happen in multiple places (adding new clauses, or solving for instance). *) - let report_unsat st confl : _ = - Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" Clause.debug confl); - st.unsat_conflict <- Some confl; - raise E_unsat + let report_unsat st (us:unsat_cause) : _ = + Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" pp_unsat_cause us); + begin match us with + | US_false c -> st.unsat_at_0 <- Some c; + | _ -> () + end; + raise (E_unsat us) (* Simplification of boolean propagation reasons. When doing boolean propagation *at level 0*, it can happen @@ -1245,18 +1238,18 @@ module Make(Plugin : PLUGIN) and a boolean stating whether it is a UIP ("Unique Implication Point") precond: the atom list is sorted by decreasing decision level *) - let backtrack_lvl st : atom list -> int * bool = function + let backtrack_lvl _st : atom list -> int * bool = function | [] | [_] -> 0, true | a :: b :: _ -> - assert(a.var.v_level > base_level st); + assert(a.var.v_level > 0); if a.var.v_level > b.var.v_level then ( (* backtrack below [a], so we can propagate [not a] *) b.var.v_level, true ) else ( assert (a.var.v_level = b.var.v_level); - assert (a.var.v_level >= base_level st); - max (a.var.v_level - 1) (base_level st), false + assert (a.var.v_level >= 0); + max (a.var.v_level - 1) 0, false ) (* result of conflict analysis, containing the learnt clause and some @@ -1303,7 +1296,7 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k->k" Resolving clause: %a" Clause.debug clause); begin match clause.cpremise with | History _ -> clause_bump_activity st clause - | Hyp | Local | Lemma _ -> () + | Hyp | Lemma _ -> () end; history := clause :: !history; (* visit the current predecessors *) @@ -1384,9 +1377,10 @@ module Make(Plugin : PLUGIN) begin match cr.cr_learnt with | [] -> assert false | [fuip] -> - assert (cr.cr_backtrack_lvl = 0); + assert (cr.cr_backtrack_lvl = 0 && decision_level st = 0); if fuip.neg.is_true then ( - report_unsat st confl + (* incompatible at level 0 *) + report_unsat st (US_false confl) ) else ( let uclause = Clause.make cr.cr_learnt (History cr.cr_history) in Vec.push st.clauses_learnt uclause; @@ -1415,26 +1409,25 @@ module Make(Plugin : PLUGIN) let add_boolean_conflict st (confl:clause): unit = Log.debugf info (fun k -> k "Boolean conflict: %a" Clause.debug confl); st.next_decision <- None; - assert (decision_level st >= base_level st); - if decision_level st = base_level st || - Array.for_all (fun a -> a.var.v_level <= base_level st) confl.atoms then ( + assert (decision_level st >= 0); + if decision_level st = 0 || + Array.for_all (fun a -> a.var.v_level <= 0) confl.atoms then ( (* Top-level conflict *) - report_unsat st confl; + report_unsat st (US_false confl); ); let cr = analyze st confl in - cancel_until st (max cr.cr_backtrack_lvl (base_level st)); + cancel_until st (max cr.cr_backtrack_lvl 0); record_learnt_clause st confl cr (* Get the correct vector to insert a clause in. *) let clause_vector st c = match c.cpremise with | Hyp -> st.clauses_hyps - | Local -> st.clauses_temp | Lemma _ | History _ -> st.clauses_learnt (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) - let add_clause st (init:clause) : unit = + let add_clause_ st (init:clause) : unit = Log.debugf debug (fun k -> k "Adding clause: @[%a@]" Clause.debug init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) @@ -1456,29 +1449,14 @@ module Make(Plugin : PLUGIN) Log.debugf info (fun k->k "New clause: @[%a@]" Clause.debug clause); match atoms with | [] -> - (* Report_unsat will raise, and the current clause will be lost if we do not - store it somewhere. Since the proof search will end, any of env.clauses_to_add - or env.clauses_root is adequate. *) - Stack.push clause st.clauses_root; - report_unsat st clause + report_unsat st @@ US_false clause | [a] -> - cancel_until st (base_level st); + cancel_until st 0; if a.neg.is_true then ( - (* Since we cannot propagate the atom [a], in order to not lose - the information that [a] must be true, we add clause to the list - of clauses to add, so that it will be e-examined later. *) - Log.debug debug "Unit clause, adding to clauses to add"; - Stack.push clause st.clauses_to_add; - report_unsat st clause + (* cannot recover from this *) + report_unsat st @@ US_false clause ) else if a.is_true then ( - (* If the atom is already true, then it should be because of a local hyp. - However it means we can't propagate it at level 0. In order to not lose - that information, we store the clause in a stack of clauses that we will - add to the solver at the next pop. *) - Log.debug debug "Unit clause, adding to root clauses"; - assert (0 < a.var.v_level && a.var.v_level <= base_level st); - Stack.push clause st.clauses_root; - () + () (* atom is already true, nothing to do *) ) else ( Log.debugf debug (fun k->k "Unit clause, propagating: %a" Atom.debug a); Vec.push vec clause; @@ -1496,7 +1474,7 @@ module Make(Plugin : PLUGIN) attach_clause clause; if b.neg.is_true && not a.is_true && not a.neg.is_true then ( let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in - cancel_until st (max lvl (base_level st)); + cancel_until st lvl; enqueue_bool st a ~level:lvl (Bcp clause) ) ) @@ -1508,7 +1486,7 @@ module Make(Plugin : PLUGIN) if not (Stack.is_empty st.clauses_to_add) then ( while not (Stack.is_empty st.clauses_to_add) do let c = Stack.pop st.clauses_to_add in - add_clause st c + add_clause_ st c done ) @@ -1723,6 +1701,7 @@ module Make(Plugin : PLUGIN) enqueue_bool st atom ~level:current_level Decision ) + (* FIXME: add assumptions first, add analyze_final *) and pick_branch_lit st = match st.next_decision with | Some atom -> @@ -1741,7 +1720,7 @@ module Make(Plugin : PLUGIN) ) | E_var v -> pick_branch_aux st v.pa - | exception Not_found -> raise E_sat + | exception Not_found -> raise_notrace E_sat end (* do some amount of search, until the number of conflicts or clause learnt @@ -1759,22 +1738,23 @@ module Make(Plugin : PLUGIN) if confl.attached then add_boolean_conflict st confl else - add_clause st confl + add_clause_ st confl | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); assert (st.elt_head = st.th_head); - if Vec.size st.trail = nb_elt st.st then raise E_sat; + if Vec.size st.trail = nb_elt st.st then raise_notrace E_sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( Log.debug info "Restarting..."; - cancel_until st (base_level st); - raise Restart + cancel_until st 0; + raise_notrace Restart ); (* if decision_level() = 0 then simplify (); *) if n_of_learnts >= 0 && - Vec.size st.clauses_learnt - Vec.size st.trail >= n_of_learnts - then reduce_db(); + Vec.size st.clauses_learnt - Vec.size st.trail >= n_of_learnts then ( + reduce_db(); + ); pick_branch_lit st done @@ -1792,7 +1772,7 @@ module Make(Plugin : PLUGIN) let[@inline] eval st lit = fst @@ eval_level st lit - let[@inline] unsat_conflict st = st.unsat_conflict + let[@inline] unsat_conflict st = st.unsat_at_0 let model (st:t) : (term * term) list = let opt = function Some a -> a | None -> assert false in @@ -1804,9 +1784,9 @@ module Make(Plugin : PLUGIN) (* fixpoint of propagation and decisions until a model is found, or a conflict is reached *) - let solve (st:t) : unit = + let solve_ (st:t) : unit = Log.debug 5 "solve"; - if is_unsat st then raise E_unsat; + check_unsat_ st; let n_of_conflicts = ref (to_float st.restart_first) in let n_of_learnts = ref ((to_float (nb_clauses st)) *. st.learntsize_factor) in try @@ -1827,67 +1807,26 @@ module Make(Plugin : PLUGIN) Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); Stack.push c st.clauses_to_add end; - if Stack.is_empty st.clauses_to_add then raise E_sat + if Stack.is_empty st.clauses_to_add then raise_notrace E_sat end done with E_sat -> () - let assume st ?tag cnf = + let assume st cnf = List.iter (fun l -> let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make ?tag atoms Hyp in + let c = Clause.make atoms Hyp in Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" Clause.debug c); Stack.push c st.clauses_to_add) cnf - (* create a factice decision level for local assumptions *) - let push st : unit = - Log.debug debug "Pushing a new user level"; - match st.unsat_conflict with - | Some _ -> raise E_unsat - | None -> - cancel_until st (base_level st); - Log.debugf debug - (fun k -> k "@[Status:@,@[trail: %d - %d@,%a@]" - st.elt_head st.th_head (Vec.pp ~sep:"" Trail_elt.debug) st.trail); - begin match propagate st with - | Some confl -> - report_unsat st confl - | None -> - Log.debugf debug - (fun k -> k "@[Current trail:@,@[%a@]@]" - (Vec.pp ~sep:"" Trail_elt.debug) st.trail); - Log.debug info "Creating new user level"; - new_decision_level st; - Vec.push st.user_levels (Vec.size st.clauses_temp); - assert (decision_level st = base_level st) - end - - (* pop the last factice decision level *) - let pop st : unit = - if base_level st = 0 then - Log.debug warn "Cannot pop (already at level 0)" - else ( - Log.debug info "Popping user level"; - assert (base_level st > 0); - st.unsat_conflict <- None; - let n = Vec.pop st.user_levels in (* before the [cancel_until]! *) - (* Add the root clauses to the clauses to add *) - Stack.iter (fun c -> Stack.push c st.clauses_to_add) st.clauses_root; - Stack.clear st.clauses_root; - (* remove from env.clauses_temp the now invalid caluses. *) - Vec.shrink st.clauses_temp n; - assert (Vec.for_all (fun c -> Array.length c.atoms = 1) st.clauses_temp); - assert (Vec.for_all (fun c -> c.atoms.(0).var.v_level <= base_level st) st.clauses_temp); - cancel_until st (base_level st) - ) - + (* TODO: remove (* Add local hyps to the current decision level *) let local (st:t) (l:atom list) : unit = let aux a = Log.debugf info (fun k-> k "Local assumption: @[%a@]" Atom.debug a); - assert (decision_level st = base_level st); + assert (decision_level st = st); if not a.is_true then ( let c = Clause.make [a] Local in Log.debugf debug (fun k -> k "Temp clause: @[%a@]" Clause.debug c); @@ -1910,6 +1849,7 @@ module Make(Plugin : PLUGIN) List.iter aux l | Some _ -> Log.debug warn "Cannot add local assumption (already unsat)" + *) (* Check satisfiability *) let check_clause c = @@ -1937,10 +1877,8 @@ module Make(Plugin : PLUGIN) let check st : bool = Stack.is_empty st.clauses_to_add && - check_stack st.clauses_root && check_vec st.clauses_hyps && - check_vec st.clauses_learnt && - check_vec st.clauses_temp + check_vec st.clauses_learnt (* Unsafe access to internal data *) @@ -1948,23 +1886,20 @@ module Make(Plugin : PLUGIN) let history env = env.clauses_learnt - let temp env = env.clauses_temp - let trail env = env.trail (* Result type *) type res = | Sat of (term,atom) Solver_intf.sat_state - | Unsat of (clause,Proof.t) Solver_intf.unsat_state + | Unsat of (atom,clause,Proof.t) Solver_intf.unsat_state let pp_all st lvl status = Log.debugf lvl (fun k -> k "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ - @[Temp:@\n%a@]@,@[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." + @[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." status (Vec.pp ~sep:"" Trail_elt.debug) (trail st) - (Vec.pp ~sep:"" Clause.debug) (temp st) (Vec.pp ~sep:"" Clause.debug) (hyps st) (Vec.pp ~sep:"" Clause.debug) (history st) ) @@ -1985,60 +1920,43 @@ module Make(Plugin : PLUGIN) model = (fun () -> model st); } - let mk_unsat (st:t) : (_,_) Solver_intf.unsat_state = + let mk_unsat (st:t) (us: unsat_cause) : _ Solver_intf.unsat_state = pp_all st 99 "UNSAT"; - let unsat_conflict () = - match unsat_conflict st with - | None -> assert false - | Some c -> c + let unsat_assumptions () = match us with + | US_local {core} -> core + | _ -> [] + in + let unsat_conflict = match us with + | US_false c -> fun() -> c + | US_local {core} -> + let c = lazy ( + let hist = [] in (* FIXME *) + Clause.make (List.map Atom.neg core) (History hist) + ) in + fun () -> Lazy.force c in let get_proof () = let c = unsat_conflict () in - Proof.prove_unsat c + Proof.prove c in - { Solver_intf.unsat_conflict; get_proof; } - - (* clean local state *) - let[@inline] cleanup_ (st:t) : unit = - if st.dirty then ( - pop st; (* reset *) - st.dirty <- false; - ) - - (* Wrappers around internal functions*) - let[@inline] assume st ?tag cls : unit = - cleanup_ st; - assume st ?tag cls + { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } let[@inline] add_clause_a st c : unit = let c = Clause.make_a c Hyp in - add_clause st c + add_clause_ st c - let[@inline] add_clause st ?tag c : unit = - cleanup_ st; - let c = Clause.make ?tag c Hyp in - add_clause st c + let[@inline] add_clause st c : unit = + let c = Clause.make c Hyp in + add_clause_ st c let solve (st:t) ?(assumptions=[]) () = - cleanup_ st; + Vec.clear st.assumptions; + List.iter (Vec.push st.assumptions) assumptions; try - push st; - st.dirty <- true; (* to call [pop] before any other action *) - local st assumptions; - solve st; + solve_ st; Sat (mk_sat st) - with E_unsat -> - Unsat (mk_unsat st) - - let[@inline] push st = - cleanup_ st; - push st - - let[@inline] pop st = - cleanup_ st; - pop st - - let unsat_core = Proof.unsat_core + with E_unsat us -> + Unsat (mk_unsat st us) let true_at_level0 st a = try @@ -2046,22 +1964,10 @@ module Make(Plugin : PLUGIN) b && lev = 0 with UndecidedLit -> false - (* TODO: remove *) - let get_tag cl = cl.tag - - let[@inline] new_lit st t = - cleanup_ st; - new_lit st t - - let[@inline] make_atom st a = - cleanup_ st; - make_atom_ st a - let export (st:t) : clause Solver_intf.export = let hyps = hyps st in let history = history st in - let local = temp st in - {Solver_intf.hyps; history; local} + {Solver_intf.hyps; history; } end [@@inline][@@specialise] diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 1dd6b961..daee1b7c 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -18,14 +18,14 @@ type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { model : unit -> ('term * 'term) list; } -type ('clause, 'proof) unsat_state = ('clause, 'proof) Solver_intf.unsat_state = { +type ('atom,'clause, 'proof) unsat_state = ('atom,'clause, 'proof) Solver_intf.unsat_state = { unsat_conflict : unit -> 'clause; get_proof : unit -> 'proof; + unsat_assumptions: unit -> 'atom list; } type 'clause export = 'clause Solver_intf.export = { hyps : 'clause Vec.t; history : 'clause Vec.t; - local : 'clause Vec.t; } type ('formula, 'proof) th_res = ('formula, 'proof) Solver_intf.th_res = | Th_sat diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 13dff750..354e9f6c 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -32,18 +32,19 @@ type ('term, 'form) sat_state = { } (** The type of values returned when the solver reaches a SAT state. *) -type ('clause, 'proof) unsat_state = { +type ('atom, 'clause, 'proof) unsat_state = { unsat_conflict : unit -> 'clause; (** Returns the unsat clause found at the toplevel *) get_proof : unit -> 'proof; (** returns a persistent proof of the empty clause from the Unsat result. *) + unsat_assumptions: unit -> 'atom list; + (** Subset of assumptions responsible for "unsat" *) } (** The type of values returned when the solver reaches an UNSAT state. *) type 'clause export = { hyps: 'clause Vec.t; history: 'clause Vec.t; - local: 'clause Vec.t; } (** Export internal state *) @@ -365,7 +366,6 @@ module type S = sig val atoms : t -> atom array val atoms_l : t -> atom list - val tag : t -> int option val equal : t -> t -> bool val name : t -> string @@ -396,7 +396,7 @@ module type S = sig (** Result type for the solver *) type res = | Sat of (term,atom) sat_state (** Returned when the solver reaches SAT, with a model *) - | Unsat of (clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) + | Unsat of (atom,clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit (** Exception raised by the evaluating functions when a literal @@ -404,11 +404,11 @@ module type S = sig (** {2 Base operations} *) - val assume : t -> ?tag:int -> formula list list -> unit + val assume : t -> formula list list -> unit (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) - val add_clause : t -> ?tag:int -> atom list -> unit + val add_clause : t -> atom list -> unit (** Lower level addition of clauses *) val add_clause_a : t -> atom array -> unit @@ -430,26 +430,10 @@ module type S = sig This formula will be decided on at some point during solving, wether it appears in clauses or not. *) - val unsat_core : Proof.t -> clause list - (** Returns the unsat core of a given proof, ie a subset of all the added - clauses that is sufficient to establish unsatisfiability. *) - val true_at_level0 : t -> atom -> bool (** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e. it must hold in all models *) - val get_tag : clause -> int option - (** Recover tag from a clause, if any *) - - val push : t -> unit - (** Push a new save point. Clauses added after this call to [push] will - be added as normal, but the corresponding call to [pop] will - remove these clauses. *) - - val pop : t -> unit - (** Return to last save point, discarding clauses added since last - call to [push] *) - val export : t -> clause export end From aa47a442426ebefc67402a67b3ce4bae30767762 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 15:56:36 -0600 Subject: [PATCH 062/182] feat: expose msat.sat as a proper library, with module `Int_lit` --- src/main/main.ml | 2 +- src/sat/{Expr_sat.ml => Int_lit.ml} | 0 src/sat/{Expr_sat.mli => Int_lit.mli} | 2 +- src/sat/Msat_sat.ml | 4 ++-- src/sat/Msat_sat.mli | 4 ++-- src/sat/dune | 3 ++- tests/icnf-solve/{Icnf_solve.ml => icnf_solve.ml} | 3 ++- tests/test_api.ml | 2 +- 8 files changed, 11 insertions(+), 9 deletions(-) rename src/sat/{Expr_sat.ml => Int_lit.ml} (100%) rename src/sat/{Expr_sat.mli => Int_lit.mli} (95%) rename tests/icnf-solve/{Icnf_solve.ml => icnf_solve.ml} (98%) diff --git a/src/main/main.ml b/src/main/main.ml index e2573b4f..e822dd1d 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -59,7 +59,7 @@ module Process = struct Format.printf "Unsat (%f/%f)@." t t' end - let conv_c c = List.rev_map S.Expr.make c + let conv_c c = List.rev_map S.Int_lit.make c let add_clauses cs = S.assume st @@ CCList.map conv_c cs diff --git a/src/sat/Expr_sat.ml b/src/sat/Int_lit.ml similarity index 100% rename from src/sat/Expr_sat.ml rename to src/sat/Int_lit.ml diff --git a/src/sat/Expr_sat.mli b/src/sat/Int_lit.mli similarity index 95% rename from src/sat/Expr_sat.mli rename to src/sat/Int_lit.mli index c4803bc9..6845d161 100644 --- a/src/sat/Expr_sat.mli +++ b/src/sat/Int_lit.mli @@ -1,5 +1,5 @@ -(** The module defining formulas *) +(** {1 The module defining formulas} *) (** SAT Formulas diff --git a/src/sat/Msat_sat.ml b/src/sat/Msat_sat.ml index 007f385b..bd4b3dd5 100644 --- a/src/sat/Msat_sat.ml +++ b/src/sat/Msat_sat.ml @@ -3,6 +3,6 @@ MSAT is free software, using the Apache license, see file LICENSE Copyright 2016 Guillaume Bury *) -module Expr = Expr_sat -include Msat.Make_pure_sat(Expr) +module Int_lit = Int_lit +include Msat.Make_pure_sat(Int_lit) diff --git a/src/sat/Msat_sat.mli b/src/sat/Msat_sat.mli index b36bda05..e2897626 100644 --- a/src/sat/Msat_sat.mli +++ b/src/sat/Msat_sat.mli @@ -9,8 +9,8 @@ Copyright 2016 Guillaume Bury atomic propositions. *) -module Expr = Expr_sat +module Int_lit = Int_lit -include Msat.S with type Formula.t = Expr.t and type theory = unit +include Msat.S with type Formula.t = Int_lit.t and type theory = unit (** A functor that can generate as many solvers as needed. *) diff --git a/src/sat/dune b/src/sat/dune index c321e17c..b5940271 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -1,7 +1,8 @@ (library (name msat_sat) - ; private + (public_name msat.sat) + (synopsis "purely boolean interface to Msat") (libraries msat) (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always diff --git a/tests/icnf-solve/Icnf_solve.ml b/tests/icnf-solve/icnf_solve.ml similarity index 98% rename from tests/icnf-solve/Icnf_solve.ml rename to tests/icnf-solve/icnf_solve.ml index afc8b61d..3fc3cb7f 100644 --- a/tests/icnf-solve/Icnf_solve.ml +++ b/tests/icnf-solve/icnf_solve.ml @@ -58,7 +58,7 @@ end = struct end module Solver = struct - module F = Msat_sat.Expr + module F = Msat_sat.Int_lit module S = Msat_sat type t = S.t @@ -85,6 +85,7 @@ let solve_with_solver ~debug file : unit = | Parse.Add_clause c -> if debug then ( Printf.printf "add_clause %a\n%!" pp_arr c; + Msat.Log.set_debug 5; ); let r = Solver.add_clause s c in if r then process_problem() diff --git a/tests/test_api.ml b/tests/test_api.ml index bef40497..1b3da279 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -6,7 +6,7 @@ Copyright 2014 Simon Cruanes (* Tests that require the API *) -module F = Msat_sat.Expr +module F = Msat_sat.Int_lit module S = Msat_sat let (|>) x f = f x From a88752759f0e643c68be13f83147b48234c4bb12 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 15:56:54 -0600 Subject: [PATCH 063/182] feat: expose simple script for icnf-solve --- icnf-solve.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 icnf-solve.sh diff --git a/icnf-solve.sh b/icnf-solve.sh new file mode 100755 index 00000000..b8c59e75 --- /dev/null +++ b/icnf-solve.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec dune exec tests/icnf-solve/icnf_solve.exe -- $@ From 4ca441fa38350eada3eda74e7a8424da41e60749 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 15:57:18 -0600 Subject: [PATCH 064/182] fix(core): cancel-until 0 before solving --- src/core/Internal.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 8220f87c..eb2e2ffb 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1950,6 +1950,7 @@ module Make(Plugin : PLUGIN) add_clause_ st c let solve (st:t) ?(assumptions=[]) () = + cancel_until st 0; Vec.clear st.assumptions; List.iter (Vec.push st.assumptions) assumptions; try From 52ae127a5dfcd041eea122af41c0f4112aae6be9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 15:57:27 -0600 Subject: [PATCH 065/182] refactor: implement analyze_final to compute unsat cores --- src/core/Internal.ml | 125 +++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index eb2e2ffb..e0e8067d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -151,8 +151,9 @@ module Make(Plugin : PLUGIN) (v.lid+1) debug_assign v Term.pp v.term end - let seen_pos = 0b1 - let seen_neg = 0b10 + let seen_var = 0b1 + let seen_pos = 0b10 + let seen_neg = 0b100 module Var = struct type t = var @@ -162,6 +163,9 @@ module Make(Plugin : PLUGIN) let[@inline] reason v = v.reason let[@inline] assignable v = v.v_assignable let[@inline] weight v = v.v_weight + let[@inline] mark v = v.v_fields <- v.v_fields lor seen_var + let[@inline] unmark v = v.v_fields <- v.v_fields land (lnot seen_var) + let[@inline] marked v = (v.v_fields land seen_var) <> 0 let make (st:st) (t:formula) : var * Solver_intf.negated = let lit, negated = Formula.norm t in @@ -706,7 +710,8 @@ module Make(Plugin : PLUGIN) (* conflict at level 0, if any *) mutable next_decision : atom option; - (* When the last conflict was a semantic one, this stores the next decision to make *) + (* When the last conflict was a semantic one (mcsat), + this stores the next decision to make *) trail : trail_elt Vec.t; (* decision stack + propagated elements (atoms or assignments). *) @@ -786,14 +791,8 @@ module Make(Plugin : PLUGIN) let st = create_st ~size () in create_ ~st th - (* Misc functions *) - let to_float = float_of_int - let to_int = int_of_float - let[@inline] st t = t.st let[@inline] nb_clauses st = Vec.size st.clauses_hyps - (* let nb_vars () = St.nb_elt () *) - let[@inline] decision_level st = Vec.size st.elt_levels (* Do we have a level-0 empty clause? *) @@ -856,11 +855,17 @@ module Make(Plugin : PLUGIN) inserting them into the heap, if it appears that it helps performance. *) let new_lit st t = let l = Lit.make st.st t in - insert_var_order st (E_lit l) + if l.l_level < 0 then ( + insert_var_order st (E_lit l) + ) let make_atom st (p:formula) : atom = let a = mk_atom st p in - insert_var_order st (E_var a.var); + if a.var.v_level < 0 then ( + insert_var_order st (E_var a.var); + ) else ( + assert (a.is_true || a.neg.is_true); + ); a (* Rather than iterate over all the heap when we want to decrease all the @@ -1278,7 +1283,7 @@ module Make(Plugin : PLUGIN) let learnt = ref [] in let cond = ref true in let blevel = ref 0 in - let seen = ref [] in + let seen = ref [] in (* for cleanup *) let c = ref (Some c_clause) in let tr_ind = ref (Vec.size st.trail - 1) in let history = ref [] in @@ -1395,6 +1400,7 @@ module Make(Plugin : PLUGIN) if cr.cr_is_uip then ( enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) ) else ( + assert Plugin.mcsat; st.next_decision <- Some fuip.neg ) end; @@ -1675,6 +1681,39 @@ module Make(Plugin : PLUGIN) | exception Conflict c -> Some c ) + (* compute unsat core from assumption [a] *) + let analyze_final (self:t) (a:atom) : atom list = + Log.debugf 5 (fun k->k "(@[sat.analyze-final@ :lit %a@])" Atom.debug a); + assert (Atom.is_false a); + let core = ref [a.neg] in + let idx = ref (Vec.size self.trail - 1) in + Var.mark a.var; + let seen = ref [a.var] in + while !idx >= 0 do + begin match Vec.get self.trail !idx with + | Lit _ -> () (* semantic decision, ignore *) + | Atom a' -> + if Var.marked a'.var then ( + match Atom.reason a' with + | Some Semantic -> () + | Some Decision -> core := a' :: !core + | Some (Bcp c) -> + Array.iter + (fun a -> + let v = a.var in + if not @@ Var.marked v then ( + seen := v :: !seen; + Var.mark v; + )) + c.atoms + | _ -> () + ); + end; + decr idx + done; + List.iter Var.unmark !seen; + !core + (* remove some learnt clauses NOTE: so far we do not forget learnt clauses. We could, as long as lemmas from the theory itself are kept. *) @@ -1701,18 +1740,31 @@ module Make(Plugin : PLUGIN) enqueue_bool st atom ~level:current_level Decision ) - (* FIXME: add assumptions first, add analyze_final *) and pick_branch_lit st = match st.next_decision with | Some atom -> + assert Plugin.mcsat; st.next_decision <- None; pick_branch_aux st atom + | None when decision_level st < Vec.size st.assumptions -> + (* use an assumption *) + let a = Vec.get st.assumptions (decision_level st) in + if Atom.is_true a then ( + new_decision_level st; (* pseudo decision level, [a] is already true *) + pick_branch_lit st + ) else if Atom.is_false a then ( + (* root conflict, find unsat core *) + let core = analyze_final st a in + raise (E_unsat (US_local {core})) + ) else ( + pick_branch_aux st a + ) | None -> begin match H.remove_min st.order with | E_lit l -> - if Lit.level l >= 0 then + if Lit.level l >= 0 then ( pick_branch_lit st - else ( + ) else ( let value = Plugin.assign st.th l.term in new_decision_level st; let current_level = decision_level st in @@ -1735,10 +1787,11 @@ module Make(Plugin : PLUGIN) might 'forget' the initial conflict clause, and only add the analyzed backtrack clause. So in those case, we use add_clause to make sure the initial conflict clause is also added. *) - if confl.attached then + if confl.attached then ( add_boolean_conflict st confl - else + ) else ( add_clause_ st confl + ); | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); @@ -1785,14 +1838,14 @@ module Make(Plugin : PLUGIN) (* fixpoint of propagation and decisions until a model is found, or a conflict is reached *) let solve_ (st:t) : unit = - Log.debug 5 "solve"; + Log.debugf 5 (fun k->k "(@[sat.solve :assms %d@])" (Vec.size st.assumptions)); check_unsat_ st; - let n_of_conflicts = ref (to_float st.restart_first) in - let n_of_learnts = ref ((to_float (nb_clauses st)) *. st.learntsize_factor) in + let n_of_conflicts = ref (float_of_int st.restart_first) in + let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. st.learntsize_factor) in try while true do begin try - search st (to_int !n_of_conflicts) (to_int !n_of_learnts) + search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) with | Restart -> n_of_conflicts := !n_of_conflicts *. restart_inc; @@ -1821,36 +1874,6 @@ module Make(Plugin : PLUGIN) Stack.push c st.clauses_to_add) cnf - (* TODO: remove - (* Add local hyps to the current decision level *) - let local (st:t) (l:atom list) : unit = - let aux a = - Log.debugf info (fun k-> k "Local assumption: @[%a@]" Atom.debug a); - assert (decision_level st = st); - if not a.is_true then ( - let c = Clause.make [a] Local in - Log.debugf debug (fun k -> k "Temp clause: @[%a@]" Clause.debug c); - Vec.push st.clauses_temp c; - if a.neg.is_true then ( - (* conflict between assumptions: UNSAT *) - report_unsat st c; - ) else ( - (* make a decision, propagate *) - let level = decision_level st in - enqueue_bool st a ~level (Bcp c); - ) - ) - in - assert (base_level st > 0); - match st.unsat_conflict with - | None -> - Log.debug info "Adding local assumption"; - cancel_until st (base_level st); - List.iter aux l - | Some _ -> - Log.debug warn "Cannot add local assumption (already unsat)" - *) - (* Check satisfiability *) let check_clause c = let tmp = Array.map (fun a -> From f8281bfcc2b9393685bf4c7621baa631f2a92a0c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 18:13:29 -0600 Subject: [PATCH 066/182] test: check unsat cores in icnf-solve --- tests/Makefile | 2 +- tests/icnf-solve/icnf_solve.ml | 34 ++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 2dd2304b..209361a8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -2,7 +2,7 @@ test-icnf: @for i in regression/*.icnf ; do \ echo "test problem $$i"; \ - ./icnf-solve/icnf_solve.exe $$i > regression/.`basename $$i`.out 2>/dev/null ; \ + ./icnf-solve/icnf_solve.exe --check $$i > regression/.`basename $$i`.out 2>/dev/null ; \ diff regression/.`basename $$i`.out regression/.`basename $$i`.ref \ || ( echo "mismatch for $$i" ; exit 1) ; \ done diff --git a/tests/icnf-solve/icnf_solve.ml b/tests/icnf-solve/icnf_solve.ml index 3fc3cb7f..4115237b 100644 --- a/tests/icnf-solve/icnf_solve.ml +++ b/tests/icnf-solve/icnf_solve.ml @@ -69,11 +69,13 @@ module Solver = struct let solve s ass = let ass = Array.to_list ass in match S.solve ~assumptions:ass s () with - | S.Sat _ -> true - | S.Unsat _ -> false + | S.Sat _ -> Ok () + | S.Unsat { unsat_assumptions; _ } -> + let core = unsat_assumptions() in + Error core end -let solve_with_solver ~debug file : unit = +let solve_with_solver ~check ~debug file : unit = Printf.eprintf "c process %S\n%!" file; let s = Solver.make () in let pp_arr out a = @@ -97,8 +99,22 @@ let solve_with_solver ~debug file : unit = if debug then ( Printf.printf "c solve %a\n%!" pp_arr assumptions; ); - let r = Solver.solve s assumptions in - Printf.printf "%s\n%!" (if r then "SAT" else "UNSAT"); + begin match Solver.solve s assumptions with + | Ok () -> Printf.printf "SAT\n%!" + | Error (_::_ as core) when check -> + Printf.printf "UNSAT\n%!"; + let core = Array.of_list core in + if debug then Printf.printf "check unsat core %a\n" pp_arr core; + (* check unsat core *) + begin match Solver.solve s core with + | Ok () -> + Printf.printf "error: unsat core %a is SAT\n%!" pp_arr core; + exit 1 + | Error _ -> () + end; + | Error _ -> + Printf.printf "UNSAT\n%!"; + end; (* next problem! *) process_problem() | exception End_of_file -> @@ -114,8 +130,8 @@ let solve_with_solver ~debug file : unit = in process_problem () -let solve_with_file ~debug file : unit = - try solve_with_solver ~debug file +let solve_with_file ~check ~debug file : unit = + try solve_with_solver ~check ~debug file with e -> Printf.printf "error while solving %S:\n%s" file (Printexc.to_string e); @@ -124,9 +140,11 @@ let solve_with_file ~debug file : unit = let () = let files = ref [] in let debug = ref false in + let check = ref false in let opts = [ "-d", Arg.Set debug, " debug"; + "--check", Arg.Set check, " check unsat cores"; ] |> Arg.align in Arg.parse opts (fun f -> files := f :: !files) "icnf_solve [options] "; - List.iter (fun f -> solve_with_file ~debug:!debug f) !files; + List.iter (fun f -> solve_with_file ~check:!check ~debug:!debug f) !files; () From 0d7ae3488082ba6a05e8f0ab31ed190955ff8712 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 19 Jan 2019 18:13:41 -0600 Subject: [PATCH 067/182] fix(analyze-final): mistake in production of unsat cores --- src/core/Internal.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e0e8067d..51651e5b 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1685,7 +1685,7 @@ module Make(Plugin : PLUGIN) let analyze_final (self:t) (a:atom) : atom list = Log.debugf 5 (fun k->k "(@[sat.analyze-final@ :lit %a@])" Atom.debug a); assert (Atom.is_false a); - let core = ref [a.neg] in + let core = ref [a] in let idx = ref (Vec.size self.trail - 1) in Var.mark a.var; let seen = ref [a.var] in From 3c940ed4f66d5b9f865f0bc7d7c4e753413d7cc6 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 22 Jan 2019 20:17:00 -0600 Subject: [PATCH 068/182] refactor(core): use bitfield in clauses, use `Vec.iter` more --- src/core/Internal.ml | 65 ++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 51651e5b..be9fc539 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -57,8 +57,7 @@ module Make(Plugin : PLUGIN) atoms : atom array; mutable cpremise : premise; mutable activity : float; - mutable attached : bool; (* TODO: use an int field *) - mutable visited : bool; + mutable flags: int; (* bitfield *) } and reason = @@ -339,8 +338,7 @@ module Make(Plugin : PLUGIN) incr n; { name; atoms = atoms; - visited = false; - attached = false; + flags = 0; activity = 0.; cpremise = premise} @@ -353,14 +351,28 @@ module Make(Plugin : PLUGIN) let[@inline] atoms_l c = Array.to_list c.atoms let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms - let[@inline] premise c = c.cpremise - let[@inline] set_premise c p = c.cpremise <- p + let flag_attached = 0b1 + let flag_visited = 0b10 + let flag_learnt = 0b100 + let flag_dead = 0b1000 - let[@inline] visited c = c.visited - let[@inline] set_visited c b = c.visited <- b + let[@inline] visited c = (c.flags land flag_visited) <> 0 + let[@inline] set_visited c b = + if b then c.flags <- c.flags lor flag_visited + else c.flags <- c.flags land lnot flag_visited - let[@inline] attached c = c.attached - let[@inline] set_attached c b = c.attached <- b + let[@inline] attached c = (c.flags land flag_attached) <> 0 + let[@inline] set_attached c b = + if b then c.flags <- c.flags lor flag_attached + else c.flags <- c.flags land lnot flag_attached + + let[@inline] learnt c = (c.flags land flag_learnt) <> 0 + let[@inline] set_learnt c b = + if b then c.flags <- c.flags lor flag_learnt + else c.flags <- c.flags land lnot flag_learnt + + let[@inline] dead c = (c.flags land flag_dead) <> 0 + let[@inline] set_dead c = c.flags <- c.flags lor flag_dead let[@inline] activity c = c.activity let[@inline] set_activity c w = c.activity <- w @@ -595,21 +607,21 @@ module Make(Plugin : PLUGIN) let rec aux res acc = function | [] -> res, acc | c :: r -> - if not c.visited then ( - c.visited <- true; + if not @@ Clause.visited c then ( + Clause.set_visited c true; match c.cpremise with | Hyp | Lemma _ -> aux (c :: res) acc r | History h -> let l = List.fold_left (fun acc c -> - if not c.visited then c :: acc else acc) r h in + if not @@ Clause.visited c then c :: acc else acc) r h in aux res (c :: acc) l ) else ( aux res acc r ) in let res, tmp = aux [] [] [proof] in - List.iter (fun c -> c.visited <- false) res; - List.iter (fun c -> c.visited <- false) tmp; + List.iter (fun c -> Clause.set_visited c false) res; + List.iter (fun c -> Clause.set_visited c false) tmp; res module Tbl = Clause.Tbl @@ -703,6 +715,7 @@ module Make(Plugin : PLUGIN) clauses_learnt : clause Vec.t; (* learnt clauses (tautologies true at any time, whatever the user level) *) + (* TODO: make this a Vec.t *) clauses_to_add : clause Stack.t; (* Clauses either assumed or pushed by the theory, waiting to be added. *) @@ -809,10 +822,11 @@ module Make(Plugin : PLUGIN) of subterms of each formula, so we have a field [v_assignable] directly in variables to do so. *) let iter_sub f v = - if Plugin.mcsat then + if Plugin.mcsat then ( match v.v_assignable with | Some l -> List.iter f l | None -> assert false + ) (* When we have a new literal, we need to first create the list of its subterms. *) @@ -892,12 +906,10 @@ module Make(Plugin : PLUGIN) ) (* increase activity of literal [l] *) - let lit_bump_activity_aux st (l:lit): unit = + let lit_bump_activity_aux (st:t) (l:lit): unit = l.l_weight <- l.l_weight +. st.var_incr; if l.l_weight > 1e100 then ( - for i = 0 to nb_elt st.st - 1 do - Elt.set_weight (get_elt st.st i) ((Elt.weight (get_elt st.st i)) *. 1e-100) - done; + iter_elt st.st (fun e -> Elt.set_weight e (Elt.weight e *. 1e-100)); st.var_incr <- st.var_incr *. 1e-100; ); let elt = Elt.of_lit l in @@ -914,10 +926,7 @@ module Make(Plugin : PLUGIN) let clause_bump_activity st (c:clause) : unit = c.activity <- c.activity +. st.clause_incr; if c.activity > 1e20 then ( - for i = 0 to Vec.size st.clauses_learnt - 1 do - (Vec.get st.clauses_learnt i).activity <- - (Vec.get st.clauses_learnt i).activity *. 1e-20; - done; + Vec.iter (fun c -> c.activity <- c.activity *. 1e-20) st.clauses_learnt; st.clause_incr <- st.clause_incr *. 1e-20 ) @@ -1032,11 +1041,11 @@ module Make(Plugin : PLUGIN) *) let attach_clause c = - assert (not c.attached); + assert (not @@ Clause.attached c); Log.debugf debug (fun k -> k "Attaching %a" Clause.debug c); Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; - c.attached <- true; + Clause.set_attached c true; () (* Backtracking. @@ -1778,6 +1787,8 @@ module Make(Plugin : PLUGIN) (* do some amount of search, until the number of conflicts or clause learnt reaches the given parameters *) let search (st:t) n_of_conflicts n_of_learnts : unit = + Log.debugf 3 + (fun k->k "(@[sat.search@ :n-conflicts %d@ :n-learnt %d@])" n_of_conflicts n_of_learnts); let conflictC = ref 0 in while true do match propagate st with @@ -1787,7 +1798,7 @@ module Make(Plugin : PLUGIN) might 'forget' the initial conflict clause, and only add the analyzed backtrack clause. So in those case, we use add_clause to make sure the initial conflict clause is also added. *) - if confl.attached then ( + if Clause.attached confl then ( add_boolean_conflict st confl ) else ( add_clause_ st confl From ca62db00e128b59bbe3694d6b424eb8c33ced7fe Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 22 Jan 2019 20:17:37 -0600 Subject: [PATCH 069/182] =?UTF-8?q?perf:=20garbage=20collect=20clauses=20(?= =?UTF-8?q?only=20for=20clauses=20with=20=E2=89=A53=20lits)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/Internal.ml | 57 +++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index be9fc539..63f6cc70 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1397,13 +1397,16 @@ module Make(Plugin : PLUGIN) report_unsat st (US_false confl) ) else ( let uclause = Clause.make cr.cr_learnt (History cr.cr_history) in - Vec.push st.clauses_learnt uclause; (* no need to attach [uclause], it is true at level 0 *) + Clause.set_learnt uclause true; enqueue_bool st fuip ~level:0 (Bcp uclause) ) | fuip :: _ -> let lclause = Clause.make cr.cr_learnt (History cr.cr_history) in - Vec.push st.clauses_learnt lclause; + Clause.set_learnt lclause true; + if Array.length lclause.atoms > 2 then ( + Vec.push st.clauses_learnt lclause; (* potentially gc'able *) + ); attach_clause lclause; clause_bump_activity st lclause; if cr.cr_is_uip then ( @@ -1437,8 +1440,8 @@ module Make(Plugin : PLUGIN) (* Get the correct vector to insert a clause in. *) let clause_vector st c = match c.cpremise with - | Hyp -> st.clauses_hyps - | Lemma _ | History _ -> st.clauses_learnt + | Hyp | Lemma _ -> st.clauses_hyps + | History _ -> st.clauses_learnt (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) @@ -1569,10 +1572,16 @@ module Make(Plugin : PLUGIN) if i >= Vec.size watched then () else ( let c = Vec.get watched i in - assert c.attached; - let j = match propagate_in_clause st a c i with - | Watch_kept -> i+1 - | Watch_removed -> i (* clause at this index changed *) + assert (Clause.attached c); + let j = + if Clause.dead c then ( + Vec.fast_remove watched i; + i + ) else ( + match propagate_in_clause st a c i with + | Watch_kept -> i+1 + | Watch_removed -> i (* clause at this index changed *) + ) in aux j ) @@ -1723,10 +1732,23 @@ module Make(Plugin : PLUGIN) List.iter Var.unmark !seen; !core - (* remove some learnt clauses - NOTE: so far we do not forget learnt clauses. We could, as long as - lemmas from the theory itself are kept. *) - let reduce_db () = () + (* remove some learnt clauses. *) + let reduce_db (st:t) (n_of_learnts: int) : unit = + let v = st.clauses_learnt in + Log.debugf 3 (fun k->k "(@[sat.gc.start :keep %d :out-of %d@])" n_of_learnts (Vec.size v)); + assert (Vec.size v > n_of_learnts); + (* sort by decreasing activity *) + Vec.sort v (fun c1 c2 -> Pervasives.compare c2.activity c1.activity); + let n_collected = ref 0 in + while Vec.size v > n_of_learnts do + let c = Vec.pop v in + assert (Clause.learnt c); + Clause.set_dead c; + assert (Clause.dead c); + incr n_collected; + done; + Log.debugf 3 (fun k->k "(@[sat.gc.done :collected %d@])" !n_collected); + () (* Decide on a new literal, and enqueue it into the trail *) let rec pick_branch_aux st atom : unit = @@ -1815,9 +1837,9 @@ module Make(Plugin : PLUGIN) ); (* if decision_level() = 0 then simplify (); *) - if n_of_learnts >= 0 && - Vec.size st.clauses_learnt - Vec.size st.trail >= n_of_learnts then ( - reduce_db(); + if n_of_learnts > 0 && + Vec.size st.clauses_learnt - Vec.size st.trail > n_of_learnts then ( + reduce_db st n_of_learnts; ); pick_branch_lit st @@ -1851,9 +1873,10 @@ module Make(Plugin : PLUGIN) let solve_ (st:t) : unit = Log.debugf 5 (fun k->k "(@[sat.solve :assms %d@])" (Vec.size st.assumptions)); check_unsat_ st; - let n_of_conflicts = ref (float_of_int st.restart_first) in - let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. st.learntsize_factor) in try + flush_clauses st; (* add initial clauses *) + let n_of_conflicts = ref (float_of_int st.restart_first) in + let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. st.learntsize_factor) in while true do begin try search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) From 8f29aa80056f57ec193749c348c70c64006c13a9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 22 Jan 2019 20:26:55 -0600 Subject: [PATCH 070/182] refactor: small cleanup --- src/core/Internal.ml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 63f6cc70..43840585 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1380,11 +1380,6 @@ module Make(Plugin : PLUGIN) let[@inline] analyze st c_clause : conflict_res = analyze_sat st c_clause - (* - if mcsat - then analyze_mcsat c_clause - else analyze_sat c_clause - *) (* add the learnt clause to the clause database, propagate, etc. *) let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = @@ -1524,7 +1519,9 @@ module Make(Plugin : PLUGIN) (* false lit must be at index 1 *) atoms.(0) <- atoms.(1); atoms.(1) <- first - ) else assert (a.neg == atoms.(1)); + ) else ( + assert (a.neg == atoms.(1)) + ); let first = atoms.(0) in if first.is_true then Watch_kept (* true clause, keep it in watched *) From 74956e2e87c96e41a118bc1a3c090a2d3c3a7def Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 22 Jan 2019 20:48:11 -0600 Subject: [PATCH 071/182] fix(proof): fix proof production for unsat cores --- src/core/Internal.ml | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 43840585..b02b1af3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -69,6 +69,7 @@ module Make(Plugin : PLUGIN) for pure SAT, [reason] is sufficient *) and premise = | Hyp + | Local | Lemma of lemma | History of clause list @@ -112,6 +113,7 @@ module Make(Plugin : PLUGIN) let name_of_clause c = match c.cpremise with | Hyp -> "H" ^ string_of_int c.name | Lemma _ -> "T" ^ string_of_int c.name + | Local -> "L" ^ string_of_int c.name | History _ -> "C" ^ string_of_int c.name module Lit = struct @@ -389,6 +391,7 @@ module Make(Plugin : PLUGIN) let debug_premise out = function | Hyp -> Format.fprintf out "hyp" | Lemma _ -> Format.fprintf out "th_lemma" + | Local -> Format.fprintf out "local" | History v -> List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v @@ -559,12 +562,14 @@ module Make(Plugin : PLUGIN) match conclusion.cpremise with | Lemma l -> {conclusion; step = Lemma l; } + | Local -> + { conclusion; step = Assumption; } | Hyp -> { conclusion; step = Hypothesis; } | History [] -> Log.debugf 0 (fun k -> k "Empty history for clause: %a" Clause.debug conclusion); raise (Resolution_error "Empty history") - | History [ c ] -> + | History [c] -> let duplicates, res = analyze (list c) in assert (cmp_cl res (list conclusion) = 0); { conclusion; step = Duplicate (c, List.concat duplicates) } @@ -610,7 +615,7 @@ module Make(Plugin : PLUGIN) if not @@ Clause.visited c then ( Clause.set_visited c true; match c.cpremise with - | Hyp | Lemma _ -> aux (c :: res) acc r + | Hyp | Lemma _ | Local -> aux (c :: res) acc r | History h -> let l = List.fold_left (fun acc c -> if not @@ Clause.visited c then c :: acc else acc) r h in @@ -673,7 +678,8 @@ module Make(Plugin : PLUGIN) (* cause of "unsat", possibly conditional to local assumptions *) type unsat_cause = | US_local of { - core: atom list; + first: atom; (* assumption which was found to be proved false *) + core: atom list; (* the set of assumptions *) } | US_false of clause (* true unsat *) @@ -1109,7 +1115,7 @@ module Make(Plugin : PLUGIN) () let pp_unsat_cause out = function - | US_local {core} -> + | US_local {first=_; core} -> Format.fprintf out "false assumptions (@[core %a@])" (Format.pp_print_list Atom.pp) core | US_false c -> @@ -1310,7 +1316,7 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k->k" Resolving clause: %a" Clause.debug clause); begin match clause.cpremise with | History _ -> clause_bump_activity st clause - | Hyp | Lemma _ -> () + | Hyp | Local | Lemma _ -> () end; history := clause :: !history; (* visit the current predecessors *) @@ -1437,6 +1443,7 @@ module Make(Plugin : PLUGIN) match c.cpremise with | Hyp | Lemma _ -> st.clauses_hyps | History _ -> st.clauses_learnt + | Local -> assert false (* never added directly *) (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) @@ -1727,6 +1734,7 @@ module Make(Plugin : PLUGIN) decr idx done; List.iter Var.unmark !seen; + Log.debugf 5 (fun k->k "(@[sat.analyze-final.done@ :core %a@])" (Format.pp_print_list Atom.debug) !core); !core (* remove some learnt clauses. *) @@ -1783,7 +1791,7 @@ module Make(Plugin : PLUGIN) ) else if Atom.is_false a then ( (* root conflict, find unsat core *) let core = analyze_final st a in - raise (E_unsat (US_local {core})) + raise (E_unsat (US_local {first=a; core})) ) else ( pick_branch_aux st a ) @@ -1977,15 +1985,27 @@ module Make(Plugin : PLUGIN) let mk_unsat (st:t) (us: unsat_cause) : _ Solver_intf.unsat_state = pp_all st 99 "UNSAT"; let unsat_assumptions () = match us with - | US_local {core} -> core + | US_local {first=_; core} -> core | _ -> [] in let unsat_conflict = match us with | US_false c -> fun() -> c - | US_local {core} -> + | US_local {core=[]; _} -> assert false + | US_local {first; core} -> let c = lazy ( - let hist = [] in (* FIXME *) - Clause.make (List.map Atom.neg core) (History hist) + let core = List.rev core in (* increasing trail order *) + assert (Atom.equal first @@ List.hd core); + let proof_of (a:atom) = match Atom.reason a with + | Some (Decision | Semantic) -> Clause.make [a] Local + | Some (Bcp c) -> c + | None -> assert false + in + let other_lits = List.filter (fun a -> not (Atom.equal a first)) core in + let hist = + Clause.make [first] Local :: + proof_of first :: + List.map proof_of other_lits in + Clause.make [] (History hist) ) in fun () -> Lazy.force c in From 53dd3acd4ee7c62ebaecaee375f892b2a5fad678 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 22 Jan 2019 20:51:16 -0600 Subject: [PATCH 072/182] chore: improve test infra --- Makefile | 4 +--- tests/run | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index b529fa09..b576e4b9 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,7 @@ build-dev: test: @echo "run tests…" - @dune runtest - -test-full: test + @OCAMLRUNPARAM=b dune runtest --force --no-buffer @echo "run benchmarks…" @/usr/bin/time -f "%e" ./tests/run sat diff --git a/tests/run b/tests/run index a8c33d98..9275f63e 100755 --- a/tests/run +++ b/tests/run @@ -1,7 +1,7 @@ #!/bin/bash CURDIR=`dirname $0` -SOLVER="$CURDIR/../msat.sh" +SOLVER="$CURDIR/../_build/default/src/main/main.exe" solvertest () { for f in `find -L $1 -type f -name '*.cnf' # -o -name '*.smt2'` From 9024b0f0a90f30fdf23bfd19a16824a676638326 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 23 Jan 2019 08:36:07 -0600 Subject: [PATCH 073/182] refactor: change theory API to be simpler and more imperative --- src/core/Internal.ml | 87 ++++++++++++++++++++++++++--------------- src/core/Msat.ml | 18 +++++++-- src/core/Solver_intf.ml | 82 +++++++++++++++++++------------------- 3 files changed, 112 insertions(+), 75 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index b02b1af3..673e47a3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1440,10 +1440,7 @@ module Make(Plugin : PLUGIN) (* Get the correct vector to insert a clause in. *) let clause_vector st c = - match c.cpremise with - | Hyp | Lemma _ -> st.clauses_hyps - | History _ -> st.clauses_learnt - | Local -> assert false (* never added directly *) + if Clause.learnt c then st.clauses_learnt else st.clauses_hyps (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) @@ -1598,6 +1595,8 @@ module Make(Plugin : PLUGIN) ignore (th_eval st a); a + exception Th_conflict of Clause.t + let slice_get st i = match Vec.get st.trail i with | Atom a -> @@ -1606,12 +1605,18 @@ module Make(Plugin : PLUGIN) Solver_intf.Assign (term, v) | Lit _ -> assert false - let slice_push st (l:formula list) (lemma:lemma): unit = + let slice_push st ?(keep=false) (l:formula list) (lemma:lemma): unit = let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in + if not keep then Clause.set_learnt c true; Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); Stack.push c st.clauses_to_add + let slice_raise st (l:formula list) proof : 'a = + let atoms = List.rev_map (create_atom st) l in + let c = Clause.make atoms (Lemma proof) in + raise_notrace (Th_conflict c) + let slice_propagate (st:t) f = function | Solver_intf.Eval l -> let a = mk_atom st f in @@ -1633,23 +1638,44 @@ module Make(Plugin : PLUGIN) invalid_arg "Msat.Internal.slice_propagate" ) - let current_slice st : (_,_,_) Solver_intf.slice = { - Solver_intf.start = st.th_head; - length = Vec.size st.trail - st.th_head; - get = slice_get st; + let[@specialise] slice_iter st ~full f : unit = + for i = (if full then 0 else st.th_head) to Vec.size st.trail-1 do + let e = match Vec.get st.trail i with + | Atom a -> + Solver_intf.Lit a.lit + | Lit {term; assigned = Some v; _} -> + Solver_intf.Assign (term, v) + | Lit _ -> assert false + in + f e + done + + let[@inline] current_slice st : (_,_,_) Solver_intf.slice = { + Solver_intf. + iter_assumptions=slice_iter st ~full:false; push = slice_push st; propagate = slice_propagate st; + raise_conflict=slice_raise st; } (* full slice, for [if_sat] final check *) - let full_slice st : (_,_,_) Solver_intf.slice = { - Solver_intf.start = 0; - length = Vec.size st.trail; - get = slice_get st; + let[@inline] full_slice st : (_,_,_) Solver_intf.slice = { + Solver_intf. + iter_assumptions=slice_iter st ~full:true; push = slice_push st; - propagate = (fun _ -> assert false); + propagate = slice_propagate st; + raise_conflict=slice_raise st; } + (* Assert that the conflict is indeeed a conflict *) + let check_is_conflict_ (c:Clause.t) : unit = + if not @@ Array.for_all (Atom.is_false) c.atoms then ( + let msg = + Format.asprintf "msat:core/internal: invalid conflict %a" Clause.debug c + in + raise (Invalid_argument msg); + ) + (* some boolean literals were decided/propagated within Msat. Now we need to inform the theory of those assumptions, so it can do its job. @return the conflict clause, if the theory detects unsatisfiability *) @@ -1662,18 +1688,11 @@ module Make(Plugin : PLUGIN) let slice = current_slice st in st.th_head <- st.elt_head; (* catch up *) match Plugin.assume st.th slice with - | Solver_intf.Th_sat -> + | () -> propagate st - | Solver_intf.Th_unsat (l, p) -> - (* conflict *) - let l = List.rev_map (create_atom st) l in - (* Assert that the conflcit is indeeed a conflict *) - if not @@ List.for_all (fun a -> a.neg.is_true) l then ( - raise (Invalid_argument "msat:core/internal: invalid conflict"); - ); - List.iter (fun a -> insert_var_order st (Elt.of_var a.var)) l; - (* Create the clause and return it. *) - let c = Clause.make l (Lemma p) in + | exception Th_conflict c -> + check_is_conflict_ c; + Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; Some c ) @@ -1892,14 +1911,18 @@ module Make(Plugin : PLUGIN) | E_sat -> assert (st.elt_head = Vec.size st.trail); begin match Plugin.if_sat st.th (full_slice st) with - | Solver_intf.Th_sat -> () - | Solver_intf.Th_unsat (l, p) -> - let atoms = List.rev_map (create_atom st) l in - let c = Clause.make atoms (Lemma p) in + | () -> + if st.elt_head = Vec.size st.trail && + Stack.is_empty st.clauses_to_add then ( + raise_notrace E_sat + ); + (* otherwise, keep on *) + | exception Th_conflict c -> + check_is_conflict_ c; + Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); Stack.push c st.clauses_to_add end; - if Stack.is_empty st.clauses_to_add then raise_notrace E_sat end done with E_sat -> () @@ -2082,8 +2105,8 @@ module Make_pure_sat(F: Solver_intf.FORMULA) = type proof = Solver_intf.void type level = unit let current_level () = () - let assume () _ = Solver_intf.Th_sat - let if_sat () _ = Solver_intf.Th_sat + let assume () _ = () + let if_sat () _ = () let backtrack () _ = () let eval () _ = Solver_intf.Unknown let assign () t = t diff --git a/src/core/Msat.ml b/src/core/Msat.ml index daee1b7c..1dbd99a3 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -27,9 +27,21 @@ type 'clause export = 'clause Solver_intf.export = { hyps : 'clause Vec.t; history : 'clause Vec.t; } -type ('formula, 'proof) th_res = ('formula, 'proof) Solver_intf.th_res = - | Th_sat - | Th_unsat of 'formula list * 'proof + +type ('term, 'formula) assumption = ('term, 'formula) Solver_intf.assumption = + | Lit of 'formula + | Assign of 'term * 'term (** The first term is assigned to the second *) + +type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.reason = + | Eval of 'term list + | Consequence of 'formula list * 'proof + +type ('term, 'formula, 'proof) slice = ('term, 'formula, 'proof) Solver_intf.slice = { + iter_assumptions: (('term,'formula) assumption -> unit) -> unit; + push : ?keep:bool -> 'formula list -> 'proof -> unit; + raise_conflict: 'b. 'formula list -> 'proof -> 'b; + propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; +} type negated = Solver_intf.negated = Negated | Same_sign diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 354e9f6c..5f209785 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -68,39 +68,40 @@ type 'term eval_res = - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) *) -type ('formula, 'proof) th_res = - | Th_sat - (** The current set of assumptions is satisfiable. *) - | Th_unsat of 'formula list * 'proof - (** The current set of assumptions is *NOT* satisfiable, and here is a - theory tautology (with its proof), for which every litteral is false - under the current assumptions. *) -(** Type returned by the theory. Formulas in the unsat clause must come from the - current set of assumptions, i.e must have been encountered in a slice. *) - type ('term, 'formula) assumption = - | Lit of 'formula (** The given formula is asserted true by the solver *) + | Lit of 'formula (** The given formula is asserted true by the solver *) | Assign of 'term * 'term (** The first term is assigned to the second *) (** Asusmptions made by the core SAT solver. *) type ('term, 'formula, 'proof) reason = - | Eval of 'term list (** The formula can be evalutaed using the terms in the list *) - | Consequence of 'formula list * 'proof (** [Consequence (l, p)] means that the formulas in [l] imply - the propagated formula [f]. The proof should be a proof of - the clause "[l] implies [f]". *) + | Eval of 'term list + (** The formula can be evalutaed using the terms in the list *) + | Consequence of 'formula list * 'proof + (** [Consequence (l, p)] means that the formulas in [l] imply the propagated + formula [f]. The proof should be a proof of the clause "[l] implies [f]". + *) (** The type of reasons for propagations of a formula [f]. *) +(* TODO: find a way to use atoms instead of formulas here *) type ('term, 'formula, 'proof) slice = { - start : int; (** Start of the slice *) - length : int; (** Length of the slice *) - get : int -> ('term, 'formula) assumption; (** Accessor for the assertions in the slice. - Should only be called on integers [i] s.t. - [start <= i < start + length] *) - push : 'formula list -> 'proof -> unit; (** Add a clause to the solver. *) - propagate : 'formula -> - ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can - evaluate the formula to be true (see the - definition of {!type:eval_res} *) + iter_assumptions: (('term,'formula) assumption -> unit) -> unit; + (** Traverse the new assumptions on the boolean trail. *) + + push : ?keep:bool -> 'formula list -> 'proof -> unit; + (** Add a clause to the solver. + @param keep if true, the clause will be kept by the solver. + Otherwise the solver is allowed to GC the clause and propose this + partial model again. + *) + + raise_conflict: 'b. 'formula list -> 'proof -> 'b; + (** Raise a conflict, yielding control back to the solver. + The list of atoms must be a valid theory lemma that is false in the + current trail. *) + + propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; + (** Propagate a formula, i.e. the theory can evaluate the formula to be true + (see the definition of {!type:eval_res} *) } (** The type for a slice of assertions to assume/propagate in the theory. *) @@ -174,19 +175,19 @@ module type PLUGIN_CDCL_T = sig (** Return the current level of the theory (either the empty/beginning state, or the last level returned by the [assume] function). *) - val assume : t -> (void, Formula.t, proof) slice -> (Formula.t, proof) th_res - (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, - and returns the result of the new assumptions. *) + val assume : t -> (void, Formula.t, proof) slice -> unit + (** Assume the formulas in the slice, possibly pushing new formulas to be + propagated or raising a conflict. *) - val if_sat : t -> (void, Formula.t, proof) slice -> (Formula.t, proof) th_res - (** Called at the end of the search in case a model has been found. If no new clause is - pushed and the function returns [Sat], then proof search ends and 'sat' is returned, - else search is resumed. *) + val if_sat : t -> (void, Formula.t, proof) slice -> unit + (** Called at the end of the search in case a model has been found. + If no new clause is pushed, then proof search ends and 'sat' is returned; + if lemmas are added, search is resumed; + if a conflict clause is added, search backtracks and then resumes. *) val backtrack : t -> level -> unit (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the same state as when it returned the value [l], *) - end (** Signature for theories to be given to the Model Constructing Solver. *) @@ -203,14 +204,15 @@ module type PLUGIN_MCSAT = sig (** Return the current level of the theory (either the empty/beginning state, or the last level returned by the [assume] function). *) - val assume : t -> (Term.t, Formula.t, proof) slice -> (Formula.t, proof) th_res - (** Assume the formulas in the slice, possibly pushing new formulas to be propagated, - and returns the result of the new assumptions. *) + val assume : t -> (Term.t, Formula.t, proof) slice -> unit + (** Assume the formulas in the slice, possibly pushing new formulas to be + propagated or raising a conflict. *) - val if_sat : t -> (Term.t, Formula.t, proof) slice -> (Formula.t, proof) th_res - (** Called at the end of the search in case a model has been found. If no new clause is - pushed and the function returns [Sat], then proof search ends and 'sat' is returned, - else search is resumed. *) + val if_sat : t -> (Term.t, Formula.t, proof) slice -> unit + (** Called at the end of the search in case a model has been found. + If no new clause is pushed, then proof search ends and 'sat' is returned; + if lemmas are added, search is resumed; + if a conflict clause is added, search backtracks and then resumes. *) val backtrack : t -> level -> unit (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the From cf6147c500698221f3d7b56ebd2892d7342eb39d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 17:53:14 -0600 Subject: [PATCH 074/182] details --- src/core/Internal.ml | 3 +++ src/core/Solver_intf.ml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 673e47a3..314d2707 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1376,6 +1376,9 @@ module Make(Plugin : PLUGIN) | _ -> assert false done; List.iter (fun q -> Var.clear q.var) !seen; + (* put high-level literals first, so that: + - they make adequate watch lits + - the first literal is the UIP, if any *) let l = List.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) !learnt in let level, is_uip = backtrack_lvl st l in { cr_backtrack_lvl = level; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 5f209785..a51fee18 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -87,7 +87,7 @@ type ('term, 'formula, 'proof) slice = { iter_assumptions: (('term,'formula) assumption -> unit) -> unit; (** Traverse the new assumptions on the boolean trail. *) - push : ?keep:bool -> 'formula list -> 'proof -> unit; + push: ?keep:bool -> 'formula list -> 'proof -> unit; (** Add a clause to the solver. @param keep if true, the clause will be kept by the solver. Otherwise the solver is allowed to GC the clause and propose this @@ -99,7 +99,7 @@ type ('term, 'formula, 'proof) slice = { The list of atoms must be a valid theory lemma that is false in the current trail. *) - propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; + propagate: 'formula -> ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) } From a5ec88f2a7d6feac0de84a52b559b2c01208b994 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 19:27:25 -0600 Subject: [PATCH 075/182] refactor: use `Var.mark` and a pre-allocated vec for `analyze` --- src/core/Internal.ml | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 314d2707..e8a1e4da 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -152,9 +152,9 @@ module Make(Plugin : PLUGIN) (v.lid+1) debug_assign v Term.pp v.term end - let seen_var = 0b1 - let seen_pos = 0b10 - let seen_neg = 0b100 + let seen_var = 0x1 + let seen_pos = 0x2 + let seen_neg = 0x4 module Var = struct type t = var @@ -762,6 +762,9 @@ module Make(Plugin : PLUGIN) order : H.t; (* Heap ordered by variable activity *) + to_clear: var Vec.t; + (* variables to unmark *) + mutable var_incr : float; (* increment for variables' activity *) @@ -787,6 +790,7 @@ module Make(Plugin : PLUGIN) clauses_learnt = Vec.create(); clauses_to_add = Stack.create (); + to_clear=Vec.create(); th_head = 0; elt_head = 0; @@ -1285,24 +1289,26 @@ module Make(Plugin : PLUGIN) cr_is_uip: bool; (* conflict is UIP? *) } - let get_atom st i = + let[@inline] get_atom st i = match Vec.get st.trail i with - | Lit _ -> assert false | Atom x -> x + | Atom x -> x + | Lit _ -> assert false (* conflict analysis for SAT Same idea as the mcsat analyze function (without semantic propagations), except we look the the Last UIP (TODO: check ?), and do it in an imperative and efficient manner. *) - let analyze_sat st c_clause : conflict_res = + let analyze st c_clause : conflict_res = let pathC = ref 0 in let learnt = ref [] in let cond = ref true in let blevel = ref 0 in - let seen = ref [] in (* for cleanup *) + let seen = st.to_clear in (* for cleanup *) let c = ref (Some c_clause) in let tr_ind = ref (Vec.size st.trail - 1) in let history = ref [] in assert (decision_level st > 0); + Vec.clear seen; let conflict_level = Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in @@ -1329,10 +1335,9 @@ module Make(Plugin : PLUGIN) | Some Bcp cl -> history := cl :: !history | _ -> assert false ); - if not (Var.seen_both q.var) then ( - Atom.mark q; - Atom.mark q.neg; - seen := q :: !seen; + if not (Var.marked q.var) then ( + Var.mark q.var; + Vec.push seen q.var; if q.var.v_level > 0 then ( var_bump_activity st q.var; if q.var.v_level >= conflict_level then ( @@ -1352,7 +1357,7 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k -> k " looking at: %a" Trail_elt.debug a); match a with | Atom q -> - (not (Var.seen_both q.var)) || + (not (Var.marked q.var)) || (q.var.v_level < conflict_level) | Lit _ -> true do @@ -1375,7 +1380,7 @@ module Make(Plugin : PLUGIN) c := Some cl | _ -> assert false done; - List.iter (fun q -> Var.clear q.var) !seen; + Vec.iter Var.clear seen; (* put high-level literals first, so that: - they make adequate watch lits - the first literal is the UIP, if any *) @@ -1387,9 +1392,6 @@ module Make(Plugin : PLUGIN) cr_is_uip = is_uip; } - let[@inline] analyze st c_clause : conflict_res = - analyze_sat st c_clause - (* add the learnt clause to the clause database, propagate, etc. *) let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = begin match cr.cr_learnt with From 14319f959f9977edf98402819d40710fe5bfeb61 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 20:05:56 -0600 Subject: [PATCH 076/182] refactor: a bit of cleanup in analyze --- src/core/Internal.ml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e8a1e4da..b88c89dc 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1303,12 +1303,12 @@ module Make(Plugin : PLUGIN) let learnt = ref [] in let cond = ref true in let blevel = ref 0 in - let seen = st.to_clear in (* for cleanup *) + let to_unmark = st.to_clear in (* for cleanup *) let c = ref (Some c_clause) in let tr_ind = ref (Vec.size st.trail - 1) in let history = ref [] in assert (decision_level st > 0); - Vec.clear seen; + Vec.clear to_unmark; let conflict_level = Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in @@ -1337,7 +1337,7 @@ module Make(Plugin : PLUGIN) ); if not (Var.marked q.var) then ( Var.mark q.var; - Vec.push seen q.var; + Vec.push to_unmark q.var; if q.var.v_level > 0 then ( var_bump_activity st q.var; if q.var.v_level >= conflict_level then ( @@ -1369,7 +1369,7 @@ module Make(Plugin : PLUGIN) match !pathC, p.var.reason with | 0, _ -> cond := false; - learnt := p.neg :: (List.rev !learnt) + learnt := p.neg :: List.rev !learnt | n, Some Semantic -> assert (n > 0); learnt := p.neg :: !learnt; @@ -1380,7 +1380,8 @@ module Make(Plugin : PLUGIN) c := Some cl | _ -> assert false done; - Vec.iter Var.clear seen; + Vec.iter Var.clear to_unmark; + Vec.clear to_unmark; (* put high-level literals first, so that: - they make adequate watch lits - the first literal is the UIP, if any *) From 5e1508ff2bc59437c3a434363894cd02f77270d5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:00:35 -0600 Subject: [PATCH 077/182] refactor: use a vec for the new clauses --- src/core/Internal.ml | 35 +- src/sudoku/dune | 9 + src/sudoku/sudoku_solve.ml | 346 +++++++++ sudoku_solve.sh | 4 + tests/sudoku/Makefile | 10 + tests/sudoku/sudoku.txt | 20 + tests/sudoku/top1465.txt | 1466 ++++++++++++++++++++++++++++++++++++ 7 files changed, 1874 insertions(+), 16 deletions(-) create mode 100644 src/sudoku/dune create mode 100644 src/sudoku/sudoku_solve.ml create mode 100755 sudoku_solve.sh create mode 100644 tests/sudoku/Makefile create mode 100644 tests/sudoku/sudoku.txt create mode 100644 tests/sudoku/top1465.txt diff --git a/src/core/Internal.ml b/src/core/Internal.ml index b88c89dc..867fcf3e 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -721,8 +721,7 @@ module Make(Plugin : PLUGIN) clauses_learnt : clause Vec.t; (* learnt clauses (tautologies true at any time, whatever the user level) *) - (* TODO: make this a Vec.t *) - clauses_to_add : clause Stack.t; + clauses_to_add : clause Vec.t; (* Clauses either assumed or pushed by the theory, waiting to be added. *) mutable unsat_at_0: clause option; @@ -789,7 +788,7 @@ module Make(Plugin : PLUGIN) clauses_hyps = Vec.create(); clauses_learnt = Vec.create(); - clauses_to_add = Stack.create (); + clauses_to_add = Vec.create (); to_clear=Vec.create(); th_head = 0; @@ -1505,13 +1504,14 @@ module Make(Plugin : PLUGIN) Vec.push vec init; Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) - let flush_clauses st = - if not (Stack.is_empty st.clauses_to_add) then ( - while not (Stack.is_empty st.clauses_to_add) do - let c = Stack.pop st.clauses_to_add in - add_clause_ st c - done - ) + let[@inline never] flush_clauses_ st = + while not @@ Vec.is_empty st.clauses_to_add do + let c = Vec.pop st.clauses_to_add in + add_clause_ st c + done + + let[@inline] flush_clauses st = + if not @@ Vec.is_empty st.clauses_to_add then flush_clauses_ st type watch_res = | Watch_kept @@ -1616,7 +1616,7 @@ module Make(Plugin : PLUGIN) let c = Clause.make atoms (Lemma lemma) in if not keep then Clause.set_learnt c true; Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); - Stack.push c st.clauses_to_add + Vec.push st.clauses_to_add c let slice_raise st (l:formula list) proof : 'a = let atoms = List.rev_map (create_atom st) l in @@ -1634,7 +1634,7 @@ module Make(Plugin : PLUGIN) let c = Clause.make (p :: List.map Atom.neg l) (Lemma proof) in if p.is_true then () else if p.neg.is_true then ( - Stack.push c st.clauses_to_add + Vec.push st.clauses_to_add c ) else ( insert_subterms_order st p.var; let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in @@ -1695,6 +1695,7 @@ module Make(Plugin : PLUGIN) st.th_head <- st.elt_head; (* catch up *) match Plugin.assume st.th slice with | () -> + flush_clauses st; propagate st | exception Th_conflict c -> check_is_conflict_ c; @@ -1919,15 +1920,17 @@ module Make(Plugin : PLUGIN) begin match Plugin.if_sat st.th (full_slice st) with | () -> if st.elt_head = Vec.size st.trail && - Stack.is_empty st.clauses_to_add then ( + Vec.is_empty st.clauses_to_add then ( raise_notrace E_sat ); (* otherwise, keep on *) + flush_clauses st; | exception Th_conflict c -> check_is_conflict_ c; Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); - Stack.push c st.clauses_to_add + Vec.push st.clauses_to_add c; + flush_clauses st; end; end done @@ -1939,7 +1942,7 @@ module Make(Plugin : PLUGIN) let atoms = List.rev_map (mk_atom st) l in let c = Clause.make atoms Hyp in Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" Clause.debug c); - Stack.push c st.clauses_to_add) + Vec.push st.clauses_to_add c) cnf (* Check satisfiability *) @@ -1967,7 +1970,7 @@ module Make(Plugin : PLUGIN) false let check st : bool = - Stack.is_empty st.clauses_to_add && + Vec.is_empty st.clauses_to_add && check_vec st.clauses_hyps && check_vec st.clauses_learnt diff --git a/src/sudoku/dune b/src/sudoku/dune new file mode 100644 index 00000000..0800a980 --- /dev/null +++ b/src/sudoku/dune @@ -0,0 +1,9 @@ + +(executable + (name sudoku_solve) + (modes native) + (libraries msat sequence containers) + (flags :standard -warn-error -a -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) +) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml new file mode 100644 index 00000000..5fbd1347 --- /dev/null +++ b/src/sudoku/sudoku_solve.ml @@ -0,0 +1,346 @@ + +(** {1 simple sudoku solver} *) + +module Fmt = CCFormat +module Log = Msat.Log +module Vec = Msat.Vec + +let errorf msg = CCFormat.kasprintf failwith msg + +module Cell : sig + type t = private int + val equal : t -> t -> bool + val neq : t -> t -> bool + val hash : t -> int + val empty : t + val is_empty : t -> bool + val is_full : t -> bool + val make : int -> t + val pp : t Fmt.printer +end = struct + type t = int + let empty = 0 + let[@inline] make i = assert (i >= 0 && i <= 9); i + let[@inline] is_empty x = x = 0 + let[@inline] is_full x = x > 0 + let hash = CCHash.int + let[@inline] equal (a:t) b = a=b + let[@inline] neq (a:t) b = a<>b + let pp out i = if i=0 then Fmt.char out '.' else Fmt.int out i +end + +module Grid : sig + type t + + val get : t -> int -> int -> Cell.t + val set : t -> int -> int -> Cell.t -> t + + (** A set of related cells *) + type set = (int*int*Cell.t) Sequence.t + + val rows : t -> set Sequence.t + val cols : t -> set Sequence.t + val squares : t -> set Sequence.t + + val all_cells : t -> (int*int*Cell.t) Sequence.t + + val parse : string -> t + val is_full : t -> bool + val is_valid : t -> bool + val pp : t Fmt.printer +end = struct + type t = Cell.t array + + let[@inline] get (s:t) i j = s.(i*9 + j) + + let[@inline] set (s:t) i j n = + let s' = Array.copy s in + s'.(i*9 + j) <- n; + s' + + (** A set of related cells *) + type set = (int*int*Cell.t) Sequence.t + + open Sequence.Infix + + let all_cells (g:t) = + 0 -- 8 >>= fun i -> + 0 -- 8 >|= fun j -> (i,j,get g i j) + + let rows (g:t) = + 0 -- 8 >|= fun i -> + ( 0 -- 8 >|= fun j -> (i,j,get g i j)) + + let cols g = + 0 -- 8 >|= fun j -> + ( 0 -- 8 >|= fun i -> (i,j,get g i j)) + + let squares g = + 0 -- 2 >>= fun sq_i -> + 0 -- 2 >|= fun sq_j -> + ( 0 -- 2 >>= fun off_i -> + 0 -- 2 >|= fun off_j -> + let i = 3*sq_i + off_i in + let j = 3*sq_j + off_j in + (i,j,get g i j)) + + let is_full g = Array.for_all Cell.is_full g + + let is_valid g = + let all_distinct (s:set) = + (s >|= fun (_,_,c) -> c) + |> Sequence.diagonal + |> Sequence.for_all (fun (c1,c2) -> Cell.neq c1 c2) + in + Sequence.for_all all_distinct @@ rows g && + Sequence.for_all all_distinct @@ cols g && + Sequence.for_all all_distinct @@ squares g + + let pp out g = + Fmt.fprintf out "@["; + Array.iteri + (fun i n -> + Cell.pp out n; + if i mod 9 = 8 then Fmt.fprintf out "@,") + g; + Fmt.fprintf out "@]" + + let parse (s:string) : t = + if String.length s < 81 then ( + errorf "line is too short, expected 81 chars, not %d" (String.length s); + ); + let a = Array.make 81 Cell.empty in + for i = 0 to 80 do + let c = String.get s i in + let n = if c = '.' then 0 else Char.code c - Char.code '0' in + if n < 0 || n > 9 then errorf "invalid char %c" c; + a.(i) <- Cell.make n + done; + a +end + +(** Backtrackable ref *) +module B_ref : sig + type 'a t + val create : 'a -> 'a t + val set : 'a t -> 'a -> unit + val get : 'a t -> 'a + val update : 'a t -> ('a -> 'a) -> unit + val push_level : _ t -> unit + val pop_levels : _ t -> int -> unit +end = struct + type 'a t = { + mutable cur: 'a; + stack: 'a Vec.t; + } + + let create x: _ t = {cur=x; stack=Vec.create()} + + let[@inline] get self = self.cur + let[@inline] set self x = self.cur <- x + let[@inline] update self f = self.cur <- f self.cur + + let[@inline] push_level self : unit = Vec.push self.stack self.cur + let pop_levels self n : unit = + assert (n>=0 && n <= Vec.size self.stack); + let i = Vec.size self.stack-n in + let x = Vec.get self.stack i in + self.cur <- x; + Vec.shrink self.stack i; + () +end + +module Solver : sig + type t + val create : Grid.t -> t + val solve : t -> Grid.t option +end = struct + open Msat.Solver_intf + + (* formulas *) + module F = struct + type t = bool*int*int*Cell.t + let equal (sign1,x1,y1,c1)(sign2,x2,y2,c2) = + sign1=sign2 && x1=x2 && y1=y2 && Cell.equal c1 c2 + let hash (sign,x,y,c) = CCHash.(combine4 (bool sign)(int x)(int y)(Cell.hash c)) + let pp out (sign,x,y,c) = + Fmt.fprintf out "[@[(%d,%d) %s %a@]]" x y (if sign then "=" else "!=") Cell.pp c + let neg (sign,x,y,c) = (not sign,x,y,c) + let norm ((sign,_,_,_) as f) = + if sign then f, Same_sign else neg f, Negated + + let make sign x y (c:Cell.t) : t = (sign,x,y,c) + end + + module Theory = struct + type proof = unit + module Formula = F + type t = { + grid: Grid.t B_ref.t; + } + + let create g : t = {grid=B_ref.create g} + let[@inline] grid self : Grid.t = B_ref.get self.grid + let[@inline] set_grid self g : unit = B_ref.set self.grid g + + let push_level self = B_ref.push_level self.grid + let pop_levels self n = B_ref.pop_levels self.grid n + + let pp_c_ = Fmt.(list ~sep:(return "@ ∨ ")) F.pp + let[@inline] logs_conflict kind c : unit = + Log.debugf 4 (fun k->k "(@[conflict.%s@ %a@])" kind pp_c_ c) + + (* check that all cells are full *) + let check_full_ (self:t) acts : unit = + Grid.all_cells (grid self) + (fun (x,y,c) -> + if Cell.is_empty c then ( + let c = + CCList.init 9 + (fun c -> F.make true x y (Cell.make (c+1))) + in + Log.debugf 4 (fun k->k "(@[add-clause@ %a@])" pp_c_ c); + acts.push ~keep:true c (); + )) + + (* check constraints *) + let check_ (self:t) acts : unit = + Log.debugf 4 (fun k->k "(@[sudoku.check@ @[:g %a@]@])" Grid.pp (B_ref.get self.grid)); + let[@inline] all_diff kind f = + let pairs = + f (grid self) + |> Sequence.flat_map + (fun set -> + set + |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) + |> Sequence.diagonal) + in + pairs + (fun ((x1,y1,c1),(x2,y2,c2)) -> + if Cell.equal c1 c2 then ( + assert (x1<>x2 || y1<>y2); + let c = [F.make false x1 y1 c1; F.make false x2 y2 c2] in + logs_conflict ("all-diff." ^ kind) c; + acts.raise_conflict c () + )) + in + all_diff "rows" Grid.rows; + all_diff "cols" Grid.cols; + all_diff "squares" Grid.squares; + () + + let trail_ acts = + acts.iter_assumptions + |> Sequence.map + (function + | Assign _ -> assert false + | Lit f -> f) + + (* update current grid with the given slice *) + let add_slice (self:t) acts : unit = + trail_ acts + (function + | false,_,_,_ -> () + | true,x,y,c -> + assert (Cell.is_full c); + let grid = grid self in + let c' = Grid.get grid x y in + if Cell.is_empty c' then ( + set_grid self (Grid.set grid x y c); + ) else if Cell.neq c c' then ( + (* conflict: at most one value *) + let c = [F.make false x y c; F.make false x y c'] in + logs_conflict "at-most-one" c; + acts.raise_conflict c () + ) + ) + + let assume (self:t) acts : unit = + Log.debugf 4 + (fun k->k "(@[sudoku.assume@ :trail [@[%a@]]@])" (Fmt.seq F.pp) (trail_ acts)); + add_slice self acts; + check_ self acts + + let if_sat (self:t) acts : unit = + Log.debugf 4 (fun k->k "(@[sudoku.if-sat@])"); + check_full_ self acts; + check_ self acts + + end + + module S = Msat.Make_cdcl_t(Theory) + + type t = { + grid0: Grid.t; + solver: S.t; + } + + let solve (self:t) : _ option = + let assumptions = + Grid.all_cells self.grid0 + |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) + |> Sequence.map (fun (x,y,c) -> F.make true x y c) + |> Sequence.map (S.make_atom self.solver) + |> Sequence.to_rev_list + in + Log.debugf 2 + (fun k->k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list S.Atom.pp) assumptions); + let r = + match S.solve self.solver ~assumptions () with + | S.Sat _ -> Some (Theory.grid (S.theory self.solver)) + | S.Unsat _ -> None + in + (* TODO: print some stats *) + r + + let create g : t = + { solver=S.create (Theory.create g); grid0=g } +end + +let solve_grid (g:Grid.t) : Grid.t option = + let s = Solver.create g in + Solver.solve s + +let solve_file file = + let grids = + CCIO.with_in file CCIO.read_lines_l + |> CCList.filter_map + (fun s -> + let s = String.trim s in + if s="" then None + else match Grid.parse s with + | g -> Some g + | exception e -> + errorf "cannot parse sudoku %S: %s@." s (Printexc.to_string e)) + in + List.iter + (fun g -> + Format.printf "@[@,#########################@,@[<2>solve grid:@ %a@]@]@." Grid.pp g; + let start=Sys.time() in + match solve_grid g with + | None -> Format.printf "no solution (in %.3fs)@." (Sys.time()-.start) + | Some g' when not @@ Grid.is_full g' -> + errorf "grid %a@ is not full" Grid.pp g' + | Some g' when not @@ Grid.is_valid g' -> + errorf "grid %a@ is not valid" Grid.pp g' + | Some g' -> + Format.printf "@[@[<2>solution (in %.3fs):@ %a@]@,###################@]@." + (Sys.time()-.start) Grid.pp g') + grids + +let () = + Fmt.set_color_default true; + let files = ref [] in + let debug = ref 0 in + let opts = [ + "--debug", Arg.Set_int debug, " debug"; + "-d", Arg.Set_int debug, " debug"; + ] |> Arg.align in + Arg.parse opts (fun f -> files := f :: !files) "sudoku_solve [options] "; + Msat.Log.set_debug !debug; + try + List.iter (fun f -> solve_file f) !files; + with + | Failure msg | Invalid_argument msg -> + Format.printf "@{Error@}:@.%s@." msg; + exit 1 diff --git a/sudoku_solve.sh b/sudoku_solve.sh new file mode 100755 index 00000000..93e8a2fa --- /dev/null +++ b/sudoku_solve.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +#exec dune exec src/sudoku/sudoku_solve.exe -- $@ +exec dune exec --profile=release src/sudoku/sudoku_solve.exe -- $@ diff --git a/tests/sudoku/Makefile b/tests/sudoku/Makefile new file mode 100644 index 00000000..35f65b03 --- /dev/null +++ b/tests/sudoku/Makefile @@ -0,0 +1,10 @@ + +sudoku17.txt: + wget https://github.com/attractivechaos/plb/releases/download/v0/sudoku17.txt.gz -O sudoku17.txt.gz + gunzip -f sudoku17.txt.gz + +top50k.txt: + wget https://github.com/attractivechaos/plb/releases/download/v0/sudoku.top50k.gz -O top50k.txt.gz + gunzip -f top50k.txt.gz + +fetch-all: sudoku17.txt top50k.txt diff --git a/tests/sudoku/sudoku.txt b/tests/sudoku/sudoku.txt new file mode 100644 index 00000000..2ad4c03c --- /dev/null +++ b/tests/sudoku/sudoku.txt @@ -0,0 +1,20 @@ +..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) +.......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) +.2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) +........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) +12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) +1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) +.......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) +12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 +..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. +1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. +..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. +....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) +4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) +7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... +3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... +........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) +.......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 +.......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. +1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 +.....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... diff --git a/tests/sudoku/top1465.txt b/tests/sudoku/top1465.txt new file mode 100644 index 00000000..d400fabe --- /dev/null +++ b/tests/sudoku/top1465.txt @@ -0,0 +1,1466 @@ +4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ +7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... +7.8...3.....6.1...5.........4.....263...8.......1...9..9.2....4....7.5........... +3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... +5..7..6....38...........2..62.4............917............35.8.4.....1......9.... +4..7..6....38...........2..62.5............917............43.8.5.....1......9.... +.4..1.2.......9.7..1..........43.6..8......5....2.....7.5..8......6..3..9........ +7.5.....2...4.1...3.........1.6..4..2...5...........9....37.....8....6...9.....8. +......41.9..3.....3...5.....48..7..........62.1.......6..2....5.7....8......9.... +7.5.....2...4.1...3.........1.6..4..2...5...........9....37.....9....8...8.....6. +.8..1......5....3.......4.....6.5.7.89....2.....3.....2.....1.9..67........4..... +8.9...3.....7.1...5.........7.....263...9.......1...4..6.2....4....8.5........... +6.9.....8...7.1...4............6...4.2.....3..3....5...1.5...7.8...9..........2.. +......41.9..3.....3...2.....48..7..........52.1.......5..2....6.7....8......9.... +1...48....5....9....6...3.....57.2..8.3.........9............4167..........2..... +7.8...3.....6.1...4.........6.....253...8.......1...9..9.5....2....7.4........... +4.3.....2...6.1...8...........5..79.2...3.....1..........84.....9....6...7.....5. +.1.62....5......43....9....7......8...5.....4...1..........36...9....2..8....7... +4.3.....2...6.1...8...........5..97.2...3.....1..........84.....9....6...7.....5. +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....1....9...4.....7. +....2..4..7...6....1.5.....2......8....3..7..4.9.........6..1.38...9..........5.. +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....4....7...1.....9. +....4...1.3.6.....8........1.9..5.........87....2......7....26.5...94.........3.. +3.7..4.2....1..8..9............3..9..5.8......4.6...........5.12...7..........6.. +...9.31..6.7....8.2.........5....4......6..2..1.......8...7.......3..5.....4....9 +8.5.....2...4.1...3.........6.7..4..2...5...........9....38.....1....7...9.....6. +7.4.....2...8.1...3.........5.6..1..2...4...........9....37.....9....5...8.....6. +7.4.....2...8.1...3.........5.6..1..2...4...........9....37.....8....6...9.....5. +..1.....7...89..........6..26..3.......5...749...........1.4.5.83.............2.. +2...4.5...1.....3............6...8.2.7.3.9......1.....4...5.6.....7...9...8...... +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....1....7...4.....9. +.8.....63....4.2............1.8.35..7.....9.....6.....2.9.7...........354........ +.9.3...2.....7.5...1.......7.86..4.....9.2...5...........1...634...8............. +...9.31..5.7....8.2.........4....6......5..2..1.......8...7.......6..4.....3....9 +5.8.....7...9.1...4............5...4.6.....3..9....6...2.3...1.7...8..........2.. +.......71.2.8........5.3...7.9.6.......2..8..1.........3...25..6...1..4.......... +......41.6..3.....3...2.....49..8..........52.1.......5..6....7.8....9......3.... +5..6.3....2....98.......1...1..9.......3....67.......4....8.25.4..7.............. +......41.9..3.....3...5.....48..7..........52.1.......6..2....5.7....8......9.... +7.4.....2...8.1...3.........5.6..1..2...4...........5....37.....9....6...8.....9. +4.35...2.....16...7............895.....3..8..2...........4...7..9....6...1....... +.5.4.9......6....12.....3..7.3...2.....5...9.1.........68....4.....8........7.... +......41.9..2.....3...5.....48..7..........62.1.......6..5....3.7....8......9.... +5.7....3.....61...1.8......62..4.......7...8...........1....6.43..5...........2.. +7.....48....6.1..........2....3..6.52...8..............53.....1.6.1.........4.7.. +6..1...8..53.............4....8...6..9....7....24.........7.3.9....2.5..1........ +...9.3.5.2.....7............59..1......4..6...43......4..67...........91....2.... +.6..5.4.3.2.1...........7..4.3...6..7..5........2.........8..5.6...4...........1. +6.9.....8...3.1...4............6...4.2.....3..7....5...1.5...7.8...9..........2.. +4.3.....2...7.1...9...........5..18.2...3.....8..........94.....7....6...6.....5. +8.5.....2...4.1...3.........6.7..4..2...5...........6....38.....9....7...1.....9. +....3..715..4.2............2..6..4...38.7..............7..8..1.6..5..2........... +4.3.....2...7.1...9...........5..81.2...3.....8..........94.....7....6...6.....5. +.......91.7..3....82..........1.5...3.....7.....9.......16...5...4.2....7.....8.. +48.3............71.2.......7.5....6....2..8.............1.76...3.....4......5.... +7.8.2...........913.........46..........3.7.....5......5.9.6......4...1.2.....8.. +..36......4.....8.9.....7..86.4...........1.5.2.......5...17...1...9...........2. +2.8.5.......7...4.3........5...2.9.......1......6......7.1.4.6.......3.2.1....... +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....4....6...9.....7. +4.35...2.....61...7............895.....3..8..2...........4...7..9....6...1....... +...3.9.7.8..4.....1........2..5..6...3.....4.....1....5.....8......2.1.....7....9 +.5.1.8.7.4..3.....2.........1.7...8.9.....4............3.....1.....4.2......5.6.. +.....1..8.9....3..2........5......84.7.63.......9.....1.4....5.....7.6.....2..... +85.....7.....41...3..........1...4.6.7.5...........2..742.6.......8...3.......... +..3...67.5.....3...4.......6..3......8......4...7....12......5.....98.......41... +.4.3............78.......5.7..65....5...9.....1....2.....1.43..8.......6...2..... +....3...27..4......3.....9..6..2....8.....7........1..4..7.1......6...35.....8... +4.3.....2...6.1...8...........5..79.2...3.....7..........84.....9....6...1.....5. +3..8.1....5....6.9......4..5..7...8..4..6...........2.2..3.........9.1....7...... +4.3.....2...6.1...8...........5..97.2...3.....7..........84.....9....6...1.....5. +4.1.6....3.....2........8..15.2.....6......1....9......2.7.8..........43.7....... +26.7...........4.15........839.........5...6....1......7.....2.....49.......3.8.. +42.....36....3.8...........7.8.1...........54..3.......5.4.6...1.....7.....2..... +...6.37...51...........2.......1..846..7............5.14.58....3.....2........... +.6..7.......8...2..1.............6.54..3...........7..2...6..4.8.31.........5.1.. +..1.....8...9..2.......3.......15.4..6....7..3............4..8572.6.....9........ +7.....4...2..7..8...3..8..9...5..3...6..2..9...1..7..6...3..9...3..4..6...9..1..5 +5.6...3.....8.1...2.........9..6.7...4.7...........2.....1...493...5...........8. +3.7..4.2....1..5..9............3..9..5.8......4.6...........8.12...7..........6.. +...6..1.....2...4..59......6.......47....9.......5..8....1..7.6.82............3.. +2...6...8.743.........2....62......1...4..5..8...........5..34......1..........7. +.......92.37.......4.......8...6.1.......43.......9...2...5...6...1..74....8..... +7.....48....6.1..........2....3..6.52...8..............63.....1.5.1.........4.7.. +56..2......3...9...............7..561......2...84........3.84..71..........9..... +......41.9..3.....3...2.....48..7..........62.1.......5..2....6.7....8......9.... +...6.37...51...........2.......1..546..7............8.14.58....3.....2........... +3..4.1....9....7...........6.41.........7.2.9......5......3..8.52..........8...6. +7...3........5.6....4....9.2.....7.1...9.8......4.....53....2.....1...8..6....... +...3..5...5..1..3...7..4..12.....4...6..9......1..6..28..7..2...9..8..5...5..9..7 +6.....7.5.3.8................52.3.8.1.9.........4.....42...........9.1......7.6.. +......41.9..3.....3...5.....48..7..........52.1.......6..2....5.7....8......3.... +...6..9.23.87.....4............95..17......8...........2..6.5.....4...3..1....... +1.37....5...6.4...8...........81.....9....7...6.....9..2....4..5...3...........2. +.4.7...6...39............57.......3.2...8.....19...57.6...4.....5.1......2...6.84 +26.7...........4.15........839.........5...6....1......7.....2.....43.......9.8.. +4.21......5.....78...3......7..5...6......1.....4.........67.5.2.....4..3........ +3..1...6...1.47.......5....68.3...........4.1.2.......4.5...7.....2...8.......... +6.1....3....8..2..9.........47...5......7........6.....8.5.2...3.......1...4...9. +.6..2...1...3...7..1.......3.49.....7.....2........5.8....586.........4.9........ +8.5.....2...4.1...3.........6.7..4..2...5...........6....38.....1....9...9.....7. +.2.....8.....4.6......1.....3.2.74..1.....5.....8.....2..3...7.4.5......6........ +....7..438.....5...........2..5.1..........64...8......34...6.....2..1...7..6.... +.2.3...6.....7.5...1.......7.86..4.....9.2...5...........1...394...8............. +5.3.9..........7.12........4...2..5..1.8......6.7........4.86.........3.9........ +5.3.9..........8.12........4...2..5..1.8......6.7........4.76.........3.9........ +.2.3...6.....7.5...1.......7.86..4.....9.2...5...........1...934...8............. +......31.7..5.....2...4.....39..8..........62.1.......5..6....7.8....9......5.... +...7....6.29.......1....3..7..56..........24....8.....4......35....91...8........ +......31.6..5.....2...4.....39..8..........42.1.......5..6....7.8....9......5.... +......12....3..6.....8.9.......2..6.3.8......7...5.....15...4.....7....9.4....... +..9.7........4.6....1....8....1.9.3.54..........8......5....7.62..3...........2.. +1..46...5.2....7......9.....3.7.8..........91...2........3..84.6........5........ +5..2..3......8..9...2..4..66..1..9...1.....4...7..3..87..3..4...9..7..2...5..6..9 +4.9...5.....8....27..3.........4.7...8.....6..1..........6..12.5...9...........3. +.5..7..83..4....6.....5....83.6........9..1...........5.7...4.....3.2...1........ +..1....8....2..5.....6.........41.3.62....7..........927.8.........3...45........ +.894............72.3.......1....5.6..4....9......1....5...6...7...3..8..2........ +...6..9.23.87.....4............59..17......8...........2..6.5.....4...3..1....... +7..63...........81....2....685..1......7..6...4..........8.4.5.2.....3........... +69....2.....7...4............73.4...5.....6.1...8........5..83.2...9........1.... +8.5.....2...9.1...3.........6.7..4..2...5...........1....38.....9....7...4.....6. +...7....2.8....1...9.......7.6....3.....91...2............5.84.3..6........4..5.. +69....2.....7...4............73.4...5.....9.1...8........5..83.2...6........1.... +56....4....72.................3...7861...............2....461..1...5......8....3. +4..6..3...1..2..6...8..7..19..8..5...4..5..1......2..75.....6...3..8..4......9..5 +7..69..........35.......1..2.9.....7...3...4.8.........4.5..6......28....1....... +46.....29....3.5...........1.5.4...........723.........7.6.2.....4...1.....9..... +.5.7.1...2.8....6...........4.3..5..9.2......6...8.....7.1..3......2..9.......... +.135.........7.4...........1......892.4.6....7.........8.1.....6.....2.....9.3... +5..4..8......9..1...2..1..56..3..4...5..7......4.....83..6..7...6.....8...8..2..1 +...4.5.2.81..............7.3...9.1....5....4...........6..1.8....27........2..9.. +.1..3..6.8..................63.9....7..8..4......1.5...9.....1.2..7........5..7.. +3..4.6....9....7...........6.41.........7.2.9......5......3..8.52..........8...1. +..9.2........4.6....1....8....1.9.3.54..........8......5....7.67..3...........2.. +...8...3...5...7.....1.........5.9..18.......3..4.......7..2..6....7.5...4.....1. +2.7.4.......6...3.9........4...2.8.......5......3......6.1.3.5.......2.9.1....... +1...6........2.7...53........98...........41.......6.742..........3.9......5...8. +.1.65..4.8.......9.......3.15........76..........28......7..5..3..4...........2.. +.7.3...6.....8.5...1.......8.96..4.....1.2...5...........7...324...9............. +.6..2...1...3...7..1.......3.49.....7.....2........5.8....856.........4.9........ +8.......24...3........5..1...1...56.......9.....7........8.4..7.6....3...9.2..... +.2.....9.....1.6..7...3....5.7...1.....2.4.8....9.....64.8.....3.....5........... +3..4...8.....5.2...........152.......7..1.......9...6..15...7..6..3.4............ +..8.17......5...3..........54.....2.63...........76.....6...8.72..4...........1.. +.2.....9.....1.3..7...4....6.7...1.....2.5.8....9.....45.8.....3.....6........... +.2.....74.98.1.............7..3.5..6....2.9..1..............83....6.7......4..... +6.....8.5.3.9................52.3.9.1.7.........4.....42...........8.1......7.6.. +...46..8.7.....9.....1.....4....53...8.6......2.......3.9.7...........215........ +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....4....7...9.....1. +6.81..........72.............3.5..615....4..........8.47....5.....63.....2....... +.71..8...5......2...............68.14...3..........7.....45..3..16.........2..... +7..4......5....3........1..368..........2..5....7........5...7213...8...6........ +.9.....2...5..4.......61.....6...1.8...3...7.......6..23.9.........8.4..7........ +......41.7..3.....3...5.....49..8..........52.1.......2..6....7.8....9......7.... +17.32...........51....8....5.6..1......7..3..4...........5.4.6..8....2........... +12......9.5..9.2.3........4.3.........18.........246......67.5...2.41.7.9........ +.8.....2...5..4.......61.....6...1.7...9...3.......6..23.8.........7.4..9........ +9.....7.1.2.4...........5..7.1..5....8.....3.............82..4.1.9.........39.... +8...3.....2......6....5.7..5.1...9.....2.4......6.....7.....39....1...8..4....... +63.2............857.........91.........8...4....7..6..2.....3......1...4....95... +1..5.3....7.....84...6.....5..1....2.2.....4..........6.....5......7.3....8.2.... +.9.3...6.....7.5...1.......7.86..4.....9.2...5...........1...324...8............. +4.....26..3..7.................8..536.1...8..........7...6.41...5.....8....2..... +...8....2.9....6...1.......8.7....3.....61...2............5.94.3..7........4..5.. +...6..2.18.39.....5.........7..2.....6....4.....5...8.....41..79...............3. +.64...9..1..2..................493..7......5.....56......5...17.43..............2 +...6.9.3.8.1.........7......6.5...7.....1.4......2....2.....5.4...3..8...9....... +.3..4.......5...9.9......2..6....8..5..7..........1...7.21.........8.3.6......4.. +.8..9....3......6....3...4.....1...5..2...9....7...8..65....1.....2.7........4... +.8..51......6...2...........3....8.55.74...........1..4.6....7.....35...2........ +.3.6..7......25....1.......2.9....8....4...3.5........8..79..........4.6......1.. +1...4........2.5...63........98...........45.......7.172..........3.9......6...8. +6...3.....2......7....5.8..3.1...9.....2.4......7.....8.....59....1...6..4....... +...7..2.18.39.....5.........4..6.....7....1.....5...8.....42..69...............3. +58..3...........219........76.2......4....8......9.5.....4.1.7.3...........6..... +..1....8....2..5.....6.........14.3.62....7..........927.8.........3...45........ +3..4.9....8....6...........4.91.........6.2.8......5......3..7.52..........7...1. +1...9..6..5.....4..6..3.......5.7..28..4.....9.........732...........8........1.. +8.75.........9.6..4.........6....2.95..7........4..3...3..2......1....8........1. +8..5..4......2..9..........5..3..1...76.........4.........69.241.....3......7.... +5..7.........3..7...8..5..36..1..4...8..2..9...9..8..1...4..3...6..9..4...2..6..9 +......8...7..9..1...3..7..51..6......5..2..4...8..9..74.....2...6..1..5......6..3 +7.2...4.....8.96.....5......8....5..3...1........4..7.1......32...6......9....... +.19.6....2.....7...........3..4.1..........91....5..6....2.34...51.........7..... +63.2............857.........91.........8...4....7..6..2.....3......1...4....59... +6.2...7.....8.95.....4......8....4..3...7........3..1.1......26...5......9....... +6.....3...5..9..8...2..6..98.....7...7..5..4......1..51..3..5...4..2..6...8..7..2 +1.....54....9......7..........6.7..9..82.....4.3......62...........1..5.....3.8.. +..84...3....3.....9....157479...8........7..514.....2...9.6...2.5....4......9..56 +2...4.3......1..75.........8..3.9..........54...2......15...6.....8..9...7....... +...3...1..92.......4...........28.........7.3...5...6....79.4..8.....25.1........ +.19....5.4..2..................91.......5..6.7.....3.....3..4.7.61............2.. +.342...5.....87....1.........24...3..5....2..6...........3....12.....7..8........ +63.2............857.........91.........8...4....7..6..2.....3......5...4....19... +36.1...5.....4.29......7...61............94..8..........2.5.......8....6........3 +.2.8..3.....7.....9............6.74..3.2......1.....5.4.5.9..........8.16........ +2..7..1......5..9...1..4..3......8......4..6...9..6..27..5..6...5..7..3...6..3..1 +2.65....8....413..9............5.73.8..6...............7....4.....2.9....1....... +8.5.....2...9.1...3.........6.7..4..2...5...........6....38.....9....7...4.....1. +6..1..7......7..8...9..8..37..4..5...3.....2...4..5..81..2..4.........5...6..9... +3..5..1...7.....8...8..4..66..4..9...3..6..5...7..1..48..1..3......2..6...2..9... +.1.4.9.........32....7.........56..48...3..........7.95.....8..2......6....1..... +12.4.........8...37....6...........8.....2.45..59...6..1...4.9..7....8..96..1..2. +......94.....9...53....5.7..8.4..1..463...........7.8.8..7.....7......28.5.26.... +.......4...2..4..1.7..5..9...3..7....4..6....6..1..8...2....1..85.9...6.....8...3 +....3..7..2....8...1.......3...6.2..2..1..5..7.4......6......4....2.5......8..... +53..2.9...24.3..5...9..........1.827...7.........981.............64....91.2.5.43. +1.83..........72..3..6.....642.5...........61.7........5....4.....8...3.......... +.8..4....3......1........2...5...4.69..1..8..2...........3.9....6....5.....2..... +1.27...8.....56...3............9.7.4...2.....8...........1...3..9....5...6....4.. +2..4......7....6.....5....1...8...2583..9.................6.37.4.1............9.. +3.2....4..7..85.............9..6.8.....2...3....1..........95.72..4.....1........ +5.4....6.....21...3........27..9...8...5...3...........1....7.96..4...........2.. +....28.7.4.1...........5......1..4.6.2..7..........3..6..34.....8.2...5.......... +....8.4..4.......7..69....3.......7...7..62.5.9...2..1..3....5..8..14......32..1. +1..........6.....3.8....4.1...6785.4...9........5..76...2......59.814......3..9.. +.2..5...9..618..73............5....8...938....1.....6...1....2......4....973..4.. +5.2....4.....81...6............3.7..4..5............9..97...3.....2...6..1....8.. +24.7.........5...6..........59...3.....1.8....6.......3.....27.8.....14.....9.... +5...7........4.6....3....9.2.....7.1...9.8......3.....42....5.....1...8..6....... +5.....3.....1...2....4.6.........5.8.6.2...........9...1.....499...3....8...9.... +...1..5..4......6..........6.2.4....5.....8.37.........315..........7.2..5.8..... +.2.41.....8...7.3..........7.5....6....18....3...........6..4.16....5.........2.. +....7.94..7..9...53....5.7..874..1..463.8.........7.8.8..7.....7......28.5.268... +.4.5.....8...9..3..76.2.....146..........9..7.....36....1..4.5..6......3..71..2.. +..14.7...5.....6.....8.....4.86...7.....2.3...........63..5...........41.2....... +2.4...8.....3.4.............3.5.7...6.....4.2...1......1.....5.8...9........6..7. +.2.4....94..189.......62.5......63..3..94.5.......8.1..........8.2..4...964.....7 +6..9..4...4..5..3...8..6..55..7..3...7..2......2..5..41..6.........3..8...6..2..1 +4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4...... +....5...11......7..6.....8......4.....9.1.3.....596.2..8..62..7..7......3.5.7.2.. +9..7..3...5..2..1...7.....93..6..7...6..5..3...2..8..61..4..6...4..6..8...6..3..4 +.2.8....51.....93............84..2......1........6....61.....7....2.5..43........ +.2..5.6.8...7..4.....1.3....6..4....3......5........1.7..3.5.........2......8.... +..3......4.6.......8.231..62...9..715.......8....4...3....7...........9...1.68..7 +2...9...8...4..7..6.........4.3.7..........92...1.....5...6..........31....5.8... +...3..16..27.........9......48.2.......6..9......8........74.2.3.....5..1........ +..1...5.....2.9......8..3..53..4...........726.........2.1...894...3............. +..1...8.....6.4.......3.......72....4......5..9.....3.6.....2.1...8..7..31....... +.......2...43...965....21..1.....7........432....5..1..6.........597...8.9.68.... +..345..8..........7.9.31...24...7.....8..9..5....1..2..7....29.8..........2...6.4 +...3..18..26.........9......47.2.......5..9......7........64.2.3.....5..1........ +52...6.........7.13...........4..8..6......5...........418.........3..2...87..... +2..5..6...9.....3...1..9..44..9..7...5..4..8...7..1..6...1..3...2..7..9...8..3..5 +6.....8.3.4.7.................5.4.7.3..2.....1.6.......2.....5.....8.6......1.... +9.4..5...25.6..1..31......8.7...9...4..26......147....7.......2...3..8.6.4.....9. +..9..63...72..........17..51..3..9..........86.84....1.6.........1.2....9.3...45. +..24............98..8..16...5....826....4.7..7..8...5...167....3...92.....9...... +...85......5..6.1..6.....5..3......147.9............26.9.6.38...8...1.9...3...... +......6894......7.....26...2...6.34...49..8.2...........87.5....6.83......7...... +.5.3.7.4.1.........3.......5.8.3.61....8..5.9.6..1........4...6...6927....2...9.. +.2......6....41.....78....1......7....37.....6..412....1..74..5..8.5..7......39.. +.175..6.....4....3.6....5..8.4....2.....51...3............6.7..2..8.............. +2.6.9...........847.........8.4......15...........76..9.....2.....3....5...1...3. +.2.4...5......6..44.....91.8.........3.7.4...27.8.3.....95.1.2.5....2.3........8. +.2......5...7...4....3.9....8..62...3..4...7..............5.8.64.1............2.. +1..4.8....5....7...........4.63.........7.59........2....8....16.......3.9..2.... +.2.4....94..18........62.5......63..3..94.5.......8.1..........8.2..4...964.....7 +.2....4..7.......6..8..7.....3..89.........42.541.........6....5....93.....34..81 +..8.9.1...6.5...2......6....3.1.7.5.........9..4...3...5....2...7...3.8.2..7....4 +..6..3....97..513..2..91.8........7.6.........41.....9...5..8.....3.47....4.16..5 +1.....9...64..1.7..7..4.......3.....3.89..5....7....2.....6.7.9.....4.1....129.3. +...9...612.7...3.....8....54.9....5.....5...9.7.6..........314.3.......6.1...2... +...3..18..26.........5......47.2.......1..3......7........64.2.9.....5..1........ +.....7.95.....1...86..2.....2..73..85......6...3..49..3.5...41724................ +.237....68...6.59.9.....7......4.97.3.7.96..2.........5..47.........2....8....... +24....6......9...8......7..419...5.....3.2......4.....3......4.6..7............1. +....62.9...9.....52.....4.1...8..1.6.9.5...7.1...3......76....83...2.6...1...3... +38.6.......9.......2..3.51......5....3..1..6....4......17.5..8.......9.......7.32 +49..6..53.......7.1.6............2..9....4.1...7..5.8..3.7.8....1..9........2.4.. +.2.4....94..18.......36..5......63..3..94.5.......8.1........4.8.2......96......7 +...5...........5.697.....2...48.2...25.1...3..8..3.........4.7..13.5..9..2...31.. +6....38......2...7..95.....96.18....7.........8.....49.........3.2.95...1..6..2.5 +.6.........3..15...9.8...716....2.3....75...4....9...82.1.....6..6.......8.5...4. +...2.5......1..7...3......45.17............83....6....1.....25.....4.8...6....... +12.....8.4.......2..9..35.....9.8....7..2..5...1..42......65..7.......61..83..... +.26..4.......3..8........7..97...5.....28.......1..........56.93.....4..1........ +...9...86......5......5...1.2......47.85...9.4..36....2....3....4.69..7.67..1.4.. +..3.4..1....192......3....7..4.6....5...34..2.8.9.........7...67......58.29....3. +1...5.......2.1.....3.7..6.........962......7.8593.....3..6..8..7....1..5....4.7. +.6.5.1.9.1...9..539....7....4.8...7.......5.8.817.5.3.....5.2............76..8... +2.....9.....3...7....7....69.5.2...........481............1.5...46.......3.8..... +...2.13...7.....9.5........4...9..8.8.....1.....3..2..9...7..5...16.............. +.47.2....8....1....3....9.2.....5...6..81..5.....4.....7....3.4...9...1.4..27.8.. +....8...7.......5.9..6.1...2.6..4.9.......1.....9..3.64.8.5....6...7.8....23..... +2.....9.....4...7....7....69.5.2...........831............1.5...36.......4.8..... +.27..46..8...9.....5......154.........1.....82...3.91....6...7......2.....28.7.46 +....5......913.8..1.6.2.....9......673.9........4....14.............36........352 +.52..68.......7.2.......6....48..9..2..41......1.....8..61..38.....9...63..6..1.9 +1..53..7..48.........2.........681..3.......2.....1....1....4..5..7...........6.. +.98.1....2......6.............3.2.5..84.........6.........4.8.93..5...........1.. +.3....2......8........1......7....8..2.5........2..3..1.8....749.....5.......56.. +.........9......84.623...5....6...453...1...6...9...7....1.....4.5..2....3.8....9 +6.2.5.........4.3..........43...8....1....2........7..5..27...........81...6..... +...8....9.873...4.6..7.......85..97...........43..75.......3....3...145.4....2..1 +.26.39......6....19.....7.......4..9.5....2....85.....3..2..9..4....762.........4 +1....67.........32..921......4..58..87......69...6...........5..4.138.........4.3 +.5..9......12.......6..73.8....2...4...98...53.....91.7.....6...4.8..1..1.8.7.... +1.....6........4.8...3.....9..7...2.....8.5..........7.68.4.....45..9..........3. +....1.78.5....9..........4..2..........6....3.74.8.........3..2.8..4..1.6..5..... +....2.7.84.1............9..3..4.1.5..7....2.....6.....6..5......8..9...........3. +........7.2..854..8.5..92.....1.867.....4....7.....3.8..9.1...23......1..6..5.... +......7.1..4....3..85....6.6..9.1....1...7..5....3.2...2...8.5.8.1.29....6..7.... +....5..6..752.....9.........1...85...3...7..8.....9.433.....6......1.8.478...5... +1.......3.6.3..7...7...5..121.7...9...7........8.1..2....8.64....9.2..6....4..... +.2.3.......6..8.9.83.5........2...8.7.9..5........6..4.......1...1...4.22..7..8.9 +.2..........6....3.74.8.........3..2.8..4..1.6..5.........1.78.5....9..........4. +...36....85.......9.4..8........68.........17..9..45...1.5...6.4....9..2.....3... +....75....1..2.....4...3...5.....3.2...8...1.......6.....1..48.2........7........ +.....3.879....5...6......4..827.......9....643...9.8......7..1....5......43..1..6 +5.1.94....2.6.59................2.17.3..4..6.......5.........2.....3.8...6.8.93.. +4......6....94..5.1.9.6...............6..2..5.5231...8.7...8..1......7....8.5.3.6 +2....1.9..1..3.7..9..8...2.......85..6.4.........7...3.2.3...6....5.....1.9...2.5 +.1.6...8.3.2..........49.....83...........4.7......5..46..........2...9.7...5.... +8.2.....4.9......7..5..139..8..17......5.2..1.....8.36..71.....4...7....32...5... +..79..1....36..7..45........2...3.8....8......4..51.3.....9...25.4....1..8...4..6 +....7.8..8.5....97...5......1..9.....2...5.3.7....3...2...6.9.......4..246...87.. +.1.....8....75.....39...6........8.6.9..2.4..6.1....9.......2....2149.381...3.... +.6.....7.2...5....7..12.9....2.....8.18...3...7..9..5.4..9.2......6..1......85..3 +.4.3..9...7...5.2.8........6.....352..5....9...4...6.....9....8....42.7..9.16.... +........92...6.5...5.72..4.87....6.11.....9.8.......5..4..........612.....3.5.... +2..7........68..57.8...3.6.....1....3..4.....6.2....9........7...9...8.5.4.53...6 +........5.7..8..3.9...25........9..669..7..5.....1.8.7.8.2.7....61.3....3.....4.. +..9.....76..2..8.....3.59.....61..2..64......9..........1.4..8....5..4..8.3.....6 +1..4..........2.3..7..8....5..7....86....5..9.4...6.......3.......9..1.5.97..8.63 +.2....16.....7.5......1.......6.8.4.3..2.....1.5......5.....7.3...4......8....... +34.6.......7.......2..8.57......5....7..1..2....4......36.2..1.......9.......7.82 +1.3..7.89.....93.7.8............34....4..5......6..2.834.....6.....9....8..7....4 +1...6......5..31...6...8..97.85...4....4..25......2.......9.3.2..1........3...714 +6.2.5.........3.4..........43...8....1....2........7..5..27...........81...6..... +......5...4.......5...37..2.8...64....9..2..8.6...17....36..8.9..78........1..... +.29.46...1..9..3.44..7.....5.......2..8.2..7..4........3..7...17....49....683.... +...3...1..14.27........5..6653...1.747.......1......8......49.58......2....5..... +....14....3....2...7..........9...3.6.1.............8.2.....1.4....5.6.....7.8... +1......4..47....3.....5.7...8..7..9......3..6..1..95.4..82....9.........2..517... +.923.........8.1...........1.7.4...........658.........6.5.2...4.....7.....9..... +8..........5.6....37.2...1.6..9..5...5.....36...1..4........7......27.8..2438.... +72........6......8..89...4.....8...63....7.2..9..3.4......4...727...3.......5.91. +2..6.34..17.....9............64....59...................53..6...8..7..1.....1.... +.524.........7.1..............8.2...3.....6...9.5.....1.6.3...........897........ +.2.......3.5.62..9.68...3...5..........64.8.2..47..9....3.....1.....6...17.43.... +.6..31...5....2..9...4......2...9..1...6..8..7.5.......3..6.4.....3.....2.1.....5 +..7..8.....6.2.3...3......9.1..5..6.....1.....7.9....2........4.83..4...26....51. +......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3. +8......21..1..95.6.....53...7.4...6.....7...9..2.9....3.86.....1.5.............42 +.2...48...54...6..9.......1...5.7.68....3.1...8...2...3.........7.9.8.52....7.... +.2.....74.....6.8...6.4.9..3..........98.....5...19....1.5...32.4.2........4....1 +...658.....4......12............96.7...3..5....2.8...3..19..8..3.6.....4....473.. +....15.6..3...4.7....8...593...7...6......21.4....1...92..........59.7..8.5..3... +.7.6..3..8.......6..2....9...5.67.....8..1..946..8......9.4..........2.7...2.8..3 +8.7..6..2....5....4.....9......2..67..1..4.....63.9..47...6..8...9..2....3....5.. +7.8.....4.65.7.....4..5..9.....1..52...8....3..6..49......85.2..7.3....5...2..1.. +.29...6....87...2........5...59..3...4..12..6...4.7.....6..9..1.....84...5.....6. +..2..583.7...2...........4...12.7.........9...8.1.34...9.8.....6...5...8.3.7....6 +63.8...1..97.............3....1....61...5.....4...6...5..2....1..4...9.73....748. +3...6.1..65.29............881..7.5....43..9....5..2..7...43..9......7..64.....8.. +1..........4..1.9..6..8......3.7.8.......51..7..1..........3..8..5..497.4.685..1. +..8..5...3...9......73......216...8......2..35....94.2......6....57..81.4.......7 +..2..9..7..6....3.5.......9..9.126...8..4..9.2.......1.....39.5...785.....3....4. +...5.36.2.3.69..5..5.7.....7.9..2....1....9...8.9..........63....1....675..37..1. +1...7........49...9.6.8..3.....9...7.2....5....846.12..618.....3.......8..5.....2 +1..6......7.3.9.4...4.7...2.1.7....9.37...2..5.......6.....59....2.3....78..1.... +1......8..7...8.3.....5147.5.......6.4...7...21...4.......9......6.2.....8....7.. +9..4.1....3........6.3..7.12.......6.....71....8...45...9..48.....9...123..2..6.. +9..3....41...2...5..2...3.12..6.9..36.9.5.............48.9...7........4....81.... +15..7..........94..8............8.256..2.....3...........9..3....46......7......1 +1....759..7..9......64........8.....63.....4..9.54.3.232......4...9...8....1.4..7 +..6.3.521...7.....9.3....4...7...6.......4....1.5.27........973.....5.....8.1.... +...75........48.2.3.......6..6........1....74.5.4.21...7....3..9....5.18.2.9..... +...4...26.7...9...28.5.....8..3....79......8.....9..64...7..1....6.....2..3.15... +....8.61..53............5.2...4......2..58..74..7.9.5.964..........2.86.3.....7.. +.4.2....8..1.5..4.2..8..7....8....97...6..3...957............6.7.4.1...2.....31.. +..4..8......7..624...........2...9..6.3....5.7...24....5.3...7..9......8....1.2.5 +..3..2.6.9..3.1...1.2.5..8.........2..5..3.98...4..1....7.....5.2..6....4.19..... +.691.7..8..2.....6.8....1.....7...32.9...8.....63..8...7...4.......5..4.5....6... +.46....9...3..4.......6......7.3..4....8...1.5..1..2....1.7.8.9..5.1...7.3.9..... +...58.6......2....974.......16...8...4.9...7.5............6...8.5.4...1...2.9.4.. +.8.65...2.9...7....1.3....87......43...4..5..5....1.....2.9.3..9......2...42...6. +.3.......8.5...4.19....1.6....93..1......6.2..4.....76....1...27..8.......135.... +...8.......43..8..7.....19..5.....7.3..24......8..9..3.29...........1.2...1..24.9 +.....6.5.3..1..86....9..4.34....81...7.3....4.9...1.....5..3...9.8.6..........2.5 +15...3......4.9.3.3...8.....3.1..7.65.4..8.......7............24......516....2.9. +.5..9....1.....6.....3.8.....8.4...9514.......3....2..........4.8...6..77..15..6. +....98.........71.2.1...4.......6...8..47....6.7..1..31..5......3....86...4...19. +....4.87..61..................6...312...7....4.........5.1.8...7.....2.....3..... +...3............81.52..9..67.9...1.....4...5....8.6.2.2.8.5..9..952.4.....1...... +1.46.5.8.8.62..................6.7..6....7.1...91..2...5.4....3...91...4..8.....2 +..23.....85..4....97.5.1...4.....89..2.91...3.....4..6......5.9.....5.1...7.3...8 +....147..6.9......3...........6.1.3..5.8............1....93.8...4......2......5.. +......8.17..2........5.6......7...5..1....3...8.......5......2..3..8....6...4.... +......8.16..2........7.5......6...2..1....3...8.......2......7..4..8....5...3.... +..13......6.....2.34..9....4..9..7....946...52..1........6.3..1.....8.5.6.8...4.. +1.....7.2.3.1......9..........39..6.5.8...2...........2....85.....6...9.....4.... +..61......7...356..9...2....8..7..2........5....5.43..4.....79.1....84.......5..6 +..3...78...6..9...7.......5..7.....3....2..9......421.5..3....68....2...9..5..... +...5.1....9....8...6.......4.1..........7..9........3.8.....1.5...2..4.....36.... +.2.........678.1.......2.....1..5..4....4....9..32.6.5........8..7....3..6.2.3957 +..26..8......1.4...5.....3......5.....1...7.4.6..382....7..6.............982..56. +......8.16..2........7.5......6...2..1....3...8.......2......7..3..8....5...4.... +.7.81....9...7....3..4....8..5...43..6...12.....9....7......6.2.8....75....524... +9.24.15....4..8......2................53.71..3...1.46...7.....4.89...32.5..6..... +1.........2......59.68.........3....6.....4.9.7..296.......59.....1..7.683......4 +........3.9.6.4.......1.28.....6...173....6...5.4...7...5...7..3.91.....6..97...2 +8..4.6....3.2..4...4..5..78.69..5.2.3...........6...1752.....3......36.......2..9 +3.6.7...........518.........1.4.5...7.....6.....2......2.....4.....8.3.....5..... +.5.....47....8..2..2......64..35..7..65.9.4...38.........51.6.......9..4..9.7...2 +..47.....3...45.....7...6..9..236.7..5.....8...2..........18.27...9..1..7.......3 +..35...8.8....1.7.......3.12...1.....7..249..6....7...3.2..6..845...8.........46. +2.5..6....9..7.8....6.5.2....2......98....57....1....4.2.6.8...5...4.7........13. +....6.4..5.83......9.........1....873...5.2.......3.4..6.5........84..7.2....6..9 +....1..594...5.8...5.7........59..8..1.2.3....34........38...........6.7.729...1. +...4..5...7...3.....421..6..3..8.1..8..7....695.3........9....1.6.....2...3..6.84 +6...4...3.1.....7...5...8.....5.2...3...9...2...1.......8...9...7.....5.2...3...4 +5....3......5..29.....7..8...57......46..132...93..8....7...9....4..9..6....6..4. +.5....46.....9......3.8.2......51.....134.58..46.........1.....4.....8.97......1. +.4............12....568..7..57.....3.985..........9.65....42.8.......7....3....5. +...37..5..27.........8..39.....8.......2...4.893......64..31..8.5............65.. +.5..4.8...6.5....9...............1.....23...7..1..7..2..4.9.....376....5..87...2. +..5..8..18......9.......78....4.....64....9......53..2.6.........138..5....9.714. +....854.6...2.....9....32....1.7...46....9.5..93..4..8..5.......6..1...747.6..... +.397.4....71............6.....3.5.4.8...2.........69...165...9.....4..6.2....8... +..........72.6.1....51...82.8...13..4.........37.9..1.....238..5.4..9.........79. +1...7...8.4.6.......5..1.......3.95...9.....2.....4.1...1.9.6...52...8..7..2.3... +..57..9.....8....4.1.....2.4....2....6318.........376.95.........854.3.......6.8. +....74....6.29......73....12.....7....8.2..4559....8...3....98.....83......1...2. +82......7..4.1.........9.5.......39..1.45....2.8.3.....7...4..6.....5...1..2..84. +.6..2...3...........3...4.5...8..7.2.1...4...8..23..9..4...3.5.78......19...8.2.. +........1...2.9.8..4.....7.8...9..3.9...3..642..1.6......68.5..1....53...75...... +2..7.34..18.....6............34....56...................53..7...9..8..1.....1.... +15.3......7..4.2....4.72.....8.........9..1.8.1..8.79......38...........6....7423 +.9.....2.8..2..........1..8..9..45...5.6..7..6.4.8...........5.2.68....3...14...9 +.2.456..9..6......7.9..........1..9....938..2..5..2..4.6.2..8..5.2....3..1....... +..2......1..49.2.5.4.....9..8...14....63....75......6.6...8.3.....7..1.63......5. +7..3.6..4..54....7.4.....3.6....5....14....25......1......8.9..8792..5.......3... +58........4...52.8...9.2...9....3.65...49..2.....5...1...36.....54...7..6........ +.2.....9..65.2..4.9.4.....5..6.5.......17....54.....18.....2....59...8..7...4...3 +...4.7..6....2.34..5.3...8...5....7..2...8...7.....1....4.5.2.....9....3.61.....7 +.....89..8.65....2...32....21.83...7.3..1......5........1.7..2....6....957......4 +......1........2.568...4.....3..........73...9..6....1..29..5..1...8.7..49.1....6 +249.6...3.3....2..8.......5.....6......2......1..4.82..9.5..7....4.....1.7...3... +..123.........7..16.8..49.....32...4.....5.9.8...7...6..45.....91....6....3....2. +8..6....29.........71...8..765......2....5.7.....6..9..13.4.........64.7....8.... +.3..8....8.75...62......4..2...6..945...1...7..92......2.6..7.......3...6.8.5.1.. +..3.1..7.....4.1.8.....2.6....8....1.753..6......7.9..8.....4...4.18....6.7...... +....6..8...3...2.69....5.....2518.9..14......7....3...2........4...9.1....9..7..5 +9....8......9.6..22....3..8......78.5..8.7..6..76..1..6....9..5.1....4...4....... +.32.....58..3.....9.428...1...4...39...6...5.....1.....2...67.8.....4....95....6. +..6..43.....3...7..1...7.4....9....536....1.8.2........9..86.....7......5....12.. +.....9.8.8..3....2.675.......27..6...3..45.1.....1...5..1....9...5..71..49....... +1..6.......7.9.4.19.3.5..7...15....93.4.....2.....7.3...5....2...........2.94..13 +..2...9..9....6..18..4...3.....6..5..3...82........3.7.2.9..1..6.1.5.....7...4... +.........6.1...9..3..24..1..5...6.....978...........87...5.7...2....43.......36.4 +2.4..9.........8.16..7........2...6..8..3.....5.....9....4..7..3.....5..1........ +.2..6.4.......5.8....7...3.3.8..9.........2.157........4..1.......2.....8........ +..4...23.....72.........1.62.3......1...86..3...7.4.......6..9..9........6..485.. +...5....6..4.6...7.7.4..2..12..8....5...9163......3..2..29....5.......6.8....6... +.........4...19........5..1.3.8.....1.5....7...8..62........32..83..2.9...67...4. +1....786...7..8.1.8..2....9........24...1......9..5...6.8..........5.9.......93.4 +.8.7..2..19.........5.98..1.4.9..7.2..7..2.4.6.........1.8..5..3...5...6.....93.. +.6.....7.9...8...6..3..91....8.95....2.4...6....21.8....5...6..3...4...8.4.....2. +.3........7.6.19...4.....2.2..8.6.....9...1......5....4....2..5.....8.1.....1.734 +2....48...95..7.3....6.........1.....43.....5.6.3.84..47.2..5....6.......2....69. +.8..3..6.9..6..4.7....9...5..5.......627.......9....84.2.....7.4...7...3...2.1... +694....1..2..7.9........25.......48.4.1...52......8..39..4......6.7.5.....3.8.... +1..7..6...4..9......74.32.....1.............7..5.3841.2.3.4..5.......3.6.79...... +......4.18..2........6.7......8...6..4....3...1.......6......2..5..1....7...3.... +7956..8.......1....2.3.....2......5.......7.8.46.7.9.....1.......2..5..9854.2.... +5..2....9......12.1..8.47...6.5.7..1.5......8....8.2...2.34....6.1.......7..18... +2.3.....75...7..987..4....5.....67......9.1....73.8..91.6....2.3...5.....5...2... +....6...9.7.2..........1.8..1..2....4....7.5272..59....41......5..9...2669.7....5 +....2.4.3...1..7...85......3..7........4...1..6...........18...4.3.........6...5. +4...1..8...9.2..5.1.......2.3.9.8..7..5.........63......6...4....1.832..7....6..8 +1...8.5...5....24.....24.31..583....6......7....16...23..45.6....8.1.....9....... +.5.3.2........16..8..5...7..7....3.....95...2...2.37.9.4...58..6..82....78....... +.....3...8..2..67..6.......51...2.4.......3..47.5....87..4..8....2.1....6..3..4.5 +.......71.2.8........4.3...7...6..5....2..3..9........6...7.....8....4......5.... +3...9...8.2.6..5.9.......4.8.423........5...7.....12...8614...........7.15...9.6. +.6.3.......15.....7....1.38..7....9.52..8.1.........56.7..4.6..4..6....5.5..9..2. +..3....9.8...21..6.5........6.25.74.....73..55.....2..2....7.8....49.1...9.5..... +..2....7...4.5.....7...8..1..6..5.8..4.1..2..5.9............9.7.3..278.....54.... +....7...56.1.92....95.6....8.......3.6........3..4.7...5.....1...2.5..78...4.95.. +......8.17..2........5.6......7...5..1....3...8.......5......2..4..8....6...3.... +1......9..65..9......63...22.6.47..5..835..2.........4..9...1.8...9.....75....4.. +6.73.........8..2..2...79....1...4..4..........3.5...8.7.1..38..4.63...7...8...9. +5....3.8....4.1...4....7..3....8..64.9....23.2...7....95.......3...4.7....7..8..9 +...213....6...9.8........9.5.84......7...8...3.......5..1...7..2...6...1..5.3.46. +....4...1.54.....9...2.1..8....9...3.2.3......8345..........6.5.6..8..9.7..6...8. +9..51...35..3..2....8..2...7.....5.8.96...31....8...9.3.5.7.6.....1.........3..85 +.5.4.....8.3..1...9..5...8...6.3..9..9.65.........2..3.2.1..7..6.....8.5....4.2.. +.3.4...81....9...5.6...84..5......1...19...4..8.1.7.53..8.3.......6..7.......4.9. +..3...6.......1.9..7..5.....18....5..2..7.1..6.9....72...83..1....2.....4...673.. +..3....1.....94...9...2.......7..6.345..............8..96...2.....5..4.....1..... +...4783....7..329..3....4..2...5......68......14....2..93......7...2..1....6....5 +9.16....2.8.....4...3.2.5........2....51....6...87...........3..189.......6..59.. +1...9..3...2..3.5.7..8.......3..7...9.........48...6...2..........14..79...7.68.. +.9...1....8..4.....1.8.2..7..3..7.6.........4...46.71...51...32.....5..62.....9.. +.6.........428........31..5....4.39...79....2..8..27....6.2..1948.......7..5..... +.4...9..3...5.79...68.....77...3.....85.2.......9...1..54..3....194....8......3.. +.139.....7...........24..1......1...48..6.........39.6.........2.5...74..6...489. +..2...4..9.563.8..........6....6........73.9...8.15...7.63...5.8......4.3...2.1.. +...7.....12.....84....8.....945.....3....2..67....45........16.6.1..73.5....5...7 +....8...2.7..2....13.5..7.....1.25...4..6....5..9....3......3.9.19.5.4....7....6. +....6...79.3....2....7....1.465......8...2...5.9..4....9...3.4...8....12..5.7.3.. +8....4..197.....8...438.9......4...26..2..8.3..5...6...............1..59..189..3. +.3...8.1.42..........9.......9..26.....75.........1.38174...2..2......85.....6..4 +.23.....7.6...1....7.6...5...63.4.....8...5......9.2..3....5..9..94...3........4. +.2.7...9...4....71..5.6......9.73...3...56.1........4..1......86....8....9...2..4 +.1....63......3...8....5..9...4.7.6..4.....1.....9...51...28...7.8.5...2..21..... +..8.....3.....58..5....2..46.......99...781...1.....3.175.........3.........9.67. +..6......35..1.....17..3.5......9.4..2.35...8...2..6.........9.672.....3.8....7.6 +...487....5.9.......7...2..2.1..4.9...4...6.3.9...5...3......2...87...3..6..1.8.. +......8....9..852.4...9.......5...6.1..6.4..37....2....63.............39......1.7 +2..1....54....9.8......276.....3....51....4.9..6....7...1..5.......7.8..728...... +1...6..8..73.....6.6.35.......8.57........1...9..36...4...8.....39..7..2..5...9.. +.63...9....78.6...84...1........52.35..1.........74.9..2....8.6.8..5..3.4.......2 +896.4..........7.2...9...6..18..9.246..3...8..3..1.......4..2..3....6.....5.3..1. +6......1...7.......5..83.2.....4.3....28.6....7.....5......91....8235.......1...4 +......3..6....1.....8.9....4........15.67..4...72...5..4.7...25....6.7...8...5.9. +4....52.....3.....2.......85.8..1..4...7..6...4.....811....9....5..2..1...31...57 +4.....7....68....59..72..1....26.....4.5....9.....1....85.....3.....3...7.....8.4 +2..3....53...591......7.4..5....8...7.1.4.......2...6.......2.4..5.....889...3... +.3.8....5.....6.49..5.2...79..........4.5....6..1...32..2..7...........3.....41.8 +.1...7......85.9....3.....4.2..4....75.96....1....8.2...62............3......57.8 +..2...68...3....2..74.82......4.5.9.3.......5..8.7.1.3............897..2....245.. +8...1.7......4....4.1..95....2...8.....9.......3.86.9...8.........6.3....7.5...36 +8........95..6......6.894..3.8....1.5.4...8.7.1....6....527.3......3..72........4 +5...6..........583..3.9.4..6...4.1.....71.....7...2....5...1.2.4..5..6...89...... +.23..6.....67....3.8....5......9.......8...4....2379.55..9..3.2.....2..8.7.....5. +.......8..72.....1..6.9...7.4..15.3.5........6..27.........81...98...37......3..5 +42..5.73.........48.7...2......6.......941..5.5....1......8..2..1...3..9.75....1. +..1245.6..2.......9..7.....4.7.6..1....5...........9.3...1.6..2.......74.58...... +......5.88..651...9..2..3...4.7..9.5....2....5.1.4.....1.56.8.9..3............61. +........5.4.....7..761.43...85.6...........51.6.2.......2.....9...4.8.....37...1. +.1..5.3.......4.7....6...2.2.7..9.........8.146........3..8.......1.....7........ +..7....35.1..4..8....3..1..5....18..........2.26...4......8....8..765.9..7.139... +6..3.2....5.....1..........7.26............543.........8.15........4.2........7.. +1..3.6.8...6..7...9.5.4....2.3...6..5...2.8...4..........4....9...7.....6...1.2.5 +.2....5938..5..46.94..6...8..2.3.....6..8.73.7..2.........4.38..7....6..........5 +.....837..7.....2.461..........9..8..1...6..3635.8.........3..7...6....9.2..49... +8...2.1.......9....651...2....5....3.3..61....874......1.24.3......169.4......7.. +1.25...78.4...........81...........9.8.....136....7...2..6.89.54...2......9.13.2. +..76.23..9..........6793....6.3..5..7.5...8....4....2.....167..........4..35.72.. +..4.8...6.......5..5.6.184..1......8..9..4.3.7...3.......24.98..6..5.......1..6.. +..24...5.8.....2...6......1.1..47.....6.5..2.......7.6....9......81.5.3.79..3.... +....81.7..3........2...9..1...4.5..........27..8.9.5....1.5.6....9.....446......3 +....7.9.6..5..9.4.9....8.7...9...6..87.....2...6..3......1........4.6..25.3.....8 +8...3............6....6258.5..9...1.....7.9.3..9...26.4....9.5..9....8..2.58..... +1.25..6..8.5....2.......5...1.....68..817..4..4.6.3......43..7......94...9..1.... +..54......4..8.7.......3..5..3.26.891...........3.7.2...9.4...3.......6..2.5....8 +..43.7.........3......8.2.141.6...9...9.3.5..7....9..8.6.....5.3.....1..5...1...7 +..3....9..5.2.......7361.........71.4....8.......3...6...5..83..85.2....2...14... +..3....8.9.....62....9......8.4....6.4...37..2.5..7.....8.3.........5.1...7..84.. +..1.2......3....4....71....2....6..8.4....3...8.2......3..6....1..8.4.7.8...7.5.9 +....7...6...3..97...5.62.8.3.....7.1..45.........9..........8..7..8...23..9.4.... +......8...2.7.9...9....871.8.4........6.4...55....3.6..7.5.....3....6..2...9....1 +......25..7....8...28....19..12.89..3...9........1...6.624.7......32.7.......1... +85.....4..6.5....3.2...87...1....4.........697...9.5..3..9.....4...17.2....2..3.. +1.....3.8.7.4..............2.3.1...........958.........5.6...7.....8.2...4....... +.6..8....1..9..2..85...1.....91.26.3.7.564..........7....4..........67.5....198.. +.5......37..6...2....1.3.....4.8..1.1.7..2.6.9.........8......9......45.6...9.... +85..3.....3....5.61..2.......91....85..7.8.......4....6..9.7.5..2.....1.....2..7. +4.2....5..8.7..1..1..9......9.6....8..5..12......2..7...9.3..4..3......6......8.. +4...5...9..3...8.....1.9........4.......8.29.15.6...7.8.6....4.5.......3..9.4.78. +2....35....7....4..6.97...........62...42....9.58.....3.9...2.......18..4..7..... +.5......88..2.3.....3..1.4.23...4.5.6.....7....8.9......654..8...7...1.........3. +.....69...2....6.....75..24..4.....9..2..3.6.59........8....7...5..1...81...39... +3..7...2...8.4...9....5...1..24.....5...3.4.......8.3...46.....9..2.3....1.....67 +.6....83..25....1.....16.....7.6....8..4.57.3.......8...2...19..5...8...4....7... +.56.....7...1...92.....7....6..2...1.7...98..3......4.13..........68.5.4......9.. +......4.17..2........8.6......7...2..4....3...1.......2......8..5..1....6...3.... +57..8.4.........3...32..1.5...4.8........5...2.7.....19.1.....4.8.7.9.....2....6. +.6..51....2...8..1...7..28......61.27.9..........4..3..8.....2.4.5...7.....3..6.. +.32..........2..4.9.7..5....1...3.98...2..6...68.1..2....53....57......3....471.. +...6.28.........7.14...56...6.2.......7..3.5.....1.29...3..7.......4..359........ +...3...86..76..1......5...4..6.1....5.4...3.2.9..4.7..2...3.6....1..54..43...9... +....2.68.9..3.........6.3...9...6..7.1....8..3.6..8.2......5...4.2.1.9...8.9...7. +.....1..8..4..765..5..6.39..13...7...7..3..8.46.7.5........6..9..29........85.... +3.2.8...6....9.7.8.5.....2.78..4...9..4.......1...2...1...7.....4...637.5..4...9. +13....6.9...2..54........7..........62..8.7.47.9.461....1.3........15...49.8..... +1..64.....7.......9.4.....1....6..79.3..7...4.4..583...5....9...........4.629.5.. +1.....9....27........34.62..1..5.7....7.....359..7...8..1....6..4.......7..6.93.. +1.........4..59....653....42..6...47.7..4...6.....58.......3.2...2..7..5......61. +.64.2.........1...8..7...3....13..7...8..2..17....59.....4..5..3.......91...6..8. +..8.5..6....7...83.5............524.8..6....9.7.....1...23.....6...9..7...4..2... +.......76.....51..2..3..9..93..2...1.2....36.7.4...........1..8.5.96.....9..4.7.. +9...182....5.......7.93.....29..3.8.3..6..7.27............9....1.73.2..6..8..6..5 +17...4...3..6..7..25.....4......58........4......3..155..7...2.....96...7..3.1.9. +1.64....5....2.......3.6.8....2..8...8.9.713...4.....9......59.47.......5..1.3..2 +.4.7.1....1.....9...9.8.7..5.6....3.1....6.......2.8....8.....4.5..3..2..7.5....9 +....2....9.6.7...88......4.....6.4..6.......9.124...3....3...8...8....71....589.. +......1.2...4.....8...3..5.3...895.........7..1.6.......3..8.....25.6..9.6.97...4 +.5.9.86....4......3...6.8........7....683.......7.2..12..5..9...9...1..6.......35 +.1.....9......4....6..8.2....3.92.5..2.8...1.6...1.4........5......39...2...5186. +..6...5....3..4...2...7.4....93...7....46....8....23...7..9..2....5...161........ +..4.8.1......13....5.4....6.......2.....67..3.98....452......7....3....28.6...4.. +..5........3.8...4...1..7.56.1......8..4.3..2....1..4.5.8....9..1.7..6......29... +..34........7...3.7....15.62....8..7...94..........9..6.....25.8.53.2..1...6....8 +7..3...6.....1...4.3...52......649..9.61........8.......3.2..5.2......1..154...7. +1..46..7...7.....496.....8...6..38...4..1........2..3.........2.8.9..5..7.5..2... +..6..1...8.3..91...9......7...2...5....1..4...3..468.12.9..4.3....5.....65..3..1. +..5.32.....9..1..34........6.....2..2....5.84..7...1..8...6...7.1...85.2.......4. +4..8.......724.....2.....7.25...96...3.75.....4......9.....5........159....9..26. +.8...4.5....7..3............1..85...6.....2......4....3.26............417........ +.7.....358..9..4..3...2........1.5.....4.8....6..3..8...........5....1739...42... +.2.4.5........9.......3.28....3....93..8.47..4....652...1......64.9....27.....3.. +..4..23..85..6.4..........1.8.7...6......5.8...3.4.......5...7...1..62...9..2.... +...1..5......26...4.3..........7.643.65.............8.3..8........4..2...1....... +.....3.1....8.7..28..6...............69...7...2...8.6...43.29....17....43.89..... +39.6.......2.......4..3.85......5....6..1..9....4......15.6..3.......7.......7.82 +375.9.2..2....13..1.............572..8...7..1.63...........2...9.4....5....8...7. +.2...3..48..7..39...7....61..3.7...8..6..213.5.........4.8....6..8.1..2..5...9... +..82.4..16...5...9..1...........37..8..6..2....5..2.3.........3.54....7.2..9.5..8 +..2..8.7.86.....2..7....3...1.6792....8.......4.....5....1....9....5.46...7.63... +...9.2.....8....753..4..9....5.83....7.......61.2.......1..7.64...8..3.....6...9. +.....3.1...2..68......5.4...2....6..6..4.7.3.1.7.......4....7..9.6...2..5..9....8 +4.5.....9.3.......9..1..57......836.1..3.........7..92....9..2.749......5...6...8 +2.....3.4.....1.8.3........7..36......4....9.....2.....8.9......5....2.....7..6.. +1.....7.9.4...72..8.........7..1..6.3.......5.6..4..2.........8..53...7.7.2....46 +.9.8....3..6.7.1..5......4...........79...6.5.1...2.8....2...1....3..9..642..1... +.5...4...7..3....2..49.2..1....617....7.....554...3.1..2.....8.69....3....1...... +.3....4..........21..45..9...976..3...8......6...1...74.2.37.......9...6...1...5. +..92....4.....1..3.6.8..52.7..........39.....58..3.94...41.....65....8.......5... +..9.....3.....9...7.....5.6..65..4.....3......28......3..75.6..6...........12.3.8 +..248....9....65......9..7...98.54...........5....31.....6..83..28..471...7...... +..2......8.13..6.......7..4.1.9..........48.......6.75.24.9.3...6.........9.5...8 +....8.....915...3.36.....4..1......623.1.94....5...........6.91....4.2....73..... +.....73.2.6.35...9.8.....4.72......6.19.....4.....52.....51....8...32.....4.7.9.. +.....729.......3..9.46....52.....7...3...89.....75.....6.............4..57...2..8 +8.4..926..7...2...9..6...475.9..7...4.......3...2..9.....93.854.....8............ +62.......4...5.1.7..57.....8..3....6....873.........98.6.....3..7.21..4.1....4... +.65.1....7..4.5........6.13..6....7..2..9..8.5....7.3...4..2...........18....492. +.3..7..9...24....1....8.......6...7...8..5....4...85.6..1...3.93.6....1...712.... +.2...........53..9...12.58.9......36....8..1....2..7..7.8..6...3.9.....5.4.....6. +..4...1...6.2..7..5..8.....8..9...3...7.1.9.........4..36.4........7965.....8.... +..1.875...34..26..9...5....8.......4.....186.....7.9...8.......2..6......1......2 +...9...7.9.5..4..3.3....2...9..7..6...715....1...8.....6....42.8...97..........3. +....43..2..12..6....7..5..8........43......16....52.89.4........853....79.2..7... +.......1.2316....99......7..2..9.......4.1..61..2..5....8......3...8.9..64.9....2 +1.2....97...4.....9....13.....2....8.3.84...27....5.......5...........1.4876..... +.7....6...8.3..........62.3...........1...39.....954.22...5..8.51.4.....6....7... +...6.3...74.....1......59.....34...119....7.42...5......1...2...2..6..7....5...6. +....9......3.2.....7..419...2..8.1.......6.5.6......7..4...2.1...15...63......8.. +2...4.8.....1..3......98.4..9...34...8..6...9..1.8.....457....1..........6....53. +1..74...69...8...2...3..5..6...9...7..8...15............1...7..4..2......5..643.. +..7....9.5...2..7..4679.8..693..1......8...6.........5..167.........8.2.......4.7 +..34........7...3.7....15.62....8..7...94..........9..6.....25.8.5..2..1...6....8 +...1..3....4.5..8.2....9..554........9...3.....27...1.....68.....73...64.......52 +.....8.294..7.........2.1..85.3..7..6......91.1......85.....4...9...3.....765.... +7....46.8....6..5..5..38.........8.75.1.47.....9.1........721.642..............3. +6...7.8...9.5.....4....1....5...86.....2..........6.29.3..2..9........4.24.3.9..1 +4..7231..1...8.........9....52....7..7....3.58.............648..13...2.....2...53 +1.2....9.......5..9......2......6....2....8...8.4.5.6.2.153.4..37.1.4.....56..3.. +.682....7.........1..6.89..23...9.4......5.....43..7..3...1..8.......3...9..8..62 +.2..914....86....13....7...8....6.7..1.72...4......6..6..9..5....4....8..3....... +..8..5......23....9.24......6.........9.4..578.3.7..1.3......7....5...26.....84.. +..6.2.5....3.....4...745.2.....91....4.28......1..7....1......2.5.8..7....2....48 +..3.7..6.8..35..........3.127.9..8...9...8.....5...61..1.....4.........27...641.. +..3......8......959....42....1..394..6...........75..35..4...8.6....7.....9....1. +....7.9....2..1.5.....6..8.5.......963.........9....16.5.9.76..3....4...4...8.1.2 +....7.84..8.....3......2..6.1.4.8..9..86......56.1...874.........9..1.........357 +....6..87..9.......31.7.2....6.98.4.1.....7....87....6..4.....2...5.3...32....... +....1.....9.2.87.6.....685...54.....6.43...7..89...5...........3....728..4.1..... +.....1.3.86.........3.....9..56....3.2.51.89..9......6.....4.71....5..2..197..... +...........5.9.17..3...2..84..........94..3...7.86...4..7..8...2.......1..19...56 +1...7956.8....2.............61..79..3..1.........6.4....2.9....6.9...14....8...2. +.8....2.9.......6...31..78...56.3...6..4.7....3..8..7.92....53...4...9......2...6 +..3.8.95.8...9.......7.3..1.3.....46..8.4..2.7....25....26.4......9..3..59....... +..3.2..7.8..5..9.......7.8.7......93....4.7....6.1.8....7..5.3...12..5..5..1..... +......9...3.2.....8.....4.1.....689...3.47..27....2...5...3.....9.6......7...46.8 +......4....5.7..1....3.2..7.6..8......9..5....1....2.9........84..6.....3.79...65 +4.27.......7..1.....1.5........7.2.3.....4...6..8..5......1.479.9......5..62...8. +2...8.61..7....4...5..2....4..13.....9.........2..7..93...9...8..7..2......5..1.. +1.....3.8.6.4..............2.3.1...........758.........7.5...6.....8.2...4....... +.6......5..7..4.8.....9.2...2.5...3..8......7..43....15...8.4......71...6.......9 +.5.6..1......35...67...84..2.....8.......3.1.....7..5......4.6...9.1.3....2.....4 +.2...6.....758......5....34..8.....3..1735.8....9........8..........175.2....3.1. +...39.....8...6......14.7..1.....47....5....69...2..8..5....3..32..8...47.1...... +....12...6..9...3..1.8...761...8...75.93...4.......8....1..5..8.........794....5. +.....17..5.72.....4...6...23.4....1.......8..2.6.4...5..5....6....3.9..1.4..2..8. +......3..36.........85...79....8..57..........2...4163..9....4.4..8.2...2..4.1... +45.....3....8.1....9...........5..9.2..7.....8.........1..4..........7.2...6..8.. +4..6......73.8.......2...9....8..2.3.35.....18....4.7..1..........5.....9...1..48 +1.3.....9..6.......8....54.....49..36......2....1...5...89..2.4..73....1.4....... +1..2.37......8...5.....412........7..93.1......6..8...3.2..6.....1.3..8.68..5.3.. +.9.5.2.7...84.........1...6..1.7.5.48..6......3.......1....6..5..2...4...5.9...32 +.6.....7.87.3..59...54..2....9.564.....9...8.2.78.....7....4..2..8...71.........9 +59..2..4....9......4..68......1...72..8...4...1......6..92.3...7......3.25...7... +.75..6..4....7..9.4....1...5....8..6.....9..5..32.........9..1....1..2...48....7. +.672......2...........7.9.5...75.8.9....4..7..8.31..........54.3..8....2..1..3... +.6.......1...93...5...8.46.4...1......8..6.3...6..5.2....74.9............2..68..7 +.5.4....8..6....9..4...3..........39..5...8.6..985.....2...4.1.6..1.......8.....2 +.2..7.93.........87......5.8....4...3..1...6.....9.1..49.8....5.....3....56..2... +..9..6.1..7...13...3...2....5..6.8....1........7...65.9...23.6......954...4.8...3 +..7.....2..149.5..6......1....38..2.......4..73...98..48...7...9....3.....5.....6 +.......5...4.3.8......917...9..7...83..5.....8.6..3.4...3......54.7.....9.....62. +7.2.1.....5...4.....92..58.6....82.....7.....1.5.....65....93..........48.7..2.6. +3...5...91.5..3.6..46......41.....5...3..871........4.6...7.........5.31..82..... +.8...57......1.....2...71.3.3.84..5.....3..49..2..........6.....41.7...58......7. +.4...6.5.....8..94.1..3...7283..5.........5......17...4.....2......7...86..3...4. +..3.5.4..7...6.....5.8...6.5....3..4.1..7..8.2..4....7.4...8.5.....4...9..6.1.2.. +..3.......6.7.9..1...6......16...8.9.......24.98......5....2.6....5.4.3...9..32.. +..27....49.....7...1...6.5...5..........4.2.7..36879..3..5...7......4...859..2... +..2..9....6..31....7.65.4...1.3.....5.......7..8.2...9..19......8..7..95......3.4 +...5.3..........62......97.5..4...........19.78..1....2.16....84...753.......2... +...5..94..5.6...8...4.9.3..3...7.1..4........297......6..95...25..2...1..2..8.... +...5...6...7..14..2...3...............1.29.7.52.....18..48.73..98..4............. +.....17.....6....3.6.4.95.1.2.......3.9.7.......56.....1..4.6.........8.7.2.....9 +.........2..1....7.3.5..1....57..2....9...........2.465....6.93..2.376....8...... +8.5.....1.1....7.......1...2..46.9...8.........6.5.2..3..7.6..46......2..243...9. +65.48..12...5......4...35....6..8.......2549.7...9...6.....7.2..69......8...3.... +3...8.......7....51..............36...2..4....7...........6.13..452...........8.. +.3...4......8..562.........6.......9..1..7..6....2..4...41.2.87.13......8...6.2.. +.2.......8.6.9..7.9..35........6...9.7..1.68.........53.1..4.....21.....78...631. +..7..3568.6..1874............64..8.......7..9..485.3.....12....2.......4.53...... +..68.3......6....51.2...........13.69....5......9....75.....2..6..1....4.8.74.... +..4.....3...7.1....76.....1..2.........9....8.8.2...5.25.8...9..6...7.4......3... +..2..56...47..1........8.2..1.....49.84....32..9...............3..95..1.....125.3 +...3.8....4......6..6...5.......12...8..4......3..7.9.1....4..8...51.6....2....7. +....5..2....61...92....3..88......74..3.......1....9...96..57.....9.....4....83.. +......5..87.5.4.9..3..9.....6..5...9.8....746.........4.1....2....4..1.7...8..... +1.....68.....6.2...652.1....1.49.8.7........4..7......5..7....8..893....7....2.3. +.4..2......7.8....1..65.....24..3.1...9....48.5..9.7..........5..6...8919......76 +.23..4....76.25..1...7...8..1..6.7....91...62.....2...3.....8..5....9..4.9.....1. +.19...6.........8..7.25..4.3........9.6.......2.937.....28...9....4.9..1....6...3 +..7..1..9.3......2.....24....4..5..8...4...1..8913..5..1..6.............7.59...8. +..5..91.......36...2....943.......6....7...81.1..9.2..5.7.1...2..8........3.6.8.. +..3.8.......5.....7.84...3.8.9..6..74..7...6.......1...32...54.5....367.........9 +...4.86.387..3.......5......163....73.82..5.......6....21....68..5...7.......3..4 +9.6.....8.4.8....9.....67...9.......83..5...2..1..75......153.....4.2.1.4........ +83.....5....9.1...5....72...8.4...97...5....4....1.6....8......67...2.4.3....5.1. +3.51..........7...49...2.7.5..........648..........78.......5...2...9..3...86.4.. +15......2......63..8......13.4.69...7...23............2...7..9....8..7........34. +.2.5.9.4...5.....19.4.6.3.....695.....6..8...4.......2.3.7...1.....3...67.8...... +..2.....51...23.8.9..7....1....3..52..6.....8.9.1..6..7.8.695.........3..4..57... +...4...1.42......3..1.6...9..5......2.6..7.......5.7...8..9....7..14....59.6...2. +....8.3.425.1...9......3....4..2..3...6.9.8..7..31...9.......8.6.2....53..4..29.. +....1.....16..7.9..5..2..6......94.....7......42.....528......3..5.6....3..4..8.. +........43...6..9..857..6.........4..9...25..1.2.....9.....4..34.75.8.....6.1.... +7....2..1...15..8.3........1..94.6.2..4.1..7...78......7..........2...699....3.1. +35.6.......4.......2..7.89......5....7..1..2....4......36.9..1.......5.......7.82 +.3..6......4.5...7.5.7...8......1.........7..3.....465.1...7..4..581.....7....29. +..3...4.....53.68.81......2.......544..8....3..9....7.7...6.....4...52.8.2...9... +..2.4.3.86....2.4...8...7..5.......7......9....4371..5...91....4.32...8..6....... +..1.......6......73...6.89.8....93...3.4..2..2..8.5......1....4.2..4.........8526 +....7..9......8..4.42..1.7...4.....88..2.5..95.....7...7.6..13.2..8......6..3.... +.....1...9.468...1..6.3...7......6..6....9..44.25...83...8...4.3..19...........78 +.......8....78.2....4...6.12.5...13.........9..91.3..6..19....45..........365.... +.........2.6.5..4..5...43......4.....7.56.9....9.28.6.6......213.7....85....8.... +5..3...8.........9.79.28...1......7.....3..2..8..69.5.8....2....46.......9....4.7 +5......9.21...67....63..........1.5.9...........97.346..8.3...1.....8......4..52. +.92.6.5..8....54.6....98.....5..16...786....36..........9.4.....6..13..2.......3. +.5.......8....1.2.9..6....1.1..3...6...2...48.....6.39.....58..4.....6....6.73... +.2....5......6.34.9..3.....2..........91...64..7.9..1..31.78....4...5.........43. +..8..........6.784...73.9...9...7...6..1........9..45.5.7.......2.....19..12..3.. +..7...4....61......23.5...7..2.....581....2.....6...1....4...........34...8.327.. +....8...1..............947.4.9...76.6.......33.....1...8.7.......4.16.3..1...4.27 +....584..8.473...2.5....7..3.5.7..6..46.1.....9...........8.6.....9...73...1...29 +96......8....47.......8..63...178...4....21...7.....5.7...2..45..9..4....3...59.. +.8...4.2.35..9....7.......5..8..3..7...4...8..9.....12.2.1.5..46.18..........6.7. +.7..1.8.....8...91.....6..2..2.....8...46....7....5...8.3...4..9...3....1......57 +.12...6.....8.7......3.....73....4......2...9......5..4..53....8......3........1. +..43...5...38..2....8...7.....5.........3..4.7...2...1....756..9..1...8.5.....4.. +...4...25.....1.8..2..9..7.7.....3..6.3.......8..37.5..9.2.....4..1..8......78.9. +87.........53.6..1...2.9...68.......7..1..84....9.......1..42.52.....43.......... +84...9..1....8.2......7..4...1..4....83....6...9.....4...7...3..1.8....5.2..3.4.. +7.6...42.8.......7......93....2.5.6442........6..8.2.....5.6.....31.4.....5.7..9. +621......9......4....51..........9.7.7...94....2...65..4..7.......1...852..36.... +.9..2....6..1...........3.5...91..2.17.8...4...24.....71.....6.85.3...7..2..8.... +.7....4.......2...2.6....98..3...5......8.....419....2...23..6......7..9.3.4.5... +.3.4...6..2...14.9..46.....5.....1..6....895...8..7..4..98...........81.3...4...5 +.2....9......164..3..94......23...8..............79..6.918..7...3...5.6...5.....3 +.1.........5...9..2...8...5...6...7..4.3...1.5.9..1..8....2.4..1..8.3.6......7... +...46....568......3....1....3....96..956........7...4..8.94.3......7..2.1.....89. +8..7.............45..9...2.....63...9.1..5.3...51..........72.3.38...6....25...79 +2.9.......8....7...7...15..41.....3..9...2......7....8...6..3.....87..1.....39.4. +1...8...3.534...2.9..........72..16.5......9...1.....7...64........1.2....5.72..8 +...2.49...4.....5...37....88...1..64...3..5...9..8..........74..15......6......2. +....716.46.72..8............7..5.........6.2..1.....3..4......2....8.3...58..9..1 +9.1...2..5..78..4.........57...4..3.1.8..95.....5....2.4...6......2....12.7.....3 +4...7.1....19.46.5.....1......7....2..2.3....847..6....14...8.6.2....3..6...9.... +..6..48..47..3....2.3.1...532...8..7....5.6....7.....4...2...9.......4..1.....3.6 +..1.935..2..4.5..8........984..7......68......3......4......9..6.8..9....2....173 +...39..72.......95.7...53..............1..2....7.48.1..69....5..3...98..5.87...4. +....5...1.8.9...6.....8.7.....765..3.2.......6......18..6.1....3..27.5.4..4...... +.......9.....3..51.7..418...13...4......5...37.8....2...1.64....9......2...5..... +3..5..1.....1..5..1.....329..86.............724........2...3.....5..9.8...9..8.6. +14......6..7...93...3........5.7..9.3..41.5...9.8......2...86.......785....1....4 +.836.........7..4...........6.1.8..........27...3.....4.2.5....7.....8........1.. +.52.......6...9...9...8..53.1.7.2.3.....3.1.7..8....4.....7.89......6.....5.....2 +.2..8...7..6..9.......5.48...3....4.5...6.2..67.1....3..14.........17....8.....6. +.2..8......7..9..1.4......52....8..9...16.....785.....3..4.69....9.1...2.......6. +..6.5..9.1..6......5...1.8.2....31....5.....27.9.6....4..3....7...4...3...1..5... +..5....6..3.6.7...1.2.....34.......6.....98...8..3.....2.....78....1..5...39...4. +6...1..2..1.36..5...7..8...4......19...7..4......34....6..2....12.....4..5.....63 +49.6..........4.8........5....2...1.....167.2.7...3....689.......25..6..3.......7 +37.6.......4.......5..8.19......5....9..1..3....4......85.3..7.......6.......7.42 +1..3..5..5..9......6....4.8.4..7..2..32....5...5............8.6....43.......597.. +1.....4...65.7..9...7....63..19.......4..5...5..1..83.....9...6.3...7..2...8.6... +.96....73....76.4.......8...6..........5.4...5..281.........1..2.......8.7.94..3. +.3..7...4....25.......9..3.87.......14....6.......4..1......3...89....2...2689... +..4..3...8...9..6......8..1.135.......9...5.7.....6.1..6.72..59...3..1...7...92.. +..1.8...7...1.5..3.5.6.9.1.16..2..8.......6....5...7.....8..2....9.57...84....... +...5.........7.1.3...1.3.74..2.6...95....4....74....1..5.8..6.......9.25...3...9. +.....7.....8...5..1...4987.2....46..4.9.2.7.38........7.3....1....6.1.......9.... +..........6....312....85.7..57..3..69...1.2...3.......6...9.8..3....17...9...7..1 +93......1.67....2.8.....3...2..7....6...3.9......2.46.4..6......9..5...3.....4... +.9...71....8...26........5...6..3..27.1.95......6......738.........4...66.2...7.. +.6...13...9.5.........4...52...549.........7....9..5147.14..8..3...6.1.......8.3. +.2.....4..4...62717.3........2.....9..8...7...9...5.1...61.9..4...6...9......21.5 +..8....54...25.8.64..8....3.......2...9...7..71..4.5....798.........5...16...7... +..5...987.4..5...1..7......2...48....9.1.....6..2.....3..6..2.......9.7.......5.. +..4....89...1.2.6...6...7..2..8.4.1..4....5...3......8....7.......5.1....154398.. +...7.6.....1.9.52..9..2.........4...35......69..5....8.8.......6.3..5....42...1.. +...68.......9..4....2.....7.7....1.9..45..2..6......5.8...9..2.9.5..3....1...6..5 +...58......7...6..3..6.42..97..2.5.3..1.5..4.5.....7..4.59...2........6.....1.3.. +...2.5.9..47.........6.....5..9.1.......8.1........3..2.......6....3.8..7...4.... +84..1...27.....8.....5.........5..3.65...9.....2.7...1.....628...6.....523....4.. +1...9............1.3.6...7.......5.259..6..3.....7.89.6..7...4.94...8..6......2.5 +.2..316.......938.9.35....1......8...1.6...4.2.6.97....9....56..........5....2.38 +..75...311......4.9..4..68....7.8....9.14..53..4.5......58...2..8.2.51..7........ +..5...4.23..............1.6....73....1...95...4...137..6.9.....8...3.7.....24.... +..34.....5......47.871.......6.1.8.......2.9.....6...2..9.....1.3.7...5..5.98.3.. +...7.........62..53......274.5.1.7..61............3.9...1..4.....625.....7....8.. +...1..5.....3....6..7..9.3..16.32..9..5......38.4..............84...71........8.2 +.....7..9....2.3....3915.7......2.......8.7..725.9..1..........8.9.3.4.7.5.....63 +8..7....4.5....6............3.97...8....43..5....2.9....6......2...6...7.71..83.2 +6............5..3..31...2....7.19.6..8.5.64.......8....5..4..71...6.2.4.......... +.7.6..3..96...3...1..58...6..9.5.........21.........54...1....8.91.6..2.43..7.6.. +.4.2.6.7...1..........34....1..2.5..27.8.9.1..8........3..5.7..........89......61 +.3..4.98..6..3.7..9.......25...9....6..4.3.1....8.......1.....7...9.......7.5.2.3 +.3....2..9.4.7.5.11...5.3.....4.5.........7.96...3...5.9....6...8...2...4..1.7..3 +.1...5.....68....189....5.....79..46...14..8.......9.....2...5.47..3....3....7..9 +..748.3....4.65.........9.6.8.......1...5.6....5.48.7........2..7.......39.17.... +..72......5...1..34....8...84............2.596...7.3..3...2.....2.6....7...3..4.1 +..7..2.....53...7...69..1......2.........46...9.5....35.3...8...8......4.1..6.5.. +..4.93.8..68..............9.3..4.6.....6.5..2.8....7..6..71.....179............4. +..19...3..3...2..78..........3.6..4.2........4.53....1.1..49.....4.1.86....5..9.. +...6..9...4521...6.....7...5.......2..8....5..32.....4.2.....1.8..369.........8.. +...45...9........3..9.....1.6...87....8..2.6.9.46.5......83....6....1.....1...54. +....9.......3...7..8...24..4...6.....175.4..3...7....593....2.....1..6....5.....4 +....2....6....48.....9....7....7....8.5.1...3..18....9.5....34.4.2..36...8.....5. +.4....1....265..4..8....5..6.......1..1..9...9.82....3.63..8.2....4.7......5..8.. +..7..8.6.1...5...8...6.....7...6..35..4..7....9.2......5.4...2..7....3......16.4. +..2...3...3...92.11......79.8..54.....9...5...7.....13.......6.7..82......56.7.2. +....6...1.3.2..6..8..49.....5....1.72........7.4....3.6...7.5.......2..8..28.476. +..........46.5.9...5.4.1.6..1.7......3...58..7.8.63.....1....9.......4.2....27.56 +8.72......4....1.2.5.....9..1...39..4...5.6.8..56............2....37.......8.9..4 +7.......4...29....6...8..5....7..8...8...1.9...4.3.2...2.3....6176....2.4........ +.3.....82..2.9......4..67......43....1....5..7..6.5.2..9.2....6..65....9......31. +...3...9..2..71.......863..6....2.3.5.1.....97.....4.......3.7....5.81.3.18....6. +......9......45..2514...8..2...3.....65..1.7.7.......4...5.......69.....1.9..35.. +6..7....3.8..3.........26..8.........1..2.59....45.12.5..3.9..77.........9.....1. +15...47..8..........47.....2..8....5......2.85.1.6.....1...3..9...4..3...39.58.1. +.7......69..73....8.2.5.9..5........4...96....8.4..2......8.15...35....4...9..... +.2.........6..4.7.1...8.69........2.5..........91.7.56.....6...781..95....5.1...3 +..63...........1..82...1..79...4..1.3..297.........5.....51..8...4..8.3......26.. +..2.5..1.....6....4..1.7...8....5.....7.3.56.......2.75...936.....5...4.19...48.. +...6.......8.9..1...6.154........97152..7...4...8..2..7..3........56...246...7... +....97...8..3....1...86.3....16...9....9.26.35........3.2...4.7.5.7..8..7........ +....49...15.6.....7......3.5......262...3..8...7...4.....85...1.7.1.....68......7 +....3.9.1.28.9....7..5......8.......5.49.....91..5..7.......6.4...6.....3...7..5. +.......2.......8...15.6.3..1.2.9.5....9.....44...5......45...7....8.6.1..3...2.5. +9...8.7...52...8...7.....9.6.9..8.....5496..2....7.....1..5....4..........714...3 +1...9....82..75..3......67.5.3.....1.1.....2.9.....7.6.58.........26..57....1...8 +.8......9...1..6.....7.9..5.6.5.....42..3.8.....2.7...59....34...1....2.....4.... +.3...2..6........16.7.9..35.1..7...8..35.8...4...........6.5.......2.1..9...3.4.. +.1..85.6.4..3........1..9...3......18.14...5.6.....2..25..9..4..6...7..........86 +...68..9........259.4...8..2.1965...........3....7....31..............5.7.91.2.3. +....3..8....4....1..5.81....1....9..3.....26...7.2............9..29.5.147..6..85. +...........67....9.2..164...4....9...8.13...........25.179....4....8..6...3.2.... +39.6.......7.......6..9.12......5....2..1..6....4......85.6..9.......4.......7.32 +.7..............8...98.4.6...61.....5...4.2...4..9..7.....316.26.....5....42....3 +...6....4..53.9..1....8.73..1...6...6.3..8...7..91....2..........7...58..9....6.3 +...45...9........3..9.....1.6...87....8..2.6.9.46........83....6....1.....1...54. +....4.......6.23..83......9.7.264.1.......4......37.2...1......69....75.5..8....6 +....3.8......2....9....163...581...92.4.......8.5..........61....2.4....63....5.. +.....7.....8.5.....6....475....2.....3..9.64..94...3...5.....6....8..9.1..71.6... +.....6..7..2.......5.9...863...9.....8.....912..7..8....3..5.7.4...3.2.9...1..4.. +9..7..1...2..1...7...5..6........56......32..1...5...824..813...1542...6..9.....2 +89.5.........983.6..3..1....3......29.6.8......8.4...7..7...4...5...97......1...3 +38.5...29..5.4..3.......5...2.4............8.4.9.8.2...7...6......2.....864.9.3.. +3..1...7....9..8....95.....7.......9.2..3.7....8..4.5.......5..1..7.8.........247 +1.....3.....2.6......8.7..2..748....94.5..2..5.1.......1......9.69..8.3...5...4.7 +.69.1.7....1.2...63.....9....5..7....4.....7....9.6..5..3...54..7...3.6.8.6.....2 +.6..9..1..4......83.5.8.........7...1..26..7....9..365.89...43....74...6.....9..1 +.6..5.7..1......85..4.9..6...5.6.....1..2.3.......31...3.7..6..4..6....2..89..... +.3.6.........87.....6.1..54.....8.....2..6..14...7.2..7..8.......9..5.3.6.1.9...2 +.3.5.......4.3..2....8.6....2..49..66..25.1....1..8.9...3..58.........7.59......4 +.3..9...18.....6...17..6...7..2.1.4.......87......3....6...235.4..3.......8.....9 +.2...9.8.......29...5...4......7.3..3.71.....4.............69..69...4.327...3.5.6 +..14.....57......429.1..3.....5...62....69.57...........795....4...2....13....7.. +...47....8.....491.....1..3....1..39..982....5....36..41....7.6..5..........6..1. +......3....9..7.1.78.4.....3..5..2.....32..8.....81........64.........914.5.....6 +..........5724...98....947...9..3...5..9..12...3.1.9...6....25....56.....7......6 +9..7...6..12.4.......3..29...6....58.7...........5.43..2753........64...1..9....5 +6..89..1.....5..........8...274......3......54....7...2..6......85..4..21......39 +3...6...5.9...7.....6.....32..69.5.......1..2.4......69...1..7...43...8......8... +..72.....9..3..82.....8...6.8...43....1....5..2.51..48....962............1..5..83 +...569...........7.69.2..14..8....4.3.4.71......4.....5..6.......7...18......2..9 +...52.....9...3..4......7...1.....4..8..453..6...1...87.2........8....32.4..8..1. +.......42581.........86.......4..9.5.....3....68..7....53.2..84........72.....1.. +9...8.3.2.3.6..7..14.2....6...9......26.34.......2.....93.47..56.1.....7........3 +67...8......4..7...2...76....2....4...3.8..91...17.8.......1.5..3..5......5.....2 +53........79..2.....25..9.....2..68.7.......1.8..9.2....17.........38..2...96.17. +.6...75.9....43.......5......3...48.....6...5.12....6..9..72..44......2...8...1.. +.4....37...3.7.49...7.8......16............2359...2...........96.51.........3.51. +.2...34.75...74......6.........1.8...81.....2.....9.7383....9.........6.4..9.2.3. +..2..8.....37..4..9....5..15....2..96.4...8.....1.....2....4......91.6...9......7 +...6.378.......5.1.......3..8..5..9.59..4.2...46........98...15..........28.1..7. +...25....65....4..3.9......4.....13..8....6.9....835.......1.4..91.7.3..5...9.... +.....9.8.....4.....271.............7..6..29...7185......89..6...5.62...8......25. +.....8.7..13..............4...1.45...9...73....82....65..6..9...2..43.6..6..1.... +8.7..46...3..6.9.....7....3.69.....55....973...3.2....7.1..6......51...........2. +3....4..6....2..7..658....113.5..9..6..2.1.....9.........9..7.48...........46..32 +1.....9.78..4....6..23.............8.8.5..73.6.498.......1.9..2..5......4...5..7. +.9...4....1.6...4.....9...7.....5...4....152..57...8....8.3.......56..1..7...86.3 +...87..2..945..1....34....7..6.5.........7..2.7.....6...9.........326..1.4...8... +...7....2...2.1.4.9...4..7..97..6....1....23.3........73.4...2.5....3..9...15.... +...1...9......6.1..2...34.7..79....2....6.9....8.741..6...3..2..5.7..3...4....... +....2...8....64.1...4..73.....5..4.1...7....92.74..........3....8....5...69.....3 +2.4...8.......8.36...9..4.5..7...6.....3.7...16........7...1...4...8..9..82..93.. +1...9......4.63.9...7.....8..2..6...59.18..2............1...8...7...2.6....91.7.. +1...5.6...5.1.9.2.................6437...4.1.8............618.3..7......9....5... +.3.64..2.1.2.5..3..........8..3...793.9.....6..4....1.....7...3...8.9....57....4. +.3....4......8.2..1.4....9..7.....2..156.7........2....93...81....1....7...85..6. +.29..........4....7..1593.......573..........17..835.92.4..6..1...5..8..9........ +..8......9.....1...1...74.........25....28...73.1.........5......3.42.8.5.4.1.7.6 +..4......2..45..1..6......3..7.6...9...12.38....5..........7.6.....9...28.....5.. +....4...1..4.6..3....9....6...5.....986.7....5.......2..2..73..693....5....4..... +8.....4..16.3....2....2...1.3...92....9.....7..85...6..5..4..1.49...78........7.. +7...5.......784.3......1....4.....71..7.....9.294...5.8...16....3....6.5......2.. +7.....1.8.1..9..3..4...5.....64.......18..39....6..8.1.2.5...........7......43.5. +.62.....5....4.8..8...21...67.....3...1.73...3..1...48...7..9.6.....5..329....... +.4..7....7.3....928....14...672....99..1.......1..7.54...3.....4....5.8.......5.1 +..3....7..192....6.4....1...9.7.4...6.....4......36.8..7.6.3.5.......8..2..59.... +....7..8..7..2...1..6...5..3..9..8.......7..4....1.26...1.58....9....3..5.....61. +....62...4.73..........137...9.2......1..68......8..32.8...5..36.....1..79.....5. +.....5.3..6....84...51..6.....2...5..9..1...7..47...9......8...9.........27...4.6 +......3..84.....9...9..652...8.1.7.......2.4.16........9..3...7.......1.6..7.98.. +9.7.5..8..2.........57.3..1....72.........812..398..5.......46..9.......6.83...7. +..5...4..7.8.....3....6...1.....5.2....73..1.1...2.8....4..2..6...4....93.2.7.... +...9....7....1.38....5..2..8.7.6...9...1..7....4.....5.1..8....32.....6.6........ +...49....8..1...5..1............24...5.8.72..3...4...79....6..3..6....8.....7...9 +....85........96..96......2.1.3....9.5..27...48...............4....1..5.7.2....3. +....8...2.6...4....7....8.9..4.2...8.....1.35..3...6....6.......5.79....1....6.5. +.........97.64....1......9.51..7..43...1......96.5.17......753...2.6.41....8..... +.........8..2..37.9.24....5..698.5..7...4.....3...6..4.........3..8.9.1...9.6.4.. +8...7....1..6...82........1..6...2....724.5.....73..6...9.64......5..4...2......3 +4.....53.81...26......3.....5.9.............4..18..36...2.1......8...2...9..6...8 +3...4..6...592...1.8.6.....97.....45......2....6.3..9.2..4.......1..5.....789.... +16...89.....7.46.1..........87...3.......2.....9....5..5.......7.8.3.24..3....8.6 +.6...9.4...4....1.2.......7..2..65..69..1........78.9.3.8..5....5.1....3....2.8.. +.4.6.8.5.6...4.9..1....5....9....3.6..1......4..8...7...89.......7..62..2.......3 +.3....8.42...4.5......2.....15.9....6....8....24.53..7......49....1.......793...6 +.2.......7..6...1.....8.6.9.9......4......5.25..17..9.....5....3.7.49...4..7....3 +..3..8..........75...4..96.........7..1.5..3.9...2..5...46......6.1....47.2.3.... +...4..62..16.7.........8......2....8..4...5.75..3..............4....9.8...8.6.13. +....6...4..6.3....1..4..5.77.....8.5...8.....6.8....9...2.9....4....32....97..1.. +.....94...15....3...92......4..5.....8....64..61.....8........2.2.78..1...3.6.7.. +.......52..7..18...9.....7...2.4......3.28.6.7..1.....6..4.5..32.576..4.......... +4.....8.5.3..........7......2.....6.....5.4......1.......6.3.7.5..2.....1.9...... +35.1..6...4..6..2....4....3..2......9.....7.25...8..1..9.6..........94.761...5.3. +1.....3.8.6.4..............2.3.1...........958.........5.6...7.....8.2...4....... +..4.....69..7..8...86..4.5...9..3.7....8..2..2.8..6.4..1...8.....5.....9...2..... +...4.6..7..2.9...6......15......892.5......7...7.5.4..63.1.........89....19...5.2 +...2.9...9......7..746..........35...48.6...3..68..2.........3.8..1....5.17.9.... +48.3.......9.7......5.6.41.1.......2.5........63..7..9.....36....2.8..45...4...9. +1...7.6..8.5.....3..74...51......7....4.2....5..1.6.2....6.93..69............4... +...7..4....6.8..3..1....2.51....6.9....5.73.6....3....8....9.........7.2..7..4.5. +...6..7.8..........4.2.1..........5.5..7..8.683...6..17..5...3......9....628..... +...5..9.....8.1.258.......1.4......3.1.6.......5.7......9.6........136...3.9.4..7 +963......1....8......2.5....4.8......1....7......3..257......3...9.2.4.7......9.. +9..4.3.....2..7....4.2...9...4...56.5.......86.....4.3.7.315.....9..21......76... +8.63......4.6....9..5..74..1..7..8............84.1.6..5.......8.....37.4.39..2... +1..5....4.64...39...5...2..........8.5.1.........6.12..3...6..96....7...7...38... +.2.5...8...5..9....7......1.......13.....39..3..296..743.8......5..4..7...2...6.. +..3.8.5.......9.7......12..2..7......8..5.16....1..84..4.8.5...6......3.7...3.... +.....13...7...3..1......4....9......5...4...228...7.5...2....3.3...75..9.9..8.6.. +.......34.....5.87....2.1....9......7..3..89686........1.6.......24..3...4.9....1 +..61.8....4.....7........2.6...9...58.....3....7..1...5....3...98....6..3...2..59 +...6...4.......918..8..7.....5....6.86...2.....93.8...3..1....7.5..4...19...3.... +...3..25.....4..3.1.........38....2.....6...4..5.......2.8........5..7..6.....1.. +....41...9..5...81.7...96.....4.....3..6..14..8.1....3......83579.........2....1. +.....7....7...8456.1...4..326...9...8.1.....5..4.1.8.........2....496....4..7.5.. +718..2.6.........45...7....29...36.......9.5....4....83..7..8.5....1..9...92.8... +6..3.2....1.....5..........7.26............843.........8.15........8.2........7.. +1.5.8.......4.6......7...82..6..29..392...8.1.1.......62...749.........57..9....3 +.7..6..45.....18..4..8.....62.1........5....1......237..3..4.....2.5.3...9...6.5. +.58.....4..9..7.5...1....6...743.1....35..........1.2.....4.3...7.19...8...7.5... +..7.4..9...83..1..3..1........23...6......8.......67...6..1...2..2..79..41....... +..2..6..........31.7.28.5..5...7........1...3..8..26...5...47..3......9.4.7...... +...84..9.....512.....6...815.......776..9.8....3...4...7..63...83.4.5....2....1.. +.....2.8..7.6......1.....92....263.99..7.......8.156......61.4.........35...97... +9..6..8...53.8.2..........5..4.1..2..3...4........2.....1..97.3....5...9..87..... +9......4....4...9..3..9.521..9......6..53.4.........5...2..8....1.6.4..2..7..3... +1..........6.2.....4....98..5......6.....74....758...3..4..5.9....8..1.27...1.... +.2.3......63.....58.......15....9.3....7........1....8.879..26......6.7...6..7..4 +.2..8.1...9....7..5..7...32...2.....1..4..3..98...15.....5..9.......3..6.7.....4. +..635....8..4......3..2..5.37..8.6.4....3..9.4.......8....67......5...4..8....7.2 +..2.3...7.57......9..4...2..7.3.....3......1858...96...1..9.8........1..7..5....4 +...6.13..85.............4......7..523.1.............8..2.....7....1..6.....9..... +....3..824.1..................4.65...8.....3....7........5..7..6.....4...3..2.... +6.....7.3.4.8.................5.4.8.7..2.....1.3.......2.....5.....7.9......1.... +39.6.......6.......4..2.81......5....7..1..2....4......85.4..7.......3.......7.42 +.56...3...7.....8.8.3..1..7.1.78.9.....9............432856.........2...5.6...91.. +.47..........5..2.....1.......3..7.98...........4........7.36..51.....8.2........ +..7..69.....73..1....8..6......51..4.3.2..86..........17..9.4...25....8...9....3. +..2.....5.1...36.......4.296.9..7.4..2...1..65..4.......6.9.7.2..8....9....3..... +...869...1......2..4....7...67..43..9.....2...5.1....8...9..6......17.....5....37 +....2..19..16..5.....73.8.4.67....5.3...5........9.....89.......5....4.2.......3. +7.96.8...8..2.5.....6.4......8.1.6.3....2..5......4.1...2.....75.......9.3....8.. +4...5.....81..2..6.............4...2...1...5.....953.7.3.72.8...5...3.6...7..6.3. +1.5....2..2...5....8..9........8..6.35...4......3...5..1...9..6.......948.2..7... +.9..3.........826.....7.4...1.7.2......8....3.....697..7........4....5..2..5...8. +.834.........7..5...........4.1.8..........27...3.....2.6.5....5.....8........1.. +.673.4.....4..8..78..2....36.....9...5.8......7...9.4.....2..1..4.5....8..64...3. +...9.6..44......72..1.......8.37.4..62...8..7....9....73....51...8.5...39........ +...1..........84.9..139...5...9.61.....8..9.346.......2.....7...13...2..95.6..3.. +.....5.47...3........28.5....352..6...8......6....4.5.24...6..........7..9....1.3 +8.......9..71..6...3...5......8.6.2.......7..4.3.7...69..2......78.....3..6...9.8 +39....1.............648....5.8.....1.7..2..562..7....3...9.3.6.......2......57..8 +28..3.......5...4..........3.....8.2...4.7......1......64....7.....1.3....5...... +2......89....57........3..1.....563..2.14....8............9....9.8.3......7....45 +.4..56...59.1..2....18.........324.6.8.4.....9.......7.....1.3.......7...65....9. +.4.....8.8....63....9......1...2..393.65.....49...8...5..7.2.9..2..59.........1.. +.36....4...75.........3......49...6.7...4.8..6.....37..59.........1..7594..6..... +.3.....1.69..........593...2....13.............76..28..8.7..19.7..2..6.......5..4 +..93.7...2..9....4...8..6....36..41.....1....6....2..913....7.5..6.......7.4..... +..6...8..8.......29.7.6......3..4......2..5.14..97........1.6....5.9..84...3....9 +..5....1...4..3...2..7.4...5....26....1.6.5...9..7814...9....8...8.2...6......3.. +...6.5..........4.857...9...1.9.8...7...361...8....6..3.......4.6.8..5..2.......3 +....576...4.....1....3...85.8.........4165...6.....2.7..6..9..8.786..........2... +1..5..2.7.5.2....6....7.8......3.98.5....9..3..9....1.3...86...4..9......9..4..68 +1......9....3.1..4....6.......8.67.9.69......78....4..4....3.575...176.......48.. +.97.68.......741...5.......91.....8...2..1..5.........4...9..7..8.4.....72..8..6. +..49....25....4....7......8......6.3..1...5.795.......7..5..8.....4.1..9.6.82.... +..3..8..2.7..4...1..4...9...18.23..57....6.2...6.17........483.3.......75........ +..2..9.6..4...1.....6.3.4......2......97....3..3...1.5......68.3..5.7...4..1..... +..2...6..4..9.......5..6.81.8......5.1.........3.79.4.........6....2..73..73.15.. +....8.64......5..3.2.9.........49....3......14..7...687.8....2.......5..6...5..8. +6..3.2....4.....8..........7.26............543.........8.15........8.2........7.. +1.2.......6.....9..7..41...2..9..6.5....8..4...8..4.....6.2...7...5.....5..8....3 +.8.71.......4...1...9..2.5.3.46.1.........3..2.....6...9...32.7..28..1..6...2.... +.2.7..........13......8.2.1.1..589.3.5..9....4..........8.1...9...932.5.7........ +.2.....3....1..7...83.6......6.2857...9..7.........46.5...7....4....1..2.....4... +..8.1..2...4689..1..7........9..8.5.8....2.......7.9..3..2...7......74......3...2 +..8...4.6..2..3.7.....5.2....7192..4..........3.........62....9..154...2.4...98.. +9...5..4......86....5....9.2......3...8..7......1.6...7..6....4..28.9..7.9.....23 +.7...93..............483.618....2....1.....5.3......2.4...9.....69.75.3....6...1. +.6.4...8.9.7.3...6....9....5.......4.....37...3.5..16.4......5..8.......35.2.6..9 +.6.....7....2.85..9....1..2.9....13...65......38.......4..37.6...71....8......7.. +.6.....4.1......72.5.78........6.9.....5..8..9.3..1.....1......28.3....1.7...4... +.5.24...3.6.....2.8.......94..812.....5........83.5.....6...1..3...792.5...1..7.. +.5....6...4..7.8....14......8..........15.78.1...2......5..1.2.2..76.5.369....... +.5......2...9......76..4.9...8..6...1......5...2..31....5.....34.......1...87.6.9 +.456...9..........9.3...2.5...29..........7...3.8.4..2.5.38........1.8.6..6.7..1. +..82.......9..5..771...86.........4.48....9..1...6............1...5.2..39.5.3..7. +....7..8...6...5...2...3.61.1...7..2..8..534.2..9.......2......58...6.3.4...1.... +....327...1...........5....1......294.78......6.......5.....43.2..9........1..... +.....3.5...875...27.3.4...6.9.........1...8.....2.5...9...64..1..7...6...8......4 +9....34.....6....5.27..9.685....29.....3......7.....52.3.56.........7.4...89..... +4...8.....7...2..4..5....1.9....46...32..8......7....338...........4.29...96....7 +.3....6.7.7.....24..4..1....9......3....1..8.....5.7..6.2..4.....32.8.6..4....5.. +.2..58..3.........9.5...6..21..9.......7....44...1..6.53..4..2.6.2....35.9.....1. +..2..4......8..3..3.5..2.4.4.........8..15..6..7...519...4..6......7...1.....6.5. +....2.5..3..8...1......1..261.5......9....87......4....2..9..6.........3861....2. +.7......16....32.....7.269......9..3...5.....5..3.67....8.1.9.4.1.....2.9....5... +.476...5.8.3.....2.....9......8.5..6...1.....6.24......78...51...6....4..9...4..7 +..96...2.1......3.4.3...6.9.8..1......2.5..91.....7....9....3.2....917....6....8. +...9.746..96.5...1.3........21....9......4....75.....2.....2.....36...1.2...8...9 +...3...798.....3...6...1.....4..5.2.....3..9..8.2....523........5.98.6..6..51.... +...2.3..14....83......9......6.....2..5...8..79.....6.5...4.2....1.....79...65.8. +.....9.8..1........724...6....9.8.4.5...32..........35.3.6..4....9..3..6....1..7. +......7..8..3....19.5.8....2...7..4.3...4...6..9..3.5......48..6.89...1...2.5.... +8.......49...2.1..74......9......41..5...9.7...3...2.8...4......9...5.3....6.1... +4.8.....2.....3.....19..8......3.......1...4...5..2....3.546..91....9..67.....4.. +.89.1..3...43.9..6..2...9...9....3.1...8.......572.......9....56.3..41........... +.7..6...59..21..3.......6.42...3.........6..8.1.7.9.5...4.5.2..1.73..........1... +.3.56...7....4.....59..81......7.8......9.4...2...6.7.3.......1.189.3.....7..5... +..7..2...9..3..4..........6.8..7..2..13....4......9.1.7..9.6.....1...8..2....5... +..7...6..8......53.6.4...9775....1....1.3.....9......6...7.2..8.256..........4... +..3..4..2....6..8....1..69718.4.......75.........71...23.8..4.............1...578 +.......7...65.1....9.....6.3...8......2.7.4...649....81.....2....9...54..5...3..9 +5...8..13.1....7..8..4........8...94....23...9.......7..6.54.39..2..6.....5.....1 +.634.....9...........9..25.........96.9...54........7..176....5.8.2.3.6.....74.2. +..58.19.........2..6.7...3.9.7..6.........14.8.......3...61......4..7...62..9...4 +..1.......2..8.3.1..4.63......4...3.....95...9....16.2.6....4.7..57.....7....98.. +...9.....9.3..18.2....3.5....7....688...6..3..4...5...2.......3..175.........41.. +....1...3.7...2..5..5697...7.93..8..6..1...9.....7.2..5......6...894....1........ +..4.3....5...........9.47...1..758.4.6....2..24...1...9......1..2...6..7..1.5...8 +....1..8.......5..6..348..1.7......6...96....42....9...4..72...7.1....3....8...5. +.....9..319.....758...7..6....4.1...4...2.....7......8..7..51..2......37.16...... +.....1...84...75....5....8......9.23...26.7......7..5.7....24....8....35.21.....8 +.........9.....2.4645.......16..2..3...9..7.2.....8.6....5...1...73.....3....95.6 +7.4.6.8..25..4...1.....7......8...5..6..251.7...1..28......3.6.9.........25...4.. +7.....3..1...5.9..4..6....1.....98..264.........7...5.6....8.....2.6..9..3..4...2 +6..3.2....4.....1..........7.26............543.........8.15........4.2........7.. +.4.5.2...........1....9..6..5.43..7..62...........1..321.8....4.......5..98.7.... +.364.2...8......4...278.....2...873.6....49.87..6..........7.1....1..3..2....6..5 +..2..4....46...8.11.......4..5..8..6...9..5...8.41.......7..2..9......6....3....8 +......2..1...34...4..9.5......2..98...4.1....5.....3..65.......9..7.......14....6 +.579......9......42.......6.....9.7..3.7...8..1..3.96.....24.5...3..........68... +.56.7........91.579........28....3......1.......2.4.9...16.9.2.3.....5....9....4. +.5....1.....16..7....5.9.3.5.9..........3.2.72..4...6..4...1.....7.4...8.6.3.8... +.1.....4...9..2...26...7..972.5..8....1...5......1..2....42.....368..1..........3 +...5..28.74.......6...........3.2.5.1.......4...8......2....3......6...7....1.... +...45......1....5..93..6..8...64..1.......8..7..3.2.....9.3...6...8....21.6....73 +37.6.......9.......2..8.65......5....6..1..8....4......98.2..4.......3.......7.12 +.6.184.9...8...4.....5.9...7.........4.......9.6.5...1......84.4...237.......8..6 +8..3....19.2.........56...4..8..4.65...7....32.6..........1........8.5427........ +37.6.......9.......2..8.16......5....9..1..8....4......58.6..9.......3.......7.42 +2.3..............8...5.4.6.9.1..........4...653.9...8.....3.......1.76.4.57.9.1.. +2.....9.6...7...4......93....4......3.....5.959.6...7....4.76...4...5.....1.3..2. +.5..4.3..2......7.............6.2.8..31.........7.....6..2...........4.3....1.5.. +..5.9..4...7....8....2.8..36..3...2.......7.4..1..........4795....8.....8.......6 +......7.2..7.5.....3..69...5..6....8.....413..1........768......5..9.42..9..3.... +.......9..6...17..9.....2...1.3.26......7...87...4..1.2...39.6..8.......4..82.... +..........2..1.63....4..2...8..7...5.....4..7..68...4.9..........425.7....1693... +1...3.8.....9...2....2.5.6...63....5.8..9......7...1....5......6298.......8.6.4.. +.5....8......4..2.6...8...1...........6.....7.1.4325.....3...9..421.93....58...7. +...74...8.96..........2..4.....576..8......21..34........3.....124....5.......7.. +39.6.......6.......4..8.52......5....2..1..9....4......18.6..4.......9.......7.32 +2.3.8....8..7...........1...6.5.7...4......3....1............82.5....6...1....... +.8....1.9.7..54......9....3..2...8......87..1.9...5...259...........9.1.3......4. +.6..7....2...4.......5.14...8..1.....1.3..247..7...9.......8.968.....3...562..... +.5..2.3.66.93..42.2............1...4.46....89.1..........8........1...7.5..46...8 +..1.3..2.....7.6...5.6.1...7...4..3...4..51.8.8...6.........8.2.3...7...5..4...9. +...8....974..6.8.........3...841..5...925...............7..86...6....2.45.......1 +......7..2...........5.9.1...9.5..46...8....7..5..3...87.4..65..2......86..3..... +........673.8....1..8.1.9....42.....9...5....2..3..7..39..4.........5...84...3.62 +38....1...6..9......2....9.41....9....8.....29.......8.....3.57....86......1.4.2. +1.9.....8.4...9.....6..5.2.........1....62.45.6..74...82.........13...........519 +.8.3.......2......6....1..4..7..4...5.......6.1.2.9.7.....5..87.3...7.9...14..... +.4...2...3.....7..8......215...3.1...29....5...1..9......5......6..8.3.5...9..6.. +..2..9.3.8.......4..95....8..........8365....46..71..3.18.3.2.7.........7......1. +......8....6.1.....8.5...6.....2.4.....4.9..37.....5..82....3...417....9.5..3...4 +7......6.12..........28.31..4.6..8..9.5..42............5..4........75....7.3..4.8 +2..81.....48..9..5...........1....3..9...2..7...13.4....624.........67........95. +1..5...2....28..6...7...8..2..7.5..34.............39....6..8.....9.7.5.....4.9..7 +..85...6..........9...184......2....8.714...214.8..65.6....3....3..8.......2.71.. +..8.6.........9...36..12.....7....51.35.....91...8...2.....34..95.6.1..3.......9. +..2...358.....7...86...........8.2.47...1.....59......5..6....3.8.13..6......98.. +...627.....3...1..95.......8.......7.3..19....6.3.8...5......9..97....86..6.....4 +63......4..5......74..3..6..2.4.5........19......6...7..4.2..9.25.9.8......5..1.. +...7...6.83....7....9...8......4.68..71......6....2.1.59.4......4..73........69.. +...5....7.6..21..5....8...1.41..........92...57....2..41..6.8...........7.5..4.62 +...4.........61.7.....984....16.9..2.5...39..3..7....57.3..6.8..18...7....4.....9 +...27...98...........4.8..132...69.......9.3.7..1....2..........948....6.789...2. +.7.6..4..9.2....71....2.......3.9..83..4....7....6.2..7.19..5...93..5...5...4.... +.6....7.....4.1..3.7..289....6.....2...38......7..4..5.91.6.3...5.........41..... +.32....6....6.9...9..2...437...849....4.....6.5..1.....9.8......2...37..5......9. +.3...1...8....2.....279...5....7...8.1..8..2......397..574...1..8...9.....6.....4 +..43.1.7...2...3..8.......1....9..2..2...8.6...5.7.1...4..5..9......76...9...67.. +....7....2..8....7.....6.5.....9.43...9.....8..41....65..7...6...7.1...33..6...8. +.....534.3849...............15.3.9........1.2....2..8.....8..139.7.........6...5. +.....46...9..2......13.9...........87..6....1.3.59...........7...2....5.6..1..83. +8...9...7...6..9.....3...8.....5..3........527.1..6...4........3...798...9.....13 +8....1......4.8..3.6..5.2.1.....79...19..5..42...8....5.8...3.2.3......8.2..3.7.. +7..1.......9..3.5.......824....451..3.....4...7.2...9.2...8...6.3.7.....4.6...... +.6.9......1...4..5.....539....581.....17.3.4...8........2...81.....2...763....... +.6...........5182.2.....5.9.7...3......1.....9..7682.....6...3...49...5..12.....4 +.2.....8....87....5....2.6........5..6.2..4....1..3......9.6.3..4...5.78..5..7..9 +..3.......5..8...7874..5.........796.18.9.......2.......63....24.16........8.43.. +2..3..1.4..6...5.2.7.2.....8....23......4.6..7..9........65...........1..394.7... +2....8.5...3....6..7...2..4..6..73.....24...7.8.6...........4.....19....91......3 +.95..3...3..16....71.9.4....2....87.1..4....3.........8.....3.7....9...4.32....6. +.89.2.......4..5...........16...7......5...89.......2.7.....3..4..6.........9.... +.53.......765......8...1.69..1.....8....562.....2.4....3.8..7....79...8.........1 +......35.....78...........1.186...7.64.........5.9..2...9.8.....7...64..2..4....7 +8...1...6.23..9.........78......84..2......3.....5...96784..1.....5.....4....6... +5...........6.3.2..9451....94.3....7.........1....6..8....3279..8.9.1...........3 +.76.....1..1........2...94...9..1.3....5....6.4..2.....8......7.1..9.82.....37... +.4....9..1.6..5...9..4..1.8...6...5.71...........31....8..4........7..29..23....4 +..2..5..7..6.....3......69...7.43.6..2..9....1.........69.....2..38....14.1.3..7. +.....8.9.87....4.1..4...3...4..2..5.61...4..8................62...91......85.6..3 +.....5.4.8.7........5.....1.3.69...2....23.....4.....9..2...9.3.1..7...4.9.4...5. +......8..2..4.......12...45.4..7....5.2.....98.3...6.....3..5..1.85...9....74.16. +8....9...7.......53......21...69...3.9....51.....47...53....2....1.73..6...4..... +8.....3.26...35.....9...........26.9....7...1.5.....3....9.82.6.2.75.........31.. +1.2...6.8..42......5..8.........7835...1.....6...2571....7...694..9..1.2......5.. +1..6......35.14.6.........2..8..3.2..2.....93.1..2.5..4693....8....5....8.....7.. +8.1.......7..1...45..6.89......63.2....1........7..61..8........1.32..7.3..5..... +6..7..2.9..7......4..2...5....3.........2.46..98.....18...1...6.....6.3......5... +1..5..68............5786...........6..9...24...6.54.17.5.6.2.7...1.7......8.9.1.3 +..5.3............8....721.3....6..7.......3.2...8.4.5.5.9..6...4.8.....76.3...... +...64.3....5.3.....2..7.9....236.7.4.4............2..9..7....1...6.5...2.1....... +..........1..7...23..1...8..5....67....3.1.....8.4....8........4.9.6.1.......45.. +.3..6....8...9.2..9..34...1......8.............9.8.5462.64.......72...5.....5...7 +..23..58...36..9..9........215.......36.....8...1..2.....91..7457..4.......8..... +..21......846....5..3.94....2......4........3.....987.8....3..19...1..8..3..4.5.. +...23....1.....7.....8..........561..82.........4.....5....14...3..6............2 +....2..8..1.....7.4.6..7.952..7..4..95483..................1...5.......3..36..7.. +.....46.....5..7.19......8.3.....5....72.6..8.5..9....6..8.5..7..2...1....1....64 +42..7.....67......19..4.5.......3.65....9.1..7....8.......5.7.49..6.....5......12 +....7....9......2..1...29.7.....168.36..4..1....5......8.2......3...7..41...6.85. +....1....71..9....6..2....8.......5.2..3...7...5.2.....671....41.......3.8267.... +4...1..6.9..5.......2...384...4..1...9..51.4........78..6.39...........72....4..6 +.34..2..69....1.........7...4.9.82..5......3.....3...829..14........36....6....5. +.1...57.4...7...9...8.....58.4...1...2.14.....9...8.........273..3.8.......5.6... +...1..4...53..67....8...5...3..4....2.5..3.68..........8......7.92....53.71.9.... +....4...6.325..7..........56.7.3.5...........9...7.8.....8.2..9.5....1.34....1... +........6..8.9..1...73..2.53..7.65.....8.........5...2.15......2....1..76......9. +8.....5.......2....9..7......4...978.32......1...9..2...1.8..94.5....6....96..... +3.........2..8...9...45.1.6..2..........98.....564.81...7...5.....7..6.81...6...4 +...53..91...64.........9.2...436....7.......6.93..51..38.4...57..2.5......9...... +8....54...........4.6...8.2..1...6...9...3..42..8..7.......756...29.....64.53.... +7....2.4..6.5..9.......81..3.7.........7.1...8..4....5..........96...7..1..8.3..4 +32.6.......9.......8..7.15......5....7..1..3....4......15.8..7.......6.......7.82 +32......1.9....7..5.....38..6..5.8........92.2.8.9...3....832.94...6.......7....6 +1..5..3...7..2...........2....38.17......7..5..51..6.......68...2..1..6..398..... +.8.6....9..4....23......4..3.9.5......2.7.53..4...1....9..........12...4..5.97..1 +..82.....6...9..7......541..6....1......5...93..6..2..4..3.7...2.....9...86....5. +..75.....1...6..9.9...7...1.....46.3........54.8....7....3.7....23.1..6...1...8.. +..2........37.....74.....36....4.....5....2.1.2....69...5..2......3..1.7.9.1.6... +...37.8........5.1...48..2.5....3....4.......7.9...1..26.....79..5.9......8..76.. +.....193.1..986....7.3...8.6.......3....3.59...32..1...5...8....9.....4....4.2... +...5..6..85.7.....9.4.........1...7.......1.4....4925..123..7.......2........8.3. +...2...9..6..947....8..6....16.....5..4.1...9.7..4..3.....53.4.......8..2.......3 +....9.....24.5....86...2.91..68..1..5...4.......5..6.7.81......4.......5..2....3. +8...7......4..53...7.2...4...9....2..........5.3.8.1...6...4.7.......5.6....67..3 +4.....8.5.3..........7......2.....6.....5.4......1.......6.3.7.5..2.....1.8...... +27....4.6...3.1................7..3..5..4....8.....9.....4..57.6..9.......2...... +..92...6....4....82.............7.1..........36......2..3..915.1...75...48....9.. +..3......47.2....8.6...5..9..7.9.81..4.....5...51.........87......3....4.8.4..2.. +....69...8....4....7....6.1........23....17.....8.613.......4..1.4.9..6..23.5.... +....6....61...9...2....1.5..8....934..7.....6..3...2.....5......5.8....79...46... +9..2.16.5..67.......5............7.22....9.3..83.............4....6.3.2.64.5...1. +8.1.7..........62....5....7........1.6..43.7......9....94....5.12.8.......7....3. +4.....5.8.3..........7......2.....6.....5.4......1.......6.3.7.5..2.....1.8...... +..2..593.....2....1..9...5.3...5..4....4....85.81.2...2....6.754.....6....9...... +...5.71.4.......2..3.1.......24..3..5.....7....19.......8.6....3...9..65..5...2.. +.....9.616..4...5.2..5....3.3..6......4..1....7....218.....812..5...........5.7.. +.....3.8.2....4.6...15....7.37...45..6..........8......154.8..67...1......4.9...1 +........1...91.5..2..74.....9...72.4.486....35....3.....1..........3.....34....18 +4......6.....8..2...62...89....4....91...87..3........259..........93..2..4...1.. +34.6.......2.......7..3.51......5....3..1..7....4......28.5..6.......1.......7.32 +.9....3.1..3..48.........6.3.7..8...1....29...6.7...8..7...35..2..47.1...398..... +.56.1...4....69.5.7.14..6...6..8...1....2...9..4..7..............8.4..16.7.8..3.. +........31......7...65..91...78.1.6...4.5..92...4......68..4...41.9...5..79.8.... +4.....5.8.3..........7......2.....6.....5.8......1.......6.3.7.5..2.....1.8...... +35.6.......2.......9..5.48......5....6..1..9....4......18.6..5.......7.......7.32 +28...6....4..5.82..1.....9..2.4....33....5.......2.5.9..257..3.1....4...4..1..97. +.7.5.....4.....29..8..4......2.....7..9.......5..28......9....3.....7.157..65..4. +.1.6..9..4......5.....39.4......6.1..2...35...6.....72.8....4..5.3..4........7.2. +..3...1...6.2...9...2.84....19..6.7.7.8.1............5..7..3...3....2....8.75...3 +..2...73.....972.....4....639..1........7....8..6..1.....9.3..8..4...3.5.....1.2. +4..9.31..............16...9.52....3......1.7....57.6..6....87..1.....8....92..... +.8.....3...12...9..2.......6...74...9....3.7......1....3...2..1....48..3..8....56 +.5....1..3..9.6.4.......3....6...41....279.5...2..1...4.7..59.....73...........6. +.4.....98....61..........2.5.6.7.3.....9..4...........3.....6.5.9.8.............. +...59......7.2..5......74.1.1.8.......6.3..4....9....2.5...6.794.........8....3.. +....7.1..2.....96.6............126.5...4....8....5....1......9..47..9.....8..425. +....4...35.1.2..4........2..8......9.1..8.....96..58.....2.6.....9..1...2..3....5 +8..9.6...........7...3..9...1...2..849.5...36.2......9......54....2......31..7... +6..9..2..3.....6...82.76....5..9..8...1.........72.9.5....3.....2...4..75.8.....2 +.89.2.......5..4...........16...7......4...89.......2.7.....3..3..6.........9.... +...92......541.8..62..7............791.....4.4.2...3...6...84.........3..8.5.7..1 +...1..34......71.2.6.......9......6......1...3.792......4........8..52...5.31.7.. +....3.....7..5...25...4..9.4..6..9...9......13.2....7...7.948.....3..1....8....5. +7.....96......5..13......8...69..3...1...8.4.97..4........6.....24.....5...8.1... +3.41...8....2.6.3..8....9....8...65.9............65..3.....3..74..7...2..2...8... +..7.2..9.......5...3......1....15...4.......71.9...6......4682....2...45.2..38... +..4.......2...8.7..7.4..5..9....1.8.83...2.........6.36..5...1...5..43......97... +..2..6.......2.9.4..4.........8.....5.......2.8..49.533...5.6...5......87...6..3. +...5.3.......6.7..5.8....1636..2.......4.1.......3...567....2.8..4.7.......2..5.. +....8.......9.....9.53..486.1........4..68..2...1..69.....9..676.8..32....2...1.. +4.....3.....8.2......7........1...8734.......6........5...6........1.4...82...... +.5.....3...7.4.9.....9...7..6.78...9...3..2...1..5..4.6..1...9.7...3.8..1.2...... +...9...4681....3.7.............3....34.....18.7..19......5.......2.489....6.7..5. +...6..8.3.5..1...4..9...1....6.8.....9..673..5.....7.......9...4.1.326...3......2 +...456....4.....2...8.1.5..81....9.3....7....6.2....18..1.6.3...3.....9....7.9... +...4....7.479...6..5...7...2..3.......4...6.8..8..4..2.....5.9.6..19..2..3.8..... +....5..6...........95.76.8..6..194..3.............8.217....431...21........78...4 +9...6.1.......1......2...76..23.5....8.9....2.6.....8.4.3........57..6......5.93. +.4....9....5.2...3....5..8...12.8.4......4......9.7.6..238..6.........9.65......1 +8.2....93....7.6.........2.5....6..8...3....57.49.....4..8......6..4......5.6..1. +.71..9.....5.1..3..2.....5.....2..8..1.4.86.3..6.....1.4......59...........2.3.7. +.1.....826...3...............9...74.3..8.2......1.....4...6.5........9...2....... +.9.2..86..8.53....5......1..6..8..9.8.5.......7.4..2....4.5.6.....8.........79.5. +.9.....7......4.35.31..2...2.716........2...4..6.....81...7.....6........743...8. +..7..53..8.2..9..1....81......6...........18.4.9.1..2...17...6.6...2.5...3....... +..5.8......75..9.....741....143..6...6....5......1.3.........32..3.5.7..7..4....6 +..41..8..9.......55.......4.2.3........4...71..8.9.......613.....78...26..1...9.. +..2..84...7..9....9...5...7..41...6......2....18..5..........3..5......64..6.79.2 +...8.7.5......2.4915.........9......8.29...6...3.7.......68...249......5.....37.. +....8..........472.75......3....5.......798.5...2..7..2...53.6.6..........86..1.. +2........48..9.1...5.....6.6.5................39.257..1...8.97......2..3.....3..8 +.1....8.....7....5............5...73.68......4........3..4...2.....826..7........ +....1..2.4..9.......9..2..38...2....6..3..5...3.4.6.1....1.985.58.....4.........6 +1.............5...2.463..8.49.3....6....7...3...5...2.7.9.6.....2..481.........62 +.9...54.........1...41...6....3.46....6.12....48.....5..5..3...8...7..........287 +.8.3...5......4.3.1.......6..7.9.3.4.6...7............3.9.4.2...14...7.....8....3 +.6......7..1.95...........8..3..94.1....3...6.8....35..2.6......17......4...1.7.9 +.5.4...7.8...5.3.1.6...1...2.....7..3.....25..86.......2..7.....3.92.6.4...1..... +.5...9...6.4..2..3....7...4.......1.8...3....769...2........7.539.1..........6... +..9.......2.39....6....2..1.1...7.288......5....9...6...5.........4.1....7...83.6 +..19...6......8....48.3...5..9.72.....5............7.2...8..1...864.1.73...3..4.. +...3.7...4.......86...............9.5....1.63.32...7..8...19.4...5.6.1....4.5...6 +....1.......8..49.......867.5.......17...3...3......4.5.62........9......426.87.. +......57..5..1...........8....7.2.3..4....6.....5.........6.4.17.2......3........ +......1.6.5...9.....7.3..5.......347..5......7.4..2.......7..386...2.....8.6.57.1 +61.8....9...3.6.........25.7.8.2...3..37..9.........6..5...78...9......5.....1... +53...8.1.......5...79..6..2...7.9.....6..589.....2.........1..43........8...4.7.. +1...49....6.3.................5..1.74.2....9.3.........7.6..9........42........1. +7......5.....52....4....38...61.....9.......7.8..3..9.2.74........9.8...4...7...6 +43..5.......1....9.....7.....5.8.34....42.5..8........1....46...6...9....2......8 +14.3..7..8.........6...1....1.7...2.3.48.5.9........1.4.6...8....5..31.....4.2... +1.....89..8.27......5..82.7..1...4.5....1....7...5....3...........4..97..9.6.7.3. +.9...6...5....7..6..2...3....6..5..48.....79.7....2.......5..1....62...8.3...1.4. +..89....3.....15..6......4.3.57..168..1..............25.4.......8..24.....36....1 +...2..7.6..5....9..3.6.........4.5...9.17.86......2.....1...45......4..3..8.1.... +....35.69.......8..1.9..3.......68.4.593......7.........25.....5....27.14........ +.....42..8....9....64......3..7.5..957.........6..3.4...95..7.2.3.......7..4...16 +.3.85.1..7..3..2....5..4.6..........284.....7...9..........6.1.6....5......48.3.. +..45....7.2..96............2.....9...438...1.....15.....5...43.78..........18.... +..3..........51.....23....4......61...19.8..3.....62..798.........8........1.956. +..2...3..1.4.......3....4.6..91.3.....6....2.....85....7...4.1.3..75.9..6........ +...9......85...6..4..2..........6..3..7.......2.43857......79.1......8.539...4... +.....31.....2..7..43....92.8.2....5.65...4........1.....9.67...3...12.........48. +..2......9...4.......671........8.......643.7....53.6.43.8...2.68.....5.7......1. +....7......5.6..4..68..42..9...1.3.77............4..21..12....95......8...7....3. +.....16.......5..82...8.9.3.3.1.2...5....3......74.3...14........6...4.9..7....6. +.9....7..27.4..9..8.43........5713.6.......8......9.....7.3.1......64....6......5 +.64...7.....8.1.....3......5......28.7..6...........5.2..5.........4.3..1........ +.3..8...4......36..6...1..2..3..2.......3815...8....2.2...64..9.862......7....... +..4....6.....15...6....7.5..9..2.3..3..7....9.....62........8.14..1...7...9..3... +94.....7...2..76......21....7.8....55.9...86...36......9.7..1..7..9.4..8........6 +7.8......4.1....5......74...2..5..8...4.9.2.......6.4.893.4................2..3.9 +.3.....6....54.2..2...9...7......1.......8.4.....56.7...63....8.45.......87..4... +.264.....85.........95...7...2.36...........6.7...5..8.1......4....92.5......7.3. +...5.1..2......4.12.......798..1.....42.6..19.....2..3.1..9..3.79..8...6......... +....6...58..5..2.....8....641.....9....71....7..4....82....3..7..7.8..6..9.....3. +1....5....3.4..9...7......4..1.........5.97.36.............4.79..8.1.4......8...5 +..1..5..6.....9.4.39.....7.2...3...1.8.....9....8.6.....34.........1..8...69..3.. +...8...2...6...3...4.2.......3...6.15..9.....8........29.....5.....7........6.... +.....7.1.....4.5...4...3..7..36...9..579.....1.....8...3..59...2.6...1.....2..... +72..1.....4....8.3..8..........7..31..952.6......8..4......2.....164...9.5....4.8 +6..9.....5..1....71.38....4..6...93.....2.........5.82..1......2.....4138...3.... +5..47.....2....9............3.1.9..........87...2.....7.4.6.........31..8........ +37.1.....6....5..48........4...8.3...169...8.......2.....7.....9..4..8.6....2...9 +16.4...3........7.58..2...1.1.9.....4.......8.28..6.9....738.........5.6..4...... +.6.9..3......7....123........7..9..6....6....84.....2.5928...4......58..6....7..1 +..8....2.7.4....8.1..9..6...7..6.5.......3.69.2....74...65....3.8...4............ +..247..58..............1.4.....2...9528.9.4....9...1.........3.3....75..685..2... +..2....4..1.....328....1.7...7.15.6..5....4.7.........79.5...2...6..2...2..89.6.. +...5...4.....132.7....4.6..4.......2.5.........27..3.9.6.3.2.8...7........18..7.. +.....21..9.2.7..3..8.3.................5..7.4136.......7..6582....8.4..........6. +.8..7.........46...1...........2..716.53.....4........3....6......7...8.......5.. +.7...1...92..8.....64..92.52.....9...9...6.1...7...8.....8..6.........5.4....512. +.3.6..8........7.5...5...126..4......149.....8.2.6...........7.....8.2.19.1....4. +..7........48........123..5...91...46....29...1....2..4.........7....1.9...3.68.. +...46....84......3.5..8....312.5.4.....3........1.6...2.....98......7..4.95.2..1. +....3......3.1.75.42.8..........93.56...4....9.8.........2..8.......36.9.17...... +1..6........7......74..13..73.....18..89...........26.....5..2....82...5.953..... +..8.5....65..1...4......1.7.....47.9..2.......3....8...6.3.....8....75...9..62... +....92...5...8.6....63..1....12.7....2..4....9..538..........8........977.8.....3 +7..9.2..53.....9.7.4.7...8....2.........1..3...46..21..1.......2.7...6...3...1..8 +.2..58.9.5.3.6..4..6....8..1...3.....9..1..2....5....7...9........7.....6.4...5.. +...6...5....3......2..947..6.9..23.8.1.....26.......4.3....6..9..89.3...4..27.... +...4...62.5.81......7......1.29.8.........3..9...6.....241..6....1..67..6.....23. +8.......1...95.................7.42.3.16...............4....57.6..3.8.........2.. +.472.........96..1..2........6..4.3...8.1.......8........9....36....274.7.4..8..9 +..9..1....7.8.....1....6.3.4....36......5......5.7.24.....6...8.3....1.26....8.5. +..2.4.......5.1..39...8.....1..3..78.9.........8.1...6..5....8.3...2.5.4471...... +9...4.5...2.....1....581....8...76..3....4..5.5.....97.......6..32..54..1....2... +1.2..49...4..9....9..7.1.....45....6.3.........8.621....1..9...4..1..5...6....8.. +.6..3....1..2.....2...7..5.9........5..8...41.....29.8.......64.....6.....41.95.2 +........185....67...64..5..6....2..3..467..1...8...........6.....3.1..49..1.34... +4..8....37.......4..372...6.2.....6..3.5..7.....6..9..8.....4...1...5....5..9.... +..24...9..7.3.....9.4..13.5..8...........5.1.74.....2...186.9........4.2...2...5. +....9.......681.4....4..2.1....3...6..1..........26859.47.........9.5...2.8...1.. +.....1...9.8....3....36.4...8.1....3.7..5......2...6....3....9.5......84.1...5.7. +.......7...63...1.8794..3.6..8...1.9...26.........45..72..8............3..1...6.. +..........73...5...527.8...1......9...6....4.9.4.31..........23......76....6.94.. +.47.8...1............6..7..6....357......5....1..6....28..4.....9.1...4.....2.69. +.4..1....5..4.8....612........1.7..3......5..4.3...81.9........7.6.3.9.2....697.. +1.....6....769..42..47.....51.9.......8175..4....8...6..1...8.93...17............ +....935..........82......47......8...6.4....98...5972.4...2...679...........1.... +.362...8......1.3...7..5....1.....4....1.3.5.....6...9...9....847....9..2....4.1. +.3...9...8.5..1....4..8.......2..6.8....7..4.7..4..3.5..1...8...7..5421...6....53 +....6...8..2......49....31...6.34........85..94...1......2.94...8.......51.....3. +......82..18.36.....7........34.1...6...7..89........28...4.76..94.5...........4. +5..........42.8..3.....3......6.54...9..3......8.49.........5..7.....64.6.2...17. +...2..7.6.....614.4....8.2.38.....9...53.1.......6....5..9.23..2......7..79...... +..4.98...5.9.3.......7...4.......92.3.....8...82..95..9....6..3.2..537..1........ +..3.8.2........6.....6.2..1.....9.....7.3.4..94.8.......42..3..718........9..5..8 +..3...6......24.....43.1...23.8..4.9.4......87....23......5.89.....7..1.5....3... +.361...2.8...6............7.....5....9..213....4....19.2....7..5..894.........1.. +.3.6.8....47..........1.89..7....18....8.23..6......5....4.15....8.2...6.1....... +...2.6....7......59.....34...7..1......6......5....2.3.9..47.3.43......8..5..9..2 +....8..5.93....7.....6.1..2698.4.5.........4.5.....2.84...2..7..69.......52...9.. +.152............86..4..........4.1..9......3.....1....6..8.9...37.............5.. +..1...6.....9...3....5.....6...24.......3..97...1..........24..15.......97....... +.....2.......7...17..3...9.8..7......2.89.6...13..6....9..5.824.....891.......... +........4..415..9.9....6....26.75.4.......9..4......8..7.......64..2.7....2...3.6 +5....82...2..5.9....69....8..18......4.....72........345.1.9....19..48.......6... +9..5...3...4..3..2....21.5...6.1..4...3.69...2..4...........46978.2.............. +9....8.....6.9....3..4....1..9....478..67......7....235......1...316...9.......7. +.761.9...9...6...1......4....5.2....3.......57....523.4..6.........1.......28.7.. +....9.7..8....3.6.7.3....2...6.471............2.1....5.9.6........5..68...4....5. +........1.6...379..9......82.61..5......8..2...93....7..2..7.4..5.2.....7...38... +9...2.7...4..6........8.3....8....2.....9....51...7.....1.75..6..........67....82 +19.........2..4.793........45..........28..6.2.....73....17.6...4.63..1.........5 +12.5.76..8.....4.........5.......9.8....2....7.3....2....8....6..96.38...7...2..4 +....4.....14.....6..29.3...2...7..9..9....5..6.5..1..35.....72...6.....1.....7.3. +.....8....3...57....4....61....6.3..7...2....9.2.4......1....9.37....5.....3..... +......6.2.3.8......1..........5..73.9..4.....2........5....6.4.....2.1......9.... +.5.8.6..........27...3......6.5..8..7....1...4........2...7..........38.....4.... +.5.....8...4...9.6...6.9..1...5.7.4...2.......3......7..19.4...2.....5.4..5..8... +6..........574..6.94.8.......4...2...6...8.4......15.7.3.........7....24.....3..1 +2.6953........2....8.....3......9......3..1...53.4..7.1.2........8.....1...4..65. +.2...6....5...72...6..81....1...53....8.9......9......4..6....8......7137........ +6......5..1..39.6..8..2...4...8...........9....1..5...9......4..2..7.........4817 +..47.8.....8.....2...6..1..3...54..7.....9.....9...5...87....3.....13..4.2.....6. +1..3....7..6.9.....4...5.....3...8..6.1.3....7...6..35.....67984.............8.1. +1......6...72....49......7..3.49.............69...8.5.32.8.7.....6....1....5.683. +.8...6.....9....2..6..1....17..2...33.....471..............28195..3........14.... +...4.8.2..3..9..8.....6..94.....5...6..8......12.36..8..1....6.46.5......9.....1. +7.....13....1.64..6....7.8.429.........2.........8.....9.81..........6.9..53..... +.5.6...14.1.7.3..........5...1...63..69..2......3....26.2..14.........2...3.....7 +..67......1..5..863....6...6...1...2159..4.........8.....8....7......3...216..9.. +.....37..8..72...1.......632...5.8..4...32.......6.3.......9.2...8...4..74....... +..874.9..1..5....7....2...5...8..49.....7....62.93......3......78....1.9.....2... +.6.28......5.......7....318..165........2...5.....7..6..9.48...78.....3.2.......9 +....3..7.9.1....52..7.....4....9......2..8..96..7...2..4...6.....6.57..13..1..... +.67....83....4....58....1.26...9...882.5..9.......7..........36.5.8.....9..4..... +.8....1..7..14.2..4..............5.3.4..7...8..64.1.......6..........8.5.53..2..1 +9.....2....7.....3.6.2...8.........2649...5...517...6...4..59.....1.46...9....... +1.2.5...4....2.58.....9.2......7.94.518.....6..........952......7..3........69.7. +...7..58.5........4..9............54....31.....8...9.6..7.9.41...1.8.....6.....7. +...45..2.1.2.....4..9........7...31...12.59..........8.....7..6..8.9.1..39.1..... +........934.........6924..........57.8...1...17.....2....26...3.....9..6...8..27. +.........17.6...3..8.7...599....47..3.2.1.......36....7..43...1.....9......1..8.4 +.34...8.7.5.8....41....6..3..3....45...6.12.............832.7.....9.5.....5.....9 +.4....7.....7....1..5.21..6...8..9..6....2..3.3...5..83.164........5......6..82.. +..9..25..13.......8..97..........61....6...4......8..2.......575.21.......6.8...9 +.53.......21.8.3.......1.245.28......7.63..........9...6.75..38.........7..2..... +..74..5..6....2...............71.8..2.....1..3........5......3..4.9............26 +..6........48...1........5..1..7..349..........5694......3.76...89.6.5.2....8.... +.6..1.3.2..8.........9....5.2..6....4.............2.713.7..94....5.........7.56.. +....8.7......9......9.4...34.8.....2.5..37....7...5.........61.19.........2..3..4 +.....45..6.47...9..1...8.4....59.....5....938..7...........23..1..4...567.6...... +...7..35.6.1..9......38.....7..3............65.9..4.8..56...9.42.......53......1. +1....6.8..64..........4...7....9.6...7.4..5..5...7.1...5....32.3....8...4........ +.7......5.........24..5...3..97.5.....28..67......92...6...8.4.....9....9...1..2. +.5..812.....5.......9...5.......3..8.1......9..8.9.41.4...3876.8.7.6.....3....... +.9.....7..6.....9...1..8.......92..6...8..4...34.5..19....1....4.....1..5..38.9.. +....2...59..3.1...........9....7...26.4.........4.816..9.2.5...4.6.......1...78.. +7.1....4....8.56..3.........8.6..5..1......3...........2....8.....47........1.... +..4.81.7...3.749....2........5.2.1.....8.6.........4..4...6.51.......6....1..5.29 +....714.2.........2.7....3..4.19.25..8.......7....3.4...3..7...5.4....9.....2.... +9..4..3......89....3....5...81.7.........57...........5...371...9.2...8...6...... +27....1..1...........5.......53.8....3.4...........9....4....836...1........2.... +......52....3.1..4.4..57.8.286................3.8..91...1....5..9.7..16..5.....7. +....4...1...2.9..3.731.........8.62..6....83.8.......4.5..1..9.7.16....23.2...... +....1...78....562...5..7....58.7..1...26...........7.6........14....38....3.9..5. +..8....47.1.3..8....5.......5.6........2.9...83...4.9...6.8.1..7....2.......5...3 +6..3..2....7....49.........32.6............871............47....5....3......9.... +1.7..259.4...8.7....2......54.1......8.7..6........94.....7.....65..94..3........ +...6...34.....96.....4.89....395....4......2.6.7.....5..1.........5.1.9.....2.7.. +...5...........9.1.5...4...6..4......73...8.4..2..6..3...7..6..9.....7.2..48.2.3. +.68..7......3...1....9.2.8...6.5.247.147..5..5........7.......4..5.9.........8.2. +....5...9.6.....3....3.941.3..6.....6..8.1..3.....5..7.315.7..4.5.....2.4...8.... +412.3.....7.5.............43.4.98....59....2....2..9...6.78...384......1..7...... +....7...1..6.....5......4...9....5..6.81.5..........8731..9....76..2....2..31...9 + From f26f74e119b3dea01dec491fd39222c31b3fac6e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:01:17 -0600 Subject: [PATCH 078/182] fix: bugfix in construction of slices in SAT/theory interface --- src/core/Internal.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 867fcf3e..b26b7e51 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1644,8 +1644,8 @@ module Make(Plugin : PLUGIN) invalid_arg "Msat.Internal.slice_propagate" ) - let[@specialise] slice_iter st ~full f : unit = - for i = (if full then 0 else st.th_head) to Vec.size st.trail-1 do + let[@specialise] slice_iter st ~full head f : unit = + for i = (if full then 0 else head) to Vec.size st.trail-1 do let e = match Vec.get st.trail i with | Atom a -> Solver_intf.Lit a.lit @@ -1658,7 +1658,7 @@ module Make(Plugin : PLUGIN) let[@inline] current_slice st : (_,_,_) Solver_intf.slice = { Solver_intf. - iter_assumptions=slice_iter st ~full:false; + iter_assumptions=slice_iter st ~full:false st.th_head; push = slice_push st; propagate = slice_propagate st; raise_conflict=slice_raise st; @@ -1667,7 +1667,7 @@ module Make(Plugin : PLUGIN) (* full slice, for [if_sat] final check *) let[@inline] full_slice st : (_,_,_) Solver_intf.slice = { Solver_intf. - iter_assumptions=slice_iter st ~full:true; + iter_assumptions=slice_iter st ~full:true st.th_head; push = slice_push st; propagate = slice_propagate st; raise_conflict=slice_raise st; From ced266663e4b886568842b90b10b6f62d526e414 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:01:50 -0600 Subject: [PATCH 079/182] refactor: change backtracking API (push/pop_levels) --- src/core/Internal.ml | 22 ++++++++++------------ src/core/Solver_intf.ml | 30 ++++++++++++------------------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index b26b7e51..2eb2695f 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -736,8 +736,6 @@ module Make(Plugin : PLUGIN) elt_levels : int Vec.t; (* decision levels in [trail] *) - th_levels : Plugin.level Vec.t; - (* theory states corresponding to elt_levels *) mutable assumptions: atom Vec.t; (* current assumptions *) @@ -796,7 +794,6 @@ module Make(Plugin : PLUGIN) trail = Vec.create (); elt_levels = Vec.create(); - th_levels = Vec.create(); assumptions= Vec.create(); order = H.create(); @@ -1040,7 +1037,7 @@ module Make(Plugin : PLUGIN) assert (st.th_head = Vec.size st.trail); assert (st.elt_head = Vec.size st.trail); Vec.push st.elt_levels (Vec.size st.trail); - Vec.push st.th_levels (Plugin.current_level st.th); (* save the current theory state *) + Plugin.push_level st.th; () (* Attach/Detach a clause. @@ -1108,13 +1105,13 @@ module Make(Plugin : PLUGIN) ) done; (* Recover the right theory state. *) - Plugin.backtrack st.th (Vec.get st.th_levels lvl); + let n = decision_level st - lvl in + assert (n>0); + Plugin.pop_levels st.th n; (* Resize the vectors according to their new size. *) Vec.shrink st.trail !head; Vec.shrink st.elt_levels lvl; - Vec.shrink st.th_levels lvl; ); - assert (Vec.size st.elt_levels = Vec.size st.th_levels); () let pp_unsat_cause out = function @@ -1974,6 +1971,8 @@ module Make(Plugin : PLUGIN) check_vec st.clauses_hyps && check_vec st.clauses_learnt + let[@inline] theory st = st.th + (* Unsafe access to internal data *) let hyps env = env.clauses_hyps @@ -2112,16 +2111,15 @@ module Make_pure_sat(F: Solver_intf.FORMULA) = end type t = unit type proof = Solver_intf.void - type level = unit - let current_level () = () + let push_level () = () + let pop_levels _ _ = () let assume () _ = () let if_sat () _ = () - let backtrack () _ = () let eval () _ = Solver_intf.Unknown let assign () t = t let mcsat = false let iter_assignable () _ _ = () - let mcsat = false - end) + let mcsat = false +end) [@@inline][@@specialise] diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index a51fee18..f4059814 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -168,12 +168,11 @@ module type PLUGIN_CDCL_T = sig type proof - type level - (** The type for levels to allow backtracking. *) + val push_level : t -> unit + (** Create a new backtrack level *) - val current_level : t -> level - (** Return the current level of the theory (either the empty/beginning state, or the - last level returned by the [assume] function). *) + val pop_levels : t -> int -> unit + (** Pop [n] levels of the theory *) val assume : t -> (void, Formula.t, proof) slice -> unit (** Assume the formulas in the slice, possibly pushing new formulas to be @@ -184,10 +183,6 @@ module type PLUGIN_CDCL_T = sig If no new clause is pushed, then proof search ends and 'sat' is returned; if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) - - val backtrack : t -> level -> unit - (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the - same state as when it returned the value [l], *) end (** Signature for theories to be given to the Model Constructing Solver. *) @@ -197,12 +192,12 @@ module type PLUGIN_MCSAT = sig include EXPR - type level - (** The type for levels to allow backtracking. *) - val current_level : t -> level - (** Return the current level of the theory (either the empty/beginning state, or the - last level returned by the [assume] function). *) + val push_level : t -> unit + (** Create a new backtrack level *) + + val pop_levels : t -> int -> unit + (** Pop [n] levels of the theory *) val assume : t -> (Term.t, Formula.t, proof) slice -> unit (** Assume the formulas in the slice, possibly pushing new formulas to be @@ -214,10 +209,6 @@ module type PLUGIN_MCSAT = sig if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) - val backtrack : t -> level -> unit - (** Backtrack to the given level. After a call to [backtrack l], the theory should be in the - same state as when it returned the value [l], *) - val assign : t -> Term.t -> Term.t (** Returns an assignment value for the given term. *) @@ -393,6 +384,9 @@ module type S = sig @param size the initial size of internal data structures. The bigger, the faster, but also the more RAM it uses. *) + val theory : t -> theory + (** Access the theory state *) + (** {2 Types} *) (** Result type for the solver *) From 9a8f0e9d82c201a56a3b5ad12b878a7f19cf4c4b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:02:06 -0600 Subject: [PATCH 080/182] test: add small sudoku solver to test the CDCL(T) interface --- tests/Makefile | 7 +++++++ tests/dune | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/tests/Makefile b/tests/Makefile index 209361a8..e8b199ee 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,3 +6,10 @@ test-icnf: diff regression/.`basename $$i`.out regression/.`basename $$i`.ref \ || ( echo "mismatch for $$i" ; exit 1) ; \ done + + +test-sudoku: + @for i in sudoku/sudoku.txt ; do \ + echo "test problem $$i"; \ + ./../src/sudoku/sudoku_solve.exe $$i ; \ + done diff --git a/tests/dune b/tests/dune index 7c8ca088..1c4c32dc 100644 --- a/tests/dune +++ b/tests/dune @@ -17,3 +17,7 @@ (deps ./icnf-solve/icnf_solve.exe Makefile (source_tree regression)) (action (run make test-icnf))) +(alias + (name runtest) + (deps ./../src/sudoku/sudoku_solve.exe Makefile (source_tree sudoku)) + (action (run make test-sudoku))) From 1c188afb6b94c2a82092f871b0151e0b323987db Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:07:34 -0600 Subject: [PATCH 081/182] test: refactor a bit so that benchs are run by dune --- Makefile | 2 -- tests/dune | 9 +++++++++ tests/run | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b576e4b9..b24f052f 100644 --- a/Makefile +++ b/Makefile @@ -23,8 +23,6 @@ build-dev: test: @echo "run tests…" @OCAMLRUNPARAM=b dune runtest --force --no-buffer - @echo "run benchmarks…" - @/usr/bin/time -f "%e" ./tests/run sat enable_log: cd src/core; ln -sf log_real.ml log.ml diff --git a/tests/dune b/tests/dune index 1c4c32dc..2dbfc6a6 100644 --- a/tests/dune +++ b/tests/dune @@ -10,14 +10,23 @@ (alias (name runtest) (deps test_api.exe) + (locks test) (action (run %{deps}))) (alias (name runtest) (deps ./icnf-solve/icnf_solve.exe Makefile (source_tree regression)) + (locks test) (action (run make test-icnf))) (alias (name runtest) (deps ./../src/sudoku/sudoku_solve.exe Makefile (source_tree sudoku)) + (locks test) (action (run make test-sudoku))) + +(alias + (name runtest) + (deps ./../src/main/main.exe ./run (source_tree .)) + (locks test) + (action (run /usr/bin/time -f "%e" ./run sat))) diff --git a/tests/run b/tests/run index 9275f63e..7720b5cb 100755 --- a/tests/run +++ b/tests/run @@ -1,7 +1,7 @@ #!/bin/bash CURDIR=`dirname $0` -SOLVER="$CURDIR/../_build/default/src/main/main.exe" +SOLVER="$CURDIR/../src/main/main.exe" solvertest () { for f in `find -L $1 -type f -name '*.cnf' # -o -name '*.smt2'` @@ -18,5 +18,7 @@ solvertest () { echo -e "\r\033[K\e[32m[OK]\e[0m $3/$2" } +echo "run benchmarks…" + solvertest "$CURDIR/sat/" "Sat" $1 solvertest "$CURDIR/unsat/" "Unsat" $1 From a6d74898ff982a35b9fa37b0f37fc55fd2234b84 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:15:04 -0600 Subject: [PATCH 082/182] test: add another check to the sudoku solver --- src/sudoku/sudoku_solve.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 5fbd1347..ca222526 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -47,6 +47,7 @@ module Grid : sig val parse : string -> t val is_full : t -> bool val is_valid : t -> bool + val matches : pat:t -> t -> bool val pp : t Fmt.printer end = struct type t = Cell.t array @@ -96,6 +97,11 @@ end = struct Sequence.for_all all_distinct @@ cols g && Sequence.for_all all_distinct @@ squares g + let matches ~pat:g1 g2 : bool = + all_cells g1 + |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) + |> Sequence.for_all (fun (x,y,c) -> Cell.equal c @@ get g2 x y) + let pp out g = Fmt.fprintf out "@["; Array.iteri @@ -323,6 +329,8 @@ let solve_file file = errorf "grid %a@ is not full" Grid.pp g' | Some g' when not @@ Grid.is_valid g' -> errorf "grid %a@ is not valid" Grid.pp g' + | Some g' when not @@ Grid.matches ~pat:g g' -> + errorf "grid %a@ @[<2>does not match original@ %a@]" Grid.pp g' Grid.pp g | Some g' -> Format.printf "@[@[<2>solution (in %.3fs):@ %a@]@,###################@]@." (Sys.time()-.start) Grid.pp g') From ecf1de42b558f7021a85e6c3043e74ee92cabb64 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:21:00 -0600 Subject: [PATCH 083/182] chore(opam): require sequence --- msat.opam | 1 + 1 file changed, 1 insertion(+) diff --git a/msat.opam b/msat.opam index d5c45b44..c0d0a3a5 100644 --- a/msat.opam +++ b/msat.opam @@ -13,6 +13,7 @@ build: [ depends: [ "ocaml" { >= "4.03" } "dune" {build} + "sequence" "containers" {with-test} ] tags: [ "sat" "smt" ] From a0ba576b0f4bda9bc66cf92ed00ff71a48aeceef Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Jan 2019 22:29:06 -0600 Subject: [PATCH 084/182] test: details in sudoku solver --- src/sudoku/sudoku_solve.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index ca222526..8269ac2d 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -308,6 +308,8 @@ let solve_grid (g:Grid.t) : Grid.t option = Solver.solve s let solve_file file = + Format.printf "solve grids in file %S@." file; + let start = Sys.time() in let grids = CCIO.with_in file CCIO.read_lines_l |> CCList.filter_map @@ -319,6 +321,7 @@ let solve_file file = | exception e -> errorf "cannot parse sudoku %S: %s@." s (Printexc.to_string e)) in + Format.printf "parsed %d grids (in %.3fs)@." (List.length grids) (Sys.time()-.start); List.iter (fun g -> Format.printf "@[@,#########################@,@[<2>solve grid:@ %a@]@]@." Grid.pp g; From 8f1c24c1a6ff3a5b73f82911f3caddc588e8ba26 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 11:23:45 -0600 Subject: [PATCH 085/182] refactor: change API to `{final,partial}_check` --- src/core/Internal.ml | 8 ++++---- src/core/Solver_intf.ml | 22 ++++++++++++---------- src/sudoku/sudoku_solve.ml | 8 ++++---- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 2eb2695f..74c89bb7 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1690,7 +1690,7 @@ module Make(Plugin : PLUGIN) ) else ( let slice = current_slice st in st.th_head <- st.elt_head; (* catch up *) - match Plugin.assume st.th slice with + match Plugin.partial_check st.th slice with | () -> flush_clauses st; propagate st @@ -1914,7 +1914,7 @@ module Make(Plugin : PLUGIN) n_of_learnts := !n_of_learnts *. learntsize_inc | E_sat -> assert (st.elt_head = Vec.size st.trail); - begin match Plugin.if_sat st.th (full_slice st) with + begin match Plugin.final_check st.th (full_slice st) with | () -> if st.elt_head = Vec.size st.trail && Vec.is_empty st.clauses_to_add then ( @@ -2113,8 +2113,8 @@ module Make_pure_sat(F: Solver_intf.FORMULA) = type proof = Solver_intf.void let push_level () = () let pop_levels _ _ = () - let assume () _ = () - let if_sat () _ = () + let partial_check () _ = () + let final_check () _ = () let eval () _ = Solver_intf.Unknown let assign () t = t let mcsat = false diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index f4059814..fdb7675a 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -174,13 +174,14 @@ module type PLUGIN_CDCL_T = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val assume : t -> (void, Formula.t, proof) slice -> unit - (** Assume the formulas in the slice, possibly pushing new formulas to be - propagated or raising a conflict. *) + val partial_check : t -> (void, Formula.t, proof) slice -> unit + (** Assume the formulas in the slice, possibly using the [slice] + to push new formulas to be propagated or to raising a conflict or to add + new lemmas. *) - val if_sat : t -> (void, Formula.t, proof) slice -> unit + val final_check : t -> (void, Formula.t, proof) slice -> unit (** Called at the end of the search in case a model has been found. - If no new clause is pushed, then proof search ends and 'sat' is returned; + If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) end @@ -199,13 +200,14 @@ module type PLUGIN_MCSAT = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val assume : t -> (Term.t, Formula.t, proof) slice -> unit - (** Assume the formulas in the slice, possibly pushing new formulas to be - propagated or raising a conflict. *) + val partial_check : t -> (Term.t, Formula.t, proof) slice -> unit + (** Assume the formulas in the slice, possibly using the [slice] + to push new formulas to be propagated or to raising a conflict or to add + new lemmas. *) - val if_sat : t -> (Term.t, Formula.t, proof) slice -> unit + val final_check : t -> (Term.t, Formula.t, proof) slice -> unit (** Called at the end of the search in case a model has been found. - If no new clause is pushed, then proof search ends and 'sat' is returned; + If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 8269ac2d..72dc1e6b 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -261,14 +261,14 @@ end = struct ) ) - let assume (self:t) acts : unit = + let partial_check (self:t) acts : unit = Log.debugf 4 - (fun k->k "(@[sudoku.assume@ :trail [@[%a@]]@])" (Fmt.seq F.pp) (trail_ acts)); + (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.seq F.pp) (trail_ acts)); add_slice self acts; check_ self acts - let if_sat (self:t) acts : unit = - Log.debugf 4 (fun k->k "(@[sudoku.if-sat@])"); + let final_check (self:t) acts : unit = + Log.debugf 4 (fun k->k "(@[sudoku.final-check@])"); check_full_ self acts; check_ self acts From 872d4433db87103dd1e6f07cdd8e4916f4bf4c52 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 11:30:11 -0600 Subject: [PATCH 086/182] feat: add a "backtrackable ref" module --- src/core/Backtrackable_ref.ml | 29 +++++++++++++++++++++++++++++ src/core/Backtrackable_ref.mli | 30 ++++++++++++++++++++++++++++++ src/core/Msat.ml | 2 ++ src/sudoku/sudoku_solve.ml | 31 +------------------------------ 4 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 src/core/Backtrackable_ref.ml create mode 100644 src/core/Backtrackable_ref.mli diff --git a/src/core/Backtrackable_ref.ml b/src/core/Backtrackable_ref.ml new file mode 100644 index 00000000..bc91cfd5 --- /dev/null +++ b/src/core/Backtrackable_ref.ml @@ -0,0 +1,29 @@ + +type 'a t = { + mutable cur: 'a; + stack: 'a Vec.t; + copy: ('a -> 'a) option; +} + +let create ?copy x: _ t = + {cur=x; stack=Vec.create(); copy} + +let[@inline] get self = self.cur +let[@inline] set self x = self.cur <- x +let[@inline] update self f = self.cur <- f self.cur + +let[@inline] n_levels self = Vec.size self.stack + +let[@inline] push_level self : unit = + let x = self.cur in + let x = match self.copy with None -> x | Some f -> f x in + Vec.push self.stack x + +let pop_levels self n : unit = + assert (n>=0); + if n > Vec.size self.stack then invalid_arg "Backtrackable_ref.pop_levels"; + let i = Vec.size self.stack-n in + let x = Vec.get self.stack i in + self.cur <- x; + Vec.shrink self.stack i; + () diff --git a/src/core/Backtrackable_ref.mli b/src/core/Backtrackable_ref.mli new file mode 100644 index 00000000..a1755115 --- /dev/null +++ b/src/core/Backtrackable_ref.mli @@ -0,0 +1,30 @@ + +(** {1 Backtrackable ref} *) + +type 'a t + +val create : ?copy:('a -> 'a) -> 'a -> 'a t +(** Create a backtrackable reference holding the given value initially. + @param copy if provided, will be used to copy the value when [push_level] + is called. *) + +val set : 'a t -> 'a -> unit +(** Set the reference's current content *) + +val get : 'a t -> 'a +(** Get the reference's current content *) + +val update : 'a t -> ('a -> 'a) -> unit +(** Update the reference's current content *) + +val push_level : _ t -> unit +(** Push a backtracking level, copying the current value on top of some + stack. The [copy] function will be used if it was provided in {!create}. *) + +val n_levels : _ t -> int +(** Number of saved values *) + +val pop_levels : _ t -> int -> unit +(** Pop [n] levels, restoring to the value the reference was storing [n] calls + to [push_level] earlier. + @raise Invalid_argument if [n] is bigger than [n_levels]. *) diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 1dbd99a3..be7f6ce4 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -49,6 +49,8 @@ module Make_mcsat = Solver.Make_mcsat module Make_cdcl_t = Solver.Make_cdcl_t module Make_pure_sat = Solver.Make_pure_sat +module Backtrackable_ref = Backtrackable_ref + (**/**) module Vec = Vec module Log = Log diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 72dc1e6b..d69026eb 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -125,36 +125,7 @@ end = struct a end -(** Backtrackable ref *) -module B_ref : sig - type 'a t - val create : 'a -> 'a t - val set : 'a t -> 'a -> unit - val get : 'a t -> 'a - val update : 'a t -> ('a -> 'a) -> unit - val push_level : _ t -> unit - val pop_levels : _ t -> int -> unit -end = struct - type 'a t = { - mutable cur: 'a; - stack: 'a Vec.t; - } - - let create x: _ t = {cur=x; stack=Vec.create()} - - let[@inline] get self = self.cur - let[@inline] set self x = self.cur <- x - let[@inline] update self f = self.cur <- f self.cur - - let[@inline] push_level self : unit = Vec.push self.stack self.cur - let pop_levels self n : unit = - assert (n>=0 && n <= Vec.size self.stack); - let i = Vec.size self.stack-n in - let x = Vec.get self.stack i in - self.cur <- x; - Vec.shrink self.stack i; - () -end +module B_ref = Msat.Backtrackable_ref module Solver : sig type t From 95bdc80ed511c91b1bab10976983c9f761f51327 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 12:00:04 -0600 Subject: [PATCH 087/182] feat: rename slice to acts, add some functions in it - add literal - add term - eval literal --- src/core/Internal.ml | 47 ++++++++++++++++++++++++++------------ src/core/Msat.ml | 18 +++++++++++---- src/core/Solver_intf.ml | 36 ++++++++++++++++++++--------- src/sudoku/sudoku_solve.ml | 10 ++++---- 4 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 74c89bb7..fb60d0bc 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -873,7 +873,7 @@ module Make(Plugin : PLUGIN) clause that constrains it. We could maybe check if they have already has been decided before inserting them into the heap, if it appears that it helps performance. *) - let new_lit st t = + let make_term st t = let l = Lit.make st.st t in if l.l_level < 0 then ( insert_var_order st (E_lit l) @@ -1608,19 +1608,19 @@ module Make(Plugin : PLUGIN) Solver_intf.Assign (term, v) | Lit _ -> assert false - let slice_push st ?(keep=false) (l:formula list) (lemma:lemma): unit = + let acts_add_clause st ?(keep=false) (l:formula list) (lemma:lemma): unit = let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in if not keep then Clause.set_learnt c true; Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); Vec.push st.clauses_to_add c - let slice_raise st (l:formula list) proof : 'a = + let acts_raise st (l:formula list) proof : 'a = let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma proof) in raise_notrace (Th_conflict c) - let slice_propagate (st:t) f = function + let acts_propagate (st:t) f = function | Solver_intf.Eval l -> let a = mk_atom st f in enqueue_semantic st a l @@ -1641,7 +1641,7 @@ module Make(Plugin : PLUGIN) invalid_arg "Msat.Internal.slice_propagate" ) - let[@specialise] slice_iter st ~full head f : unit = + let[@specialise] acts_iter st ~full head f : unit = for i = (if full then 0 else head) to Vec.size st.trail-1 do let e = match Vec.get st.trail i with | Atom a -> @@ -1653,21 +1653,38 @@ module Make(Plugin : PLUGIN) f e done - let[@inline] current_slice st : (_,_,_) Solver_intf.slice = { + let acts_eval_lit st (f:formula) : Solver_intf.lbool = + let a = create_atom st f in + if Atom.is_true a then Solver_intf.L_true + else if Atom.is_false a then Solver_intf.L_false + else Solver_intf.L_undefined + + let[@inline] acts_mk_lit st f : unit = + ignore (create_atom st f : atom) + + let[@inline] acts_mk_term st t : unit = make_term st t + + let[@inline] current_slice st : (_,_,_) Solver_intf.acts = { Solver_intf. - iter_assumptions=slice_iter st ~full:false st.th_head; - push = slice_push st; - propagate = slice_propagate st; - raise_conflict=slice_raise st; + acts_iter_assumptions=acts_iter st ~full:false st.th_head; + acts_eval_lit= acts_eval_lit st; + acts_mk_lit=acts_mk_lit st; + acts_mk_term=acts_mk_term st; + acts_add_clause = acts_add_clause st; + acts_propagate = acts_propagate st; + acts_raise_conflict=acts_raise st; } (* full slice, for [if_sat] final check *) - let[@inline] full_slice st : (_,_,_) Solver_intf.slice = { + let[@inline] full_slice st : (_,_,_) Solver_intf.acts = { Solver_intf. - iter_assumptions=slice_iter st ~full:true st.th_head; - push = slice_push st; - propagate = slice_propagate st; - raise_conflict=slice_raise st; + acts_iter_assumptions=acts_iter st ~full:true st.th_head; + acts_eval_lit= acts_eval_lit st; + acts_mk_lit=acts_mk_lit st; + acts_mk_term=acts_mk_term st; + acts_add_clause = acts_add_clause st; + acts_propagate = acts_propagate st; + acts_raise_conflict=acts_raise st; } (* Assert that the conflict is indeeed a conflict *) diff --git a/src/core/Msat.ml b/src/core/Msat.ml index be7f6ce4..e9d36153 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -11,6 +11,11 @@ module type PLUGIN_CDCL_T = Solver_intf.PLUGIN_CDCL_T module type PLUGIN_MCSAT = Solver_intf.PLUGIN_MCSAT module type PROOF = Solver_intf.PROOF +(** Empty type *) +type void = (unit,bool) Solver_intf.gadt_eq + +type lbool = Solver_intf.lbool = L_true | L_false | L_undefined + type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { eval : 'form -> bool; eval_level : 'form -> bool * int; @@ -36,11 +41,14 @@ type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.re | Eval of 'term list | Consequence of 'formula list * 'proof -type ('term, 'formula, 'proof) slice = ('term, 'formula, 'proof) Solver_intf.slice = { - iter_assumptions: (('term,'formula) assumption -> unit) -> unit; - push : ?keep:bool -> 'formula list -> 'proof -> unit; - raise_conflict: 'b. 'formula list -> 'proof -> 'b; - propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; +type ('term, 'formula, 'proof) acts = ('term, 'formula, 'proof) Solver_intf.acts = { + acts_iter_assumptions: (('term,'formula) assumption -> unit) -> unit; + acts_eval_lit: 'formula -> lbool; + acts_mk_lit: 'formula -> unit; + acts_mk_term: 'term -> unit; + acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; + acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; + acts_propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; } type negated = Solver_intf.negated = Negated | Same_sign diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index fdb7675a..816636e0 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -82,24 +82,38 @@ type ('term, 'formula, 'proof) reason = *) (** The type of reasons for propagations of a formula [f]. *) +type lbool = L_true | L_false | L_undefined +(** Valuation of an atom *) + (* TODO: find a way to use atoms instead of formulas here *) -type ('term, 'formula, 'proof) slice = { - iter_assumptions: (('term,'formula) assumption -> unit) -> unit; +type ('term, 'formula, 'proof) acts = { + acts_iter_assumptions: (('term,'formula) assumption -> unit) -> unit; (** Traverse the new assumptions on the boolean trail. *) - push: ?keep:bool -> 'formula list -> 'proof -> unit; + acts_eval_lit: 'formula -> lbool; + (** Obtain current value of the given literal *) + + acts_mk_lit: 'formula -> unit; + (** Map the given formula to a literal, which will be decided by the + SAT solver. *) + + acts_mk_term: 'term -> unit; + (** Map the given term (and its subterms) to decision variables, + for the MCSAT solver to decide. *) + + acts_add_clause: ?keep:bool -> 'formula list -> 'proof -> unit; (** Add a clause to the solver. @param keep if true, the clause will be kept by the solver. Otherwise the solver is allowed to GC the clause and propose this partial model again. *) - raise_conflict: 'b. 'formula list -> 'proof -> 'b; + acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; (** Raise a conflict, yielding control back to the solver. The list of atoms must be a valid theory lemma that is false in the current trail. *) - propagate: 'formula -> ('term, 'formula, 'proof) reason -> unit; + acts_propagate: 'formula -> ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) } @@ -174,12 +188,12 @@ module type PLUGIN_CDCL_T = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val partial_check : t -> (void, Formula.t, proof) slice -> unit + val partial_check : t -> (void, Formula.t, proof) acts -> unit (** Assume the formulas in the slice, possibly using the [slice] to push new formulas to be propagated or to raising a conflict or to add new lemmas. *) - val final_check : t -> (void, Formula.t, proof) slice -> unit + val final_check : t -> (void, Formula.t, proof) acts -> unit (** Called at the end of the search in case a model has been found. If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; @@ -200,12 +214,12 @@ module type PLUGIN_MCSAT = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val partial_check : t -> (Term.t, Formula.t, proof) slice -> unit + val partial_check : t -> (Term.t, Formula.t, proof) acts -> unit (** Assume the formulas in the slice, possibly using the [slice] to push new formulas to be propagated or to raising a conflict or to add new lemmas. *) - val final_check : t -> (Term.t, Formula.t, proof) slice -> unit + val final_check : t -> (Term.t, Formula.t, proof) acts -> unit (** Called at the end of the search in case a model has been found. If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; @@ -418,8 +432,8 @@ module type S = sig The assumptions are just used for this call to [solve], they are not saved in the solver's state. *) - val new_lit : t -> term -> unit - (** Add a new litteral (i.e term) to the solver. This term will + val make_term : t -> term -> unit + (** Add a new term (i.e. decision variable) to the solver. This term will be decided on at some point during solving, wether it appears in clauses or not. *) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index d69026eb..22b66a69 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -177,7 +177,7 @@ end = struct (fun c -> F.make true x y (Cell.make (c+1))) in Log.debugf 4 (fun k->k "(@[add-clause@ %a@])" pp_c_ c); - acts.push ~keep:true c (); + acts.acts_add_clause ~keep:true c (); )) (* check constraints *) @@ -198,7 +198,7 @@ end = struct assert (x1<>x2 || y1<>y2); let c = [F.make false x1 y1 c1; F.make false x2 y2 c2] in logs_conflict ("all-diff." ^ kind) c; - acts.raise_conflict c () + acts.acts_raise_conflict c () )) in all_diff "rows" Grid.rows; @@ -206,8 +206,8 @@ end = struct all_diff "squares" Grid.squares; () - let trail_ acts = - acts.iter_assumptions + let trail_ (acts:(Msat.void,_,_) Msat.acts) = + acts.acts_iter_assumptions |> Sequence.map (function | Assign _ -> assert false @@ -228,7 +228,7 @@ end = struct (* conflict: at most one value *) let c = [F.make false x y c; F.make false x y c'] in logs_conflict "at-most-one" c; - acts.raise_conflict c () + acts.acts_raise_conflict c () ) ) From 83c0d0e7f1b40a2c3f32c3d73e0bdee42aa14527 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 12:36:07 -0600 Subject: [PATCH 088/182] feat: add `Value.t` to the mcsat interface it can be useful to separate terms from pure values. --- src/core/Internal.ml | 41 +++++++++++++++++++------------------ src/core/Msat.ml | 14 ++++++------- src/core/Solver_intf.ml | 42 +++++++++++++++++++++++++------------- src/sudoku/sudoku_solve.ml | 2 +- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index fb60d0bc..9c18bdbd 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -15,11 +15,13 @@ module Make(Plugin : PLUGIN) = struct module Term = Plugin.Term module Formula = Plugin.Formula + module Value = Plugin.Value type term = Term.t type formula = Formula.t type theory = Plugin.t type lemma = Plugin.proof + type value = Value.t (* MCSAT literal *) type lit = { @@ -28,7 +30,7 @@ module Make(Plugin : PLUGIN) mutable l_level : int; mutable l_idx: int; mutable l_weight : float; - mutable assigned : term option; + mutable assigned : value option; } type var = { @@ -144,7 +146,7 @@ module Make(Plugin : PLUGIN) | None -> Format.fprintf fmt "" | Some t -> - Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level Term.pp t + Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level Value.pp t let pp out v = Term.pp out v.term let debug out v = @@ -1197,7 +1199,7 @@ module Make(Plugin : PLUGIN) ) (* MCsat semantic assignment *) - let enqueue_assign st l value lvl = + let enqueue_assign st (l:lit) (value:value) lvl = match l.assigned with | Some _ -> Log.debugf error @@ -1664,7 +1666,7 @@ module Make(Plugin : PLUGIN) let[@inline] acts_mk_term st t : unit = make_term st t - let[@inline] current_slice st : (_,_,_) Solver_intf.acts = { + let[@inline] current_slice st : _ Solver_intf.acts = { Solver_intf. acts_iter_assumptions=acts_iter st ~full:false st.th_head; acts_eval_lit= acts_eval_lit st; @@ -1676,7 +1678,7 @@ module Make(Plugin : PLUGIN) } (* full slice, for [if_sat] final check *) - let[@inline] full_slice st : (_,_,_) Solver_intf.acts = { + let[@inline] full_slice st : _ Solver_intf.acts = { Solver_intf. acts_iter_assumptions=acts_iter st ~full:true st.th_head; acts_eval_lit= acts_eval_lit st; @@ -1905,7 +1907,7 @@ module Make(Plugin : PLUGIN) let[@inline] unsat_conflict st = st.unsat_at_0 - let model (st:t) : (term * term) list = + let model (st:t) : (term * value) list = let opt = function Some a -> a | None -> assert false in Vec.fold (fun acc e -> match e with @@ -2000,7 +2002,7 @@ module Make(Plugin : PLUGIN) (* Result type *) type res = - | Sat of (term,atom) Solver_intf.sat_state + | Sat of (term,atom,value) Solver_intf.sat_state | Unsat of (atom,clause,Proof.t) Solver_intf.unsat_state let pp_all st lvl status = @@ -2014,7 +2016,7 @@ module Make(Plugin : PLUGIN) (Vec.pp ~sep:"" Clause.debug) (history st) ) - let mk_sat (st:t) : (_,_) Solver_intf.sat_state = + let mk_sat (st:t) : _ Solver_intf.sat_state = pp_all st 99 "SAT"; let t = trail st in let iter f f' = @@ -2094,15 +2096,18 @@ module Make(Plugin : PLUGIN) end [@@inline][@@specialise] + module Void_ = struct + type t = Solver_intf.void + let equal _ _ = assert false + let hash _ = assert false + let pp _ _ = assert false + end + module Make_cdcl_t(Plugin : Solver_intf.PLUGIN_CDCL_T) = Make(struct include Plugin - module Term = struct - type t = Solver_intf.void - let equal _ _ = assert false - let hash _ = assert false - let pp _ _ = assert false - end + module Term = Void_ + module Value = Void_ let eval _ _ = Solver_intf.Unknown let assign _ t = t let mcsat = false @@ -2120,12 +2125,8 @@ module Make_mcsat(Plugin : Solver_intf.PLUGIN_MCSAT) = module Make_pure_sat(F: Solver_intf.FORMULA) = Make(struct module Formula = F - module Term = struct - type t = Solver_intf.void - let equal _ _ = true - let hash _ = 1 - let pp out _ = Format.pp_print_string out "()" - end + module Term = Void_ + module Value = Void_ type t = unit type proof = Solver_intf.void let push_level () = () diff --git a/src/core/Msat.ml b/src/core/Msat.ml index e9d36153..4cf87e24 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -16,11 +16,11 @@ type void = (unit,bool) Solver_intf.gadt_eq type lbool = Solver_intf.lbool = L_true | L_false | L_undefined -type ('term, 'form) sat_state = ('term, 'form) Solver_intf.sat_state = { +type ('term, 'form, 'value) sat_state = ('term, 'form, 'value) Solver_intf.sat_state = { eval : 'form -> bool; eval_level : 'form -> bool * int; iter_trail : ('form -> unit) -> ('term -> unit) -> unit; - model : unit -> ('term * 'term) list; + model : unit -> ('term * 'value) list; } type ('atom,'clause, 'proof) unsat_state = ('atom,'clause, 'proof) Solver_intf.unsat_state = { @@ -33,16 +33,16 @@ type 'clause export = 'clause Solver_intf.export = { history : 'clause Vec.t; } -type ('term, 'formula) assumption = ('term, 'formula) Solver_intf.assumption = - | Lit of 'formula - | Assign of 'term * 'term (** The first term is assigned to the second *) +type ('term, 'formula, 'value) assumption = ('term, 'formula, 'value) Solver_intf.assumption = + | Lit of 'formula (** The given formula is asserted true by the solver *) + | Assign of 'term * 'value (** The term is assigned to the value *) type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.reason = | Eval of 'term list | Consequence of 'formula list * 'proof -type ('term, 'formula, 'proof) acts = ('term, 'formula, 'proof) Solver_intf.acts = { - acts_iter_assumptions: (('term,'formula) assumption -> unit) -> unit; +type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) Solver_intf.acts = { + acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; acts_eval_lit: 'formula -> lbool; acts_mk_lit: 'formula -> unit; acts_mk_term: 'term -> unit; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 816636e0..3d9bdb69 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -13,7 +13,7 @@ Copyright 2016 Simon Cruanes type 'a printer = Format.formatter -> 'a -> unit -type ('term, 'form) sat_state = { +type ('term, 'form, 'value) sat_state = { eval: 'form -> bool; (** Returns the valuation of a formula in the current state of the sat solver. @@ -27,7 +27,7 @@ type ('term, 'form) sat_state = { iter_trail : ('form -> unit) -> ('term -> unit) -> unit; (** Iter thorugh the formulas and terms in order of decision/propagation (starting from the first propagation, to the last propagation). *) - model: unit -> ('term * 'term) list; + model: unit -> ('term * 'value) list; (** Returns the model found if the formula is satisfiable. *) } (** The type of values returned when the solver reaches a SAT state. *) @@ -68,9 +68,9 @@ type 'term eval_res = - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) *) -type ('term, 'formula) assumption = +type ('term, 'formula, 'value) assumption = | Lit of 'formula (** The given formula is asserted true by the solver *) - | Assign of 'term * 'term (** The first term is assigned to the second *) + | Assign of 'term * 'value (** The term is assigned to the value *) (** Asusmptions made by the core SAT solver. *) type ('term, 'formula, 'proof) reason = @@ -86,8 +86,8 @@ type lbool = L_true | L_false | L_undefined (** Valuation of an atom *) (* TODO: find a way to use atoms instead of formulas here *) -type ('term, 'formula, 'proof) acts = { - acts_iter_assumptions: (('term,'formula) assumption -> unit) -> unit; +type ('term, 'formula, 'value, 'proof) acts = { + acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; (** Traverse the new assumptions on the boolean trail. *) acts_eval_lit: 'formula -> lbool; @@ -164,7 +164,22 @@ module type EXPR = sig val hash : t -> int (** Hashing function for terms. Should be such that two terms equal according - to {!val:Expr_intf.S.equal} have the same hash. *) + to {!equal} have the same hash. *) + + val pp : t printer + (** Printing function used among other for debugging. *) + end + + module Value : sig + type t + (** The type of semantic values (domain elements) *) + + val equal : t -> t -> bool + (** Equality over values. *) + + val hash : t -> int + (** Hashing function for values. Should be such that two terms equal according + to {!equal} have the same hash. *) val pp : t printer (** Printing function used among other for debugging. *) @@ -188,12 +203,12 @@ module type PLUGIN_CDCL_T = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val partial_check : t -> (void, Formula.t, proof) acts -> unit + val partial_check : t -> (void, Formula.t, void, proof) acts -> unit (** Assume the formulas in the slice, possibly using the [slice] to push new formulas to be propagated or to raising a conflict or to add new lemmas. *) - val final_check : t -> (void, Formula.t, proof) acts -> unit + val final_check : t -> (void, Formula.t, void, proof) acts -> unit (** Called at the end of the search in case a model has been found. If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; @@ -207,25 +222,24 @@ module type PLUGIN_MCSAT = sig include EXPR - val push_level : t -> unit (** Create a new backtrack level *) val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val partial_check : t -> (Term.t, Formula.t, proof) acts -> unit + val partial_check : t -> (Term.t, Formula.t, Value.t, proof) acts -> unit (** Assume the formulas in the slice, possibly using the [slice] to push new formulas to be propagated or to raising a conflict or to add new lemmas. *) - val final_check : t -> (Term.t, Formula.t, proof) acts -> unit + val final_check : t -> (Term.t, Formula.t, Value.t, proof) acts -> unit (** Called at the end of the search in case a model has been found. If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) - val assign : t -> Term.t -> Term.t + val assign : t -> Term.t -> Value.t (** Returns an assignment value for the given term. *) val iter_assignable : t -> (Term.t -> unit) -> Formula.t -> unit @@ -407,7 +421,7 @@ module type S = sig (** Result type for the solver *) type res = - | Sat of (term,atom) sat_state (** Returned when the solver reaches SAT, with a model *) + | Sat of (term,atom,Value.t) sat_state (** Returned when the solver reaches SAT, with a model *) | Unsat of (atom,clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 22b66a69..8b05c6f8 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -206,7 +206,7 @@ end = struct all_diff "squares" Grid.squares; () - let trail_ (acts:(Msat.void,_,_) Msat.acts) = + let trail_ (acts:_ Msat.acts) = acts.acts_iter_assumptions |> Sequence.map (function From fdc042aee34e41ab420a96bdc36234799757979a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 12:51:58 -0600 Subject: [PATCH 089/182] fix: no need to add trivial clauses at all --- src/core/Internal.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 9c18bdbd..0e27a550 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1500,7 +1500,6 @@ module Make(Plugin : PLUGIN) ) ) with Trivial -> - Vec.push vec init; Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) let[@inline never] flush_clauses_ st = From 0a3a3b576a2d5ff2b8d081373114aaa39bc498e6 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 12:57:33 -0600 Subject: [PATCH 090/182] refactor: remove dead code, some basic simplifications --- src/core/Internal.ml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 0e27a550..34665ec6 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1248,10 +1248,11 @@ module Make(Plugin : PLUGIN) else match Plugin.eval st.th a.lit with | Solver_intf.Unknown -> None | Solver_intf.Valued (b, l) -> - if l = [] then + if l = [] then ( raise (Invalid_argument ( Format.asprintf "msat:core/internal.ml: %s" "semantic propagation at level 0 are currently forbidden")); + ); let atom = if b then a else a.neg in enqueue_semantic st atom l; Some b @@ -1962,11 +1963,7 @@ module Make(Plugin : PLUGIN) (* Check satisfiability *) let check_clause c = - let tmp = Array.map (fun a -> - if a.is_true then true - else if a.neg.is_true then false - else raise UndecidedLit) c.atoms in - let res = Array.exists (fun x -> x) tmp in + let res = Array.exists (fun a -> a.is_true) c.atoms in if not res then ( Log.debugf debug (fun k -> k "Clause not satisfied: @[%a@]" Clause.debug c); @@ -1974,16 +1971,7 @@ module Make(Plugin : PLUGIN) ) else true - let check_vec v = - Vec.for_all check_clause v - - let check_stack s = - try - Stack.iter (fun c -> if not (check_clause c) then raise Exit) s; - true - with Exit -> - false - + let check_vec v = Vec.for_all check_clause v let check st : bool = Vec.is_empty st.clauses_to_add && check_vec st.clauses_hyps && From 4fbaae7d2dfdef6e5cef877cc7366ccde783f166 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 12:57:50 -0600 Subject: [PATCH 091/182] refactor(log): use a S-expr-style format for log messages --- src/core/Internal.ml | 113 ++++++++++++++++++++-------------------- src/core/Solver_intf.ml | 8 +-- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 34665ec6..656b3a0c 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -403,7 +403,6 @@ module Make(Plugin : PLUGIN) end module Proof = struct - exception Insuficient_hyps exception Resolution_error of string type atom = Atom.t @@ -413,6 +412,8 @@ module Make(Plugin : PLUGIN) let merge = List.merge Atom.compare + let error_res_f msg = Format.kasprintf (fun s -> raise (Resolution_error s)) msg + let _c = ref 0 let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c) @@ -455,12 +456,12 @@ module Make(Plugin : PLUGIN) let to_list c = let cl = list c in - let doublons, l = analyze cl in + let dups, l = analyze cl in let conflicts, _ = resolve l in - if doublons <> [] then - Log.debug 3 "Input clause has redundancies"; + if dups <> [] then + Log.debug 3 "(@[sat.input-clause@ :has-duplicates@])"; if conflicts <> [] then - Log.debug 3 "Input clause is a tautology"; + Log.debug 3 "(@[sat.input-clause@ :is-tautology@])"; cl (* Comparison of clauses *) @@ -489,9 +490,9 @@ module Make(Plugin : PLUGIN) assert (a.var.v_level >= 0); match (a.var.reason) with | Some (Bcp c) -> - Log.debugf 5 (fun k->k "Analysing: @[%a@ %a@]" Atom.debug a Clause.debug c); + Log.debugf 5 (fun k->k "(@[proof.analyze.clause@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c); if Array.length c.atoms = 1 then ( - Log.debugf 5 (fun k -> k "Old reason: @[%a@]" Atom.debug a); + Log.debugf 5 (fun k -> k "(@[proof.analyze.old-reason@ %a@])" Atom.debug a); c ) else ( assert (a.neg.is_true); @@ -499,20 +500,20 @@ module Make(Plugin : PLUGIN) let c' = Clause.make [a.neg] r in a.var.reason <- Some (Bcp c'); Log.debugf 5 - (fun k -> k "New reason: @[%a@ %a@]" Atom.debug a Clause.debug c'); + (fun k -> k "(@[proof.analyze.new-reason@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c'); c' ) | _ -> - Log.debugf 0 (fun k -> k "Error while proving atom %a" Atom.debug a); - raise (Resolution_error "Cannot prove atom") + error_res_f "cannot prove atom %a" Atom.debug a let prove_unsat conflict = - if Array.length conflict.atoms = 0 then conflict - else ( - Log.debugf 1 (fun k -> k "Proving unsat from: @[%a@]" Clause.debug conflict); + if Array.length conflict.atoms = 0 then ( + conflict + ) else ( + Log.debugf 1 (fun k -> k "(@[sat.prove-unsat@ :from %a@])" Clause.debug conflict); let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.atoms in let res = Clause.make [] (History (conflict :: l)) in - Log.debugf 1 (fun k -> k "Proof found: @[%a@]" Clause.debug res); + Log.debugf 1 (fun k -> k "(@[sat.proof-found@ %a@])" Clause.debug res); res ) @@ -535,10 +536,12 @@ module Make(Plugin : PLUGIN) | Duplicate of t * atom list | Resolution of t * t * atom + let[@inline] conclusion (p:t) : clause = p + let rec chain_res (c, cl) = function | d :: r -> Log.debugf 5 - (fun k -> k " Resolving clauses : @[%a@\n%a@]" Clause.debug c Clause.debug d); + (fun k -> k "(@[sat.analyze.resolving@ :c1 %a@ :c2 %a@])" Clause.debug c Clause.debug d); let dl = to_list d in begin match resolve (merge cl dl) with | [ a ], l -> @@ -549,18 +552,14 @@ module Make(Plugin : PLUGIN) chain_res (new_clause, l) r end | _ -> - Log.debugf 5 - (fun k -> k "While resolving clauses:@[%a@\n%a@]" - Clause.debug c Clause.debug d); - raise (Resolution_error "Clause mismatch") + error_res_f "@[<2>clause mismatch while resolving@ %a@ and %a@]" + Clause.debug c Clause.debug d end | _ -> - raise (Resolution_error "Bad history") - - let[@inline] conclusion (p:t) : clause = p + error_res_f "bad history" let expand conclusion = - Log.debugf 5 (fun k -> k "Expanding : @[%a@]" Clause.debug conclusion); + Log.debugf 5 (fun k -> k "(@[sat.proof.expand@ @[%a@]@])" Clause.debug conclusion); match conclusion.cpremise with | Lemma l -> {conclusion; step = Lemma l; } @@ -569,8 +568,7 @@ module Make(Plugin : PLUGIN) | Hyp -> { conclusion; step = Hypothesis; } | History [] -> - Log.debugf 0 (fun k -> k "Empty history for clause: %a" Clause.debug conclusion); - raise (Resolution_error "Empty history") + error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion | History [c] -> let duplicates, res = analyze (list c) in assert (cmp_cl res (list conclusion) = 0); @@ -956,7 +954,7 @@ module Make(Plugin : PLUGIN) if i >= Array.length arr then [] else Array.to_list (Array.sub arr i (Array.length arr - i)) - (* Eliminates atom doublons in clauses *) + (* Eliminates atom duplicates in clauses *) let eliminate_duplicates clause : clause = let trivial = ref false in let duplicates = ref [] in @@ -1050,7 +1048,7 @@ module Make(Plugin : PLUGIN) *) let attach_clause c = assert (not @@ Clause.attached c); - Log.debugf debug (fun k -> k "Attaching %a" Clause.debug c); + Log.debugf debug (fun k -> k "(@[sat.attach-clause@ %a@])" Clause.debug c); Vec.push c.atoms.(0).neg.watched c; Vec.push c.atoms.(1).neg.watched c; Clause.set_attached c true; @@ -1064,9 +1062,9 @@ module Make(Plugin : PLUGIN) assert (lvl >= 0); (* Nothing to do if we try to backtrack to a non-existent level. *) if decision_level st <= lvl then ( - Log.debugf debug (fun k -> k "Already at level <= %d" lvl) + Log.debugf debug (fun k -> k "(@[sat.cancel-until.nop@ :already-at-level <= %d@])" lvl) ) else ( - Log.debugf info (fun k -> k "Backtracking to lvl %d" lvl); + Log.debugf info (fun k -> k "(@[sat.cancel-until %d@])" lvl); (* We set the head of the solver and theory queue to what it was. *) let head = ref (Vec.get st.elt_levels lvl) in st.elt_head <- !head; @@ -1118,15 +1116,15 @@ module Make(Plugin : PLUGIN) let pp_unsat_cause out = function | US_local {first=_; core} -> - Format.fprintf out "false assumptions (@[core %a@])" + Format.fprintf out "(@[unsat-cause@ :false-assumptions %a@])" (Format.pp_print_list Atom.pp) core | US_false c -> - Format.fprintf out "false %a" Clause.debug c + Format.fprintf out "(@[unsat-cause@ :false %a@])" Clause.debug c (* Unsatisfiability is signaled through an exception, since it can happen in multiple places (adding new clauses, or solving for instance). *) let report_unsat st (us:unsat_cause) : _ = - Log.debugf info (fun k -> k "@[Unsat conflict: %a@]" pp_unsat_cause us); + Log.debugf info (fun k -> k "(@[sat.unsat-conflict@ %a@])" pp_unsat_cause us); begin match us with | US_false c -> st.unsat_at_0 <- Some c; | _ -> () @@ -1155,15 +1153,14 @@ module Make(Plugin : PLUGIN) rebuild the whole resolution tree when we want to prove [a]. *) let c' = Clause.make l (History (cl :: history)) in Log.debugf debug - (fun k -> k "Simplified reason: @[%a@,%a@]" Clause.debug cl Clause.debug c'); + (fun k -> k "(@[sat.simplified-reason@ %a@ %a@])" Clause.debug cl Clause.debug c'); Bcp c' ) | _ -> Log.debugf error (fun k -> - k "@[Failed at reason simplification:@,%a@,%a@]" - (Vec.pp ~sep:"" Atom.debug) - (Vec.of_list l) + k "(@[sat.simplify-reason.failed@ :at %a@ %a@]" + (Vec.pp ~sep:"" Atom.debug) (Vec.of_list l) Clause.debug cl); assert false end @@ -1173,7 +1170,8 @@ module Make(Plugin : PLUGIN) Wrapper function for adding a new propagated formula. *) let enqueue_bool st a ~level:lvl reason : unit = if a.neg.is_true then ( - Log.debugf error (fun k->k "Trying to enqueue a false literal: %a" Atom.debug a); + Log.debugf error + (fun k->k "(@[sat.error.trying to enqueue a false literal %a@])" Atom.debug a); assert false ); assert (not a.is_true && a.var.v_level < 0 && @@ -1187,7 +1185,7 @@ module Make(Plugin : PLUGIN) a.var.reason <- Some reason; Vec.push st.trail (Trail_elt.of_atom a); Log.debugf debug - (fun k->k "Enqueue (%d): %a" (Vec.size st.trail) Atom.debug a); + (fun k->k "(@[sat.enqueue[%d]@ %a@])" (Vec.size st.trail) Atom.debug a); () let enqueue_semantic st a terms = @@ -1203,7 +1201,7 @@ module Make(Plugin : PLUGIN) match l.assigned with | Some _ -> Log.debugf error - (fun k -> k "Trying to assign an already assigned literal: %a" Lit.debug l); + (fun k -> k "(@[sat.error: Trying to assign an already assigned literal:@ %a@])" Lit.debug l); assert false | None -> assert (l.l_level < 0); @@ -1211,7 +1209,7 @@ module Make(Plugin : PLUGIN) l.l_level <- lvl; Vec.push st.trail (Trail_elt.of_lit l); Log.debugf debug - (fun k -> k "Enqueue (%d): %a" (Vec.size st.trail) Lit.debug l); + (fun k -> k "(@[sat.enqueue-semantic[%d]@ %a@])" (Vec.size st.trail) Lit.debug l); () (* swap elements of array *) @@ -1312,13 +1310,13 @@ module Make(Plugin : PLUGIN) Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms in Log.debugf debug - (fun k -> k "Analyzing conflict (%d): %a" conflict_level Clause.debug c_clause); + (fun k -> k "(@[sat.analyze-conflict@ :c-level %d@ :clause %a@])" conflict_level Clause.debug c_clause); while !cond do begin match !c with | None -> - Log.debug debug " skipping resolution for semantic propagation" + Log.debug debug "(@[sat.analyze-conflict: skipping resolution for semantic propagation@])" | Some clause -> - Log.debugf debug (fun k->k" Resolving clause: %a" Clause.debug clause); + Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); begin match clause.cpremise with | History _ -> clause_bump_activity st clause | Hyp | Local | Lemma _ -> () @@ -1353,7 +1351,8 @@ module Make(Plugin : PLUGIN) (* look for the next node to expand *) while let a = Vec.get st.trail !tr_ind in - Log.debugf debug (fun k -> k " looking at: %a" Trail_elt.debug a); + Log.debugf debug + (fun k -> k "(@[sat.analyze-conflict.at-trail-elt@ %a@])" Trail_elt.debug a); match a with | Atom q -> (not (Var.marked q.var)) || @@ -1431,7 +1430,7 @@ module Make(Plugin : PLUGIN) - report unsat if conflict at level 0 *) let add_boolean_conflict st (confl:clause): unit = - Log.debugf info (fun k -> k "Boolean conflict: %a" Clause.debug confl); + Log.debugf info (fun k -> k "(@[sat.add-bool-conflict@ %a@])" Clause.debug confl); st.next_decision <- None; assert (decision_level st >= 0); if decision_level st = 0 || @@ -1450,14 +1449,14 @@ module Make(Plugin : PLUGIN) (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) let add_clause_ st (init:clause) : unit = - Log.debugf debug (fun k -> k "Adding clause: @[%a@]" Clause.debug init); + Log.debugf debug (fun k -> k "(@[sat.add-clause@ @[%a@]@])" Clause.debug init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) Array.iter (fun x -> insert_var_order st (Elt.of_var x.var)) init.atoms; let vec = clause_vector st init in try let c = eliminate_duplicates init in - Log.debugf debug (fun k -> k "Doublons eliminated: %a" Clause.debug c); + Log.debugf debug (fun k -> k "(@[sat.dups-removed@ %a@])" Clause.debug c); let atoms, history = partition c.atoms in let clause = if history = [] @@ -1468,7 +1467,7 @@ module Make(Plugin : PLUGIN) ) else Clause.make atoms (History (c :: history)) in - Log.debugf info (fun k->k "New clause: @[%a@]" Clause.debug clause); + Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); match atoms with | [] -> report_unsat st @@ US_false clause @@ -1480,7 +1479,8 @@ module Make(Plugin : PLUGIN) ) else if a.is_true then ( () (* atom is already true, nothing to do *) ) else ( - Log.debugf debug (fun k->k "Unit clause, propagating: %a" Atom.debug a); + Log.debugf debug + (fun k->k "(@[sat.add-clause.unit-clause@ :propagating %a@])" Atom.debug a); Vec.push vec clause; enqueue_bool st a ~level:0 (Bcp clause) ) @@ -1501,7 +1501,8 @@ module Make(Plugin : PLUGIN) ) ) with Trivial -> - Log.debugf info (fun k->k "Trivial clause ignored : @[%a@]" Clause.debug init) + Log.debugf info + (fun k->k "(@[sat.add-clause@ :ignore-trivial @[%a@]@])" Clause.debug init) let[@inline never] flush_clauses_ st = while not @@ Vec.is_empty st.clauses_to_add do @@ -1614,7 +1615,7 @@ module Make(Plugin : PLUGIN) let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in if not keep then Clause.set_learnt c true; - Log.debugf info (fun k->k "Pushing clause %a" Clause.debug c); + Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c let acts_raise st (l:formula list) proof : 'a = @@ -1878,7 +1879,7 @@ module Make(Plugin : PLUGIN) assert (st.elt_head = st.th_head); if Vec.size st.trail = nb_elt st.st then raise_notrace E_sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( - Log.debug info "Restarting..."; + Log.debug info "(@[sat.restarting@])"; cancel_until st 0; raise_notrace Restart ); @@ -1944,7 +1945,7 @@ module Make(Plugin : PLUGIN) | exception Th_conflict c -> check_is_conflict_ c; Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; - Log.debugf info (fun k -> k "Theory conflict clause: %a" Clause.debug c); + Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c; flush_clauses st; end; @@ -1957,7 +1958,7 @@ module Make(Plugin : PLUGIN) (fun l -> let atoms = List.rev_map (mk_atom st) l in let c = Clause.make atoms Hyp in - Log.debugf debug (fun k -> k "Assuming clause: @[%a@]" Clause.debug c); + Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); Vec.push st.clauses_to_add c) cnf @@ -1966,7 +1967,7 @@ module Make(Plugin : PLUGIN) let res = Array.exists (fun a -> a.is_true) c.atoms in if not res then ( Log.debugf debug - (fun k -> k "Clause not satisfied: @[%a@]" Clause.debug c); + (fun k -> k "(@[sat.check-clause@ :not-satisfied @[%a@]@])" Clause.debug c); false ) else true @@ -1995,7 +1996,7 @@ module Make(Plugin : PLUGIN) let pp_all st lvl status = Log.debugf lvl (fun k -> k - "@[%s - Full resume:@,@[Trail:@\n%a@]@,\ + "(@[sat.full-state :res %s - Full summary:@,@[Trail:@\n%a@]@,\ @[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." status (Vec.pp ~sep:"" Trail_elt.debug) (trail st) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 3d9bdb69..d615d8c8 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -255,8 +255,8 @@ module type PROOF = sig (** {3 Type declarations} *) - exception Insuficient_hyps - (** Raised when a complete resolution derivation cannot be found using the current hypotheses. *) + exception Resolution_error of string + (** Raised when resolution failed. *) type formula type atom @@ -293,11 +293,11 @@ module type PROOF = sig val prove : clause -> t (** Given a clause, return a proof of that clause. - @raise Insuficient_hyps if it does not succeed. *) + @raise Resolution_error if it does not succeed. *) val prove_unsat : clause -> t (** Given a conflict clause [c], returns a proof of the empty clause. - @raise Insuficient_hyps if it does not succeed. *) + @raise Resolution_error if it does not succeed. *) val prove_atom : atom -> t option (** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *) From a58c940c6d322031813647a3b659ad7353cac0fb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 26 Jan 2019 13:07:46 -0600 Subject: [PATCH 092/182] feat: ask less from values in mcsat --- src/core/Solver_intf.ml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index d615d8c8..76724b76 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -174,13 +174,6 @@ module type EXPR = sig type t (** The type of semantic values (domain elements) *) - val equal : t -> t -> bool - (** Equality over values. *) - - val hash : t -> int - (** Hashing function for values. Should be such that two terms equal according - to {!equal} have the same hash. *) - val pp : t printer (** Printing function used among other for debugging. *) end From 1736b4a99ecaab875281faef085ed3689a3cb278 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 28 Jan 2019 19:00:10 -0600 Subject: [PATCH 093/182] api: sat_state takes formulas, not atoms --- src/core/Internal.ml | 16 ++++++++-------- src/core/Solver_intf.ml | 2 +- src/main/main.ml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 656b3a0c..951bc6e7 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1990,7 +1990,7 @@ module Make(Plugin : PLUGIN) (* Result type *) type res = - | Sat of (term,atom,value) Solver_intf.sat_state + | Sat of (term,Formula.t,value) Solver_intf.sat_state | Unsat of (atom,clause,Proof.t) Solver_intf.unsat_state let pp_all st lvl status = @@ -2004,19 +2004,19 @@ module Make(Plugin : PLUGIN) (Vec.pp ~sep:"" Clause.debug) (history st) ) - let mk_sat (st:t) : _ Solver_intf.sat_state = + let mk_sat (st:t) : (Term.t, Formula.t, _) Solver_intf.sat_state = pp_all st 99 "SAT"; let t = trail st in - let iter f f' = + let iter_trail f f' = Vec.iter (function - | Atom a -> f a + | Atom a -> f (Atom.formula a) | Lit l -> f' l.term) t in + let[@inline] eval f = eval st (mk_atom st f) in + let[@inline] eval_level f = eval_level st (mk_atom st f) in { Solver_intf. - eval = eval st; - eval_level = eval_level st; - iter_trail = iter; + eval; eval_level; iter_trail; model = (fun () -> model st); } @@ -2061,7 +2061,7 @@ module Make(Plugin : PLUGIN) let c = Clause.make c Hyp in add_clause_ st c - let solve (st:t) ?(assumptions=[]) () = + let solve (st:t) ?(assumptions=[]) () : res = cancel_until st 0; Vec.clear st.assumptions; List.iter (Vec.push st.assumptions) assumptions; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 76724b76..dbc3bd4d 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -414,7 +414,7 @@ module type S = sig (** Result type for the solver *) type res = - | Sat of (term,atom,Value.t) sat_state (** Returned when the solver reaches SAT, with a model *) + | Sat of (term,formula,Value.t) sat_state (** Returned when the solver reaches SAT, with a model *) | Unsat of (atom,clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit diff --git a/src/main/main.ml b/src/main/main.ml index e822dd1d..b4f86061 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -29,7 +29,7 @@ module Process = struct let check_clause c = let l = List.map (function a -> Log.debugf 99 - (fun k -> k "Checking value of %a" S.Atom.pp a); + (fun k -> k "Checking value of %a" S.Formula.pp a); sat.Msat.eval a) c in List.exists (fun x -> x) l in From 79bd88b999708426206c821026b5ab7af2e62927 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 28 Jan 2019 19:02:32 -0600 Subject: [PATCH 094/182] api: remove spurious `()` for calls to `solve` --- src/core/Internal.ml | 2 +- src/core/Solver_intf.ml | 2 +- src/main/main.ml | 2 +- src/sudoku/sudoku_solve.ml | 2 +- tests/icnf-solve/icnf_solve.ml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 951bc6e7..004ed7f2 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -2061,7 +2061,7 @@ module Make(Plugin : PLUGIN) let c = Clause.make c Hyp in add_clause_ st c - let solve (st:t) ?(assumptions=[]) () : res = + let solve ?(assumptions=[]) (st:t) : res = cancel_until st 0; Vec.clear st.assumptions; List.iter (Vec.push st.assumptions) assumptions; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index dbc3bd4d..f21f958c 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -433,7 +433,7 @@ module type S = sig val add_clause_a : t -> atom array -> unit (** Lower level addition of clauses *) - val solve : t -> ?assumptions:atom list -> unit -> res + val solve : ?assumptions:atom list -> t -> res (** Try and solves the current set of clauses. @param assumptions additional atomic assumptions to be temporarily added. The assumptions are just used for this call to [solve], they are diff --git a/src/main/main.ml b/src/main/main.ml index b4f86061..eb13666c 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -37,7 +37,7 @@ module Process = struct List.for_all (fun x -> x) l let prove ~assumptions () = - let res = S.solve st ~assumptions () in + let res = S.solve ~assumptions st in let t = Sys.time () in begin match res with | S.Sat state -> diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 8b05c6f8..0dfb04fc 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -263,7 +263,7 @@ end = struct Log.debugf 2 (fun k->k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list S.Atom.pp) assumptions); let r = - match S.solve self.solver ~assumptions () with + match S.solve self.solver ~assumptions with | S.Sat _ -> Some (Theory.grid (S.theory self.solver)) | S.Unsat _ -> None in diff --git a/tests/icnf-solve/icnf_solve.ml b/tests/icnf-solve/icnf_solve.ml index 4115237b..d6b2467b 100644 --- a/tests/icnf-solve/icnf_solve.ml +++ b/tests/icnf-solve/icnf_solve.ml @@ -68,7 +68,7 @@ module Solver = struct let to_int a : int = F.to_int @@ S.Atom.formula a let solve s ass = let ass = Array.to_list ass in - match S.solve ~assumptions:ass s () with + match S.solve ~assumptions:ass s with | S.Sat _ -> Ok () | S.Unsat { unsat_assumptions; _ } -> let core = unsat_assumptions() in From f62fa88b0fbbdf6f0b451907b0400b3d147359ee Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 28 Jan 2019 19:21:14 -0600 Subject: [PATCH 095/182] api: annotate input clauses with theory proofs, too this replaces the old "tag" system --- src/backend/Coq.ml | 2 +- src/backend/Dot.ml | 2 +- src/core/Internal.ml | 42 +++++++++++++++++----------------- src/core/Solver.mli | 6 ++--- src/core/Solver_intf.ml | 15 ++++++++---- src/main/main.ml | 2 +- src/sat/Msat_sat.ml | 5 +++- src/sat/Msat_sat.mli | 5 +++- tests/icnf-solve/icnf_solve.ml | 2 +- tests/test_api.ml | 4 ++-- 10 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index 865c1e72..e4e9f56b 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -124,7 +124,7 @@ module Make(S : Msat.S)(A : Arg with type hyp := S.clause let prove_node t fmt node = let clause = node.P.conclusion in match node.P.step with - | P.Hypothesis -> + | P.Hypothesis _ -> A.prove_hyp fmt (name clause) clause | P.Assumption -> A.prove_assumption fmt (name clause) clause diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index b00178a7..ea1badcd 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -110,7 +110,7 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom let print_contents fmt n = match P.(n.step) with (* Leafs of the proof tree *) - | P.Hypothesis -> + | P.Hypothesis _ -> let rule, color, l = A.hyp_info P.(n.conclusion) in let color = match color with None -> "LIGHTBLUE" | Some c -> c in print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 004ed7f2..ac7fbfc2 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -70,7 +70,7 @@ module Make(Plugin : PLUGIN) (* TODO: remove, replace with user-provided proof trackng device? for pure SAT, [reason] is sufficient *) and premise = - | Hyp + | Hyp of lemma | Local | Lemma of lemma | History of clause list @@ -113,7 +113,7 @@ module Make(Plugin : PLUGIN) let iter_elt st f = Vec.iter f st.vars let name_of_clause c = match c.cpremise with - | Hyp -> "H" ^ string_of_int c.name + | Hyp _ -> "H" ^ string_of_int c.name | Lemma _ -> "T" ^ string_of_int c.name | Local -> "L" ^ string_of_int c.name | History _ -> "C" ^ string_of_int c.name @@ -391,7 +391,7 @@ module Make(Plugin : PLUGIN) Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms let debug_premise out = function - | Hyp -> Format.fprintf out "hyp" + | Hyp _ -> Format.fprintf out "hyp" | Lemma _ -> Format.fprintf out "th_lemma" | Local -> Format.fprintf out "local" | History v -> @@ -530,7 +530,7 @@ module Make(Plugin : PLUGIN) step : step; } and step = - | Hypothesis + | Hypothesis of lemma | Assumption | Lemma of lemma | Duplicate of t * atom list @@ -565,8 +565,8 @@ module Make(Plugin : PLUGIN) {conclusion; step = Lemma l; } | Local -> { conclusion; step = Assumption; } - | Hyp -> - { conclusion; step = Hypothesis; } + | Hyp l -> + { conclusion; step = Hypothesis l; } | History [] -> error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion | History [c] -> @@ -585,21 +585,21 @@ module Make(Plugin : PLUGIN) (* Proof nodes manipulation *) let is_leaf = function - | Hypothesis + | Hypothesis _ | Assumption | Lemma _ -> true | Duplicate _ | Resolution _ -> false let parents = function - | Hypothesis + | Hypothesis _ | Assumption | Lemma _ -> [] | Duplicate (p, _) -> [p] | Resolution (p, p', _) -> [p; p'] let expl = function - | Hypothesis -> "hypothesis" + | Hypothesis _ -> "hypothesis" | Assumption -> "assumption" | Lemma _ -> "lemma" | Duplicate _ -> "duplicate" @@ -615,7 +615,7 @@ module Make(Plugin : PLUGIN) if not @@ Clause.visited c then ( Clause.set_visited c true; match c.cpremise with - | Hyp | Lemma _ | Local -> aux (c :: res) acc r + | Hyp _ | Lemma _ | Local -> aux (c :: res) acc r | History h -> let l = List.fold_left (fun acc c -> if not @@ Clause.visited c then c :: acc else acc) r h in @@ -653,7 +653,7 @@ module Make(Plugin : PLUGIN) | Resolution (p1, p2, _) -> Stack.push (Enter p2) s; Stack.push (Enter p1) s - | Hypothesis | Assumption | Lemma _ -> () + | Hypothesis _ | Assumption | Lemma _ -> () end end; fold_aux s h f acc @@ -1319,7 +1319,7 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); begin match clause.cpremise with | History _ -> clause_bump_activity st clause - | Hyp | Local | Lemma _ -> () + | Hyp _ | Local | Lemma _ -> () end; history := clause :: !history; (* visit the current predecessors *) @@ -1953,11 +1953,11 @@ module Make(Plugin : PLUGIN) done with E_sat -> () - let assume st cnf = + let assume st cnf lemma = List.iter (fun l -> let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make atoms Hyp in + let c = Clause.make atoms (Hyp lemma) in Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); Vec.push st.clauses_to_add c) cnf @@ -2053,12 +2053,12 @@ module Make(Plugin : PLUGIN) in { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } - let[@inline] add_clause_a st c : unit = - let c = Clause.make_a c Hyp in + let[@inline] add_clause_a st c lemma : unit = + let c = Clause.make_a c (Hyp lemma) in add_clause_ st c - let[@inline] add_clause st c : unit = - let c = Clause.make c Hyp in + let[@inline] add_clause st c lemma : unit = + let c = Clause.make c (Hyp lemma) in add_clause_ st c let solve ?(assumptions=[]) (st:t) : res = @@ -2110,13 +2110,13 @@ module Make_mcsat(Plugin : Solver_intf.PLUGIN_MCSAT) = end) [@@inline][@@specialise] -module Make_pure_sat(F: Solver_intf.FORMULA) = +module Make_pure_sat(Plugin : Solver_intf.PLUGIN_SAT) = Make(struct - module Formula = F + module Formula = Plugin.Formula module Term = Void_ module Value = Void_ type t = unit - type proof = Solver_intf.void + type proof = Plugin.proof let push_level () = () let pop_levels _ _ = () let partial_check () _ = () diff --git a/src/core/Solver.mli b/src/core/Solver.mli index d50fc2e4..b256a4a8 100644 --- a/src/core/Solver.mli +++ b/src/core/Solver.mli @@ -25,10 +25,10 @@ module Make_mcsat(Th : Solver_intf.PLUGIN_MCSAT) and type lemma = Th.proof and type theory = Th.t -module Make_pure_sat(F: Solver_intf.FORMULA) +module Make_pure_sat(Th: Solver_intf.PLUGIN_SAT) : S with type Term.t = Solver_intf.void - and module Formula = F - and type lemma = Solver_intf.void + and module Formula = Th.Formula + and type lemma = Th.proof and type theory = unit diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index f21f958c..90e06822 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -240,7 +240,13 @@ module type PLUGIN_MCSAT = sig val eval : t -> Formula.t -> Term.t eval_res (** Returns the evaluation of the Formula.t in the current assignment *) +end +(** Signature for pure SAT solvers *) +module type PLUGIN_SAT = sig + module Formula : FORMULA + + type proof end module type PROOF = sig @@ -269,7 +275,7 @@ module type PROOF = sig (** The type of reasoning steps allowed in a proof. *) and step = - | Hypothesis + | Hypothesis of lemma (** The conclusion is a user-provided hypothesis *) | Assumption (** The conclusion has been locally assumed by the user *) @@ -361,6 +367,7 @@ module type S = sig type theory type lemma + (** A theory lemma or an input axiom *) type solver @@ -423,14 +430,14 @@ module type S = sig (** {2 Base operations} *) - val assume : t -> formula list list -> unit + val assume : t -> formula list list -> lemma -> unit (** Add the list of clauses to the current set of assumptions. Modifies the sat solver state in place. *) - val add_clause : t -> atom list -> unit + val add_clause : t -> atom list -> lemma -> unit (** Lower level addition of clauses *) - val add_clause_a : t -> atom array -> unit + val add_clause_a : t -> atom array -> lemma -> unit (** Lower level addition of clauses *) val solve : ?assumptions:atom list -> t -> res diff --git a/src/main/main.ml b/src/main/main.ml index eb13666c..79745c5e 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -62,7 +62,7 @@ module Process = struct let conv_c c = List.rev_map S.Int_lit.make c let add_clauses cs = - S.assume st @@ CCList.map conv_c cs + S.assume st (CCList.map conv_c cs) () end let parse_file f = diff --git a/src/sat/Msat_sat.ml b/src/sat/Msat_sat.ml index bd4b3dd5..91cc70c6 100644 --- a/src/sat/Msat_sat.ml +++ b/src/sat/Msat_sat.ml @@ -4,5 +4,8 @@ Copyright 2016 Guillaume Bury *) module Int_lit = Int_lit -include Msat.Make_pure_sat(Int_lit) +include Msat.Make_pure_sat(struct + module Formula = Int_lit + type proof = unit + end) diff --git a/src/sat/Msat_sat.mli b/src/sat/Msat_sat.mli index e2897626..0517e913 100644 --- a/src/sat/Msat_sat.mli +++ b/src/sat/Msat_sat.mli @@ -11,6 +11,9 @@ Copyright 2016 Guillaume Bury module Int_lit = Int_lit -include Msat.S with type Formula.t = Int_lit.t and type theory = unit +include Msat.S + with type Formula.t = Int_lit.t + and type theory = unit + and type lemma = unit (** A functor that can generate as many solvers as needed. *) diff --git a/tests/icnf-solve/icnf_solve.ml b/tests/icnf-solve/icnf_solve.ml index d6b2467b..567b58ad 100644 --- a/tests/icnf-solve/icnf_solve.ml +++ b/tests/icnf-solve/icnf_solve.ml @@ -64,7 +64,7 @@ module Solver = struct let make () = S.create() let mklit s i = S.make_atom s (let v = F.make (abs i) in if i>0 then v else F.neg v) - let add_clause s c = S.add_clause_a s c; true + let add_clause s c = S.add_clause_a s c (); true let to_int a : int = F.to_int @@ S.Atom.formula a let solve s ass = let ass = Array.to_list ass in diff --git a/tests/test_api.ml b/tests/test_api.ml index 1b3da279..d4bb142b 100644 --- a/tests/test_api.ml +++ b/tests/test_api.ml @@ -72,10 +72,10 @@ module Test = struct List.iter (function | A_assume cs -> - S.assume st cs + S.assume st cs () | A_solve (assumptions, expect) -> let assumptions = List.map (S.make_atom st) assumptions in - match S.solve st ~assumptions (), expect with + match S.solve ~assumptions st, expect with | S.Sat _, `Expect_sat -> () | S.Unsat us, `Expect_unsat -> let p = us.Msat.get_proof () in From 65a8a6509598e6e32aba151c68f57b9996e1240d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 30 Jan 2019 15:44:56 -0600 Subject: [PATCH 096/182] chore: be robust to deprecations --- src/core/dune | 2 +- src/main/dune | 2 +- src/sat/dune | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/dune b/src/core/dune index e5c561a3..6ec93b3a 100644 --- a/src/core/dune +++ b/src/core/dune @@ -4,7 +4,7 @@ (public_name msat) (libraries sequence) (synopsis "core data structures and algorithms for msat") - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) ) diff --git a/src/main/dune b/src/main/dune index 7b96abd7..bb0eb7d4 100644 --- a/src/main/dune +++ b/src/main/dune @@ -4,7 +4,7 @@ (name main) ;(package msat) (libraries containers msat msat_sat msat.backend) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) ) diff --git a/src/sat/dune b/src/sat/dune index b5940271..624b2a57 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -4,7 +4,7 @@ (public_name msat.sat) (synopsis "purely boolean interface to Msat") (libraries msat) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) ) From da24541fa0d397e0146d33d7fb07d74d34438914 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 19:05:53 -0600 Subject: [PATCH 097/182] feat: allow optional disabling of proof logging - goal: do not keep clauses alive after they're gc'd - default is to indeed log proofs --- src/core/Internal.ml | 43 ++++++++++++++++++++++++-------------- src/core/Solver_intf.ml | 10 ++++++++- src/main/main.ml | 14 ++++++++----- src/sudoku/sudoku_solve.ml | 2 +- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index ac7fbfc2..0fc077a3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -74,6 +74,7 @@ module Make(Plugin : PLUGIN) | Local | Lemma of lemma | History of clause list + | Empty_premise type elt = | E_lit of lit @@ -117,6 +118,7 @@ module Make(Plugin : PLUGIN) | Lemma _ -> "T" ^ string_of_int c.name | Local -> "L" ^ string_of_int c.name | History _ -> "C" ^ string_of_int c.name + | Empty_premise -> string_of_int c.name module Lit = struct type t = lit @@ -396,6 +398,7 @@ module Make(Plugin : PLUGIN) | Local -> Format.fprintf out "local" | History v -> List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v + | Empty_premise -> Format.fprintf out "" let debug out ({atoms=arr; cpremise=cp;_}as c) = Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" @@ -478,9 +481,11 @@ module Make(Plugin : PLUGIN) in aux (c, d) - let[@inline] prove conclusion = - assert (conclusion.cpremise <> History []); - conclusion + let prove conclusion = + match conclusion.cpremise with + | History [] -> assert false + | Empty_premise -> raise Solver_intf.No_proof + | _ -> conclusion let rec set_atom_proof a = let aux acc b = @@ -582,6 +587,7 @@ module Make(Plugin : PLUGIN) conclusion.cpremise <- History [c'; d']; assert (cmp_cl l (to_list conclusion) = 0); { conclusion; step = Resolution (c', d', a); } + | Empty_premise -> raise Solver_intf.No_proof (* Proof nodes manipulation *) let is_leaf = function @@ -615,6 +621,7 @@ module Make(Plugin : PLUGIN) if not @@ Clause.visited c then ( Clause.set_visited c true; match c.cpremise with + | Empty_premise -> raise Solver_intf.No_proof | Hyp _ | Lemma _ | Local -> aux (c :: res) acc r | History h -> let l = List.fold_left (fun acc c -> @@ -712,6 +719,8 @@ module Make(Plugin : PLUGIN) st : st; th: theory; + log_proof: bool; (* do we store proofs? *) + (* Clauses are simplified for eficiency purposes. In the following vectors, the comments actually refer to the original non-simplified clause. *) @@ -778,7 +787,7 @@ module Make(Plugin : PLUGIN) type solver = t (* Starting environment. *) - let create_ ~st (th:theory) : t = { + let create_ ~st ~log_proof (th:theory) : t = { st; th; unsat_at_0=None; next_decision = None; @@ -800,15 +809,16 @@ module Make(Plugin : PLUGIN) var_incr = 1.; clause_incr = 1.; + log_proof; restart_first = 100; learntsize_factor = 1. /. 3. ; } - let create ?(size=`Big) (th:theory) : t = + let create ?(log_proof=true) ?(size=`Big) (th:theory) : t = let st = create_st ~size () in - create_ ~st th + create_ ~st ~log_proof th let[@inline] st t = t.st let[@inline] nb_clauses st = Vec.size st.clauses_hyps @@ -1317,10 +1327,9 @@ module Make(Plugin : PLUGIN) Log.debug debug "(@[sat.analyze-conflict: skipping resolution for semantic propagation@])" | Some clause -> Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); - begin match clause.cpremise with - | History _ -> clause_bump_activity st clause - | Hyp _ | Local | Lemma _ -> () - end; + if Clause.learnt clause then ( + clause_bump_activity st clause; + ); history := clause :: !history; (* visit the current predecessors *) for j = 0 to Array.length clause.atoms - 1 do @@ -1393,6 +1402,7 @@ module Make(Plugin : PLUGIN) (* add the learnt clause to the clause database, propagate, etc. *) let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = + let proof = if st.log_proof then History cr.cr_history else Empty_premise in begin match cr.cr_learnt with | [] -> assert false | [fuip] -> @@ -1401,13 +1411,13 @@ module Make(Plugin : PLUGIN) (* incompatible at level 0 *) report_unsat st (US_false confl) ) else ( - let uclause = Clause.make cr.cr_learnt (History cr.cr_history) in + let uclause = Clause.make cr.cr_learnt proof in (* no need to attach [uclause], it is true at level 0 *) Clause.set_learnt uclause true; enqueue_bool st fuip ~level:0 (Bcp uclause) ) | fuip :: _ -> - let lclause = Clause.make cr.cr_learnt (History cr.cr_history) in + let lclause = Clause.make cr.cr_learnt proof in Clause.set_learnt lclause true; if Array.length lclause.atoms > 2 then ( Vec.push st.clauses_learnt lclause; (* potentially gc'able *) @@ -1459,13 +1469,14 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k -> k "(@[sat.dups-removed@ %a@])" Clause.debug c); let atoms, history = partition c.atoms in let clause = - if history = [] - then ( - (* update order of atoms *) + if history = [] then ( + (* just update order of atoms *) List.iteri (fun i a -> c.atoms.(i) <- a) atoms; c + ) else ( + let proof = if st.log_proof then History (c::history) else Empty_premise in + Clause.make atoms proof ) - else Clause.make atoms (History (c :: history)) in Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); match atoms with diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 90e06822..f52b5466 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -124,6 +124,8 @@ type ('a, 'b) gadt_eq = GADT_EQ : ('a, 'a) gadt_eq type void = (unit,bool) gadt_eq (** A provably empty type *) +exception No_proof + module type FORMULA = sig (** formulas *) @@ -408,9 +410,15 @@ module type S = sig type t = solver (** Main solver type, containing all state for solving. *) - val create : ?size:[`Tiny|`Small|`Big] -> theory -> t + val create : + ?log_proof:bool -> + ?size:[`Tiny|`Small|`Big] -> + theory -> + t (** Create new solver @param theory the theory + @param log_proof if true, stores proof (default [true]). Otherwise + the functions that return proofs will fail with [No_proof] @param size the initial size of internal data structures. The bigger, the faster, but also the more RAM it uses. *) diff --git a/src/main/main.ml b/src/main/main.ml index 79745c5e..d60fe072 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -15,15 +15,16 @@ let p_dot_proof = ref "" let p_proof_print = ref false let time_limit = ref 300. let size_limit = ref 1000_000_000. +let no_proof = ref false module S = Msat_sat -module Process = struct +module Process() = struct module D = Msat_backend.Dot.Make(S)(Msat_backend.Dot.Default(S)) let hyps = ref [] - let st = S.create ~size:`Big () + let st = S.create ~log_proof:(not !no_proof) ~size:`Big () let check_model sat = let check_clause c = @@ -63,7 +64,7 @@ module Process = struct let add_clauses cs = S.assume st (CCList.map conv_c cs) () -end +end[@@inline] let parse_file f = let module L = Lexing in @@ -126,6 +127,7 @@ let argspec = Arg.align [ "[smhd] Sets the time limit for the sat solver"; "-v", Arg.Int (fun i -> Log.set_debug i), " Sets the debug verbose level"; + "-no-proof", Arg.Set no_proof, " disable proof logging"; ] (* Limits alarm *) @@ -147,10 +149,12 @@ let main () = ); let al = Gc.create_alarm check in + let module P = Process() in + (* Interesting stuff happening *) let clauses = parse_file !file in - Process.add_clauses clauses; - Process.prove ~assumptions:[] (); + P.add_clauses clauses; + P.prove ~assumptions:[] (); Gc.delete_alarm al; () diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 0dfb04fc..0a873079 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -271,7 +271,7 @@ end = struct r let create g : t = - { solver=S.create (Theory.create g); grid0=g } + { solver=S.create ~log_proof:false (Theory.create g); grid0=g } end let solve_grid (g:Grid.t) : Grid.t option = From ebd8ad7110fa73a904a989c023be39c6f3f89dd8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 19:09:22 -0600 Subject: [PATCH 098/182] refactor: rename flag `clause.{learnt,removable}` more accurate representation of the semantics, esp. for theory lemmas --- src/core/Internal.ml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 0fc077a3..13f7704e 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -359,7 +359,7 @@ module Make(Plugin : PLUGIN) let flag_attached = 0b1 let flag_visited = 0b10 - let flag_learnt = 0b100 + let flag_removable = 0b100 let flag_dead = 0b1000 let[@inline] visited c = (c.flags land flag_visited) <> 0 @@ -372,10 +372,10 @@ module Make(Plugin : PLUGIN) if b then c.flags <- c.flags lor flag_attached else c.flags <- c.flags land lnot flag_attached - let[@inline] learnt c = (c.flags land flag_learnt) <> 0 - let[@inline] set_learnt c b = - if b then c.flags <- c.flags lor flag_learnt - else c.flags <- c.flags land lnot flag_learnt + let[@inline] removable c = (c.flags land flag_removable) <> 0 + let[@inline] set_removable c b = + if b then c.flags <- c.flags lor flag_removable + else c.flags <- c.flags land lnot flag_removable let[@inline] dead c = (c.flags land flag_dead) <> 0 let[@inline] set_dead c = c.flags <- c.flags lor flag_dead @@ -1327,7 +1327,7 @@ module Make(Plugin : PLUGIN) Log.debug debug "(@[sat.analyze-conflict: skipping resolution for semantic propagation@])" | Some clause -> Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); - if Clause.learnt clause then ( + if Clause.removable clause then ( clause_bump_activity st clause; ); history := clause :: !history; @@ -1413,12 +1413,12 @@ module Make(Plugin : PLUGIN) ) else ( let uclause = Clause.make cr.cr_learnt proof in (* no need to attach [uclause], it is true at level 0 *) - Clause.set_learnt uclause true; + Clause.set_removable uclause true; enqueue_bool st fuip ~level:0 (Bcp uclause) ) | fuip :: _ -> let lclause = Clause.make cr.cr_learnt proof in - Clause.set_learnt lclause true; + Clause.set_removable lclause true; if Array.length lclause.atoms > 2 then ( Vec.push st.clauses_learnt lclause; (* potentially gc'able *) ); @@ -1453,8 +1453,8 @@ module Make(Plugin : PLUGIN) record_learnt_clause st confl cr (* Get the correct vector to insert a clause in. *) - let clause_vector st c = - if Clause.learnt c then st.clauses_learnt else st.clauses_hyps + let[@inline] clause_vector st c = + if Clause.removable c then st.clauses_learnt else st.clauses_hyps (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) @@ -1625,7 +1625,7 @@ module Make(Plugin : PLUGIN) let acts_add_clause st ?(keep=false) (l:formula list) (lemma:lemma): unit = let atoms = List.rev_map (create_atom st) l in let c = Clause.make atoms (Lemma lemma) in - if not keep then Clause.set_learnt c true; + if not keep then Clause.set_removable c true; Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c @@ -1801,7 +1801,7 @@ module Make(Plugin : PLUGIN) let n_collected = ref 0 in while Vec.size v > n_of_learnts do let c = Vec.pop v in - assert (Clause.learnt c); + assert (Clause.removable c); Clause.set_dead c; assert (Clause.dead c); incr n_collected; From c89a99e82b9c97d2abce618511b6e6dd242f2bf9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 19:16:38 -0600 Subject: [PATCH 099/182] rename log-proof to store-proof --- src/core/Internal.ml | 14 +++++++------- src/core/Solver_intf.ml | 4 ++-- src/main/main.ml | 2 +- src/sudoku/sudoku_solve.ml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 13f7704e..5b98b9b3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -719,7 +719,7 @@ module Make(Plugin : PLUGIN) st : st; th: theory; - log_proof: bool; (* do we store proofs? *) + store_proof: bool; (* do we store proofs? *) (* Clauses are simplified for eficiency purposes. In the following vectors, the comments actually refer to the original non-simplified @@ -787,7 +787,7 @@ module Make(Plugin : PLUGIN) type solver = t (* Starting environment. *) - let create_ ~st ~log_proof (th:theory) : t = { + let create_ ~st ~store_proof (th:theory) : t = { st; th; unsat_at_0=None; next_decision = None; @@ -809,16 +809,16 @@ module Make(Plugin : PLUGIN) var_incr = 1.; clause_incr = 1.; - log_proof; + store_proof; restart_first = 100; learntsize_factor = 1. /. 3. ; } - let create ?(log_proof=true) ?(size=`Big) (th:theory) : t = + let create ?(store_proof=true) ?(size=`Big) (th:theory) : t = let st = create_st ~size () in - create_ ~st ~log_proof th + create_ ~st ~store_proof th let[@inline] st t = t.st let[@inline] nb_clauses st = Vec.size st.clauses_hyps @@ -1402,7 +1402,7 @@ module Make(Plugin : PLUGIN) (* add the learnt clause to the clause database, propagate, etc. *) let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = - let proof = if st.log_proof then History cr.cr_history else Empty_premise in + let proof = if st.store_proof then History cr.cr_history else Empty_premise in begin match cr.cr_learnt with | [] -> assert false | [fuip] -> @@ -1474,7 +1474,7 @@ module Make(Plugin : PLUGIN) List.iteri (fun i a -> c.atoms.(i) <- a) atoms; c ) else ( - let proof = if st.log_proof then History (c::history) else Empty_premise in + let proof = if st.store_proof then History (c::history) else Empty_premise in Clause.make atoms proof ) in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index f52b5466..2a005f21 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -411,13 +411,13 @@ module type S = sig (** Main solver type, containing all state for solving. *) val create : - ?log_proof:bool -> + ?store_proof:bool -> ?size:[`Tiny|`Small|`Big] -> theory -> t (** Create new solver @param theory the theory - @param log_proof if true, stores proof (default [true]). Otherwise + @param store_proof if true, stores proof (default [true]). Otherwise the functions that return proofs will fail with [No_proof] @param size the initial size of internal data structures. The bigger, the faster, but also the more RAM it uses. *) diff --git a/src/main/main.ml b/src/main/main.ml index d60fe072..8822b501 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -24,7 +24,7 @@ module Process() = struct let hyps = ref [] - let st = S.create ~log_proof:(not !no_proof) ~size:`Big () + let st = S.create ~store_proof:(not !no_proof) ~size:`Big () let check_model sat = let check_clause c = diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 0a873079..2b2f6de4 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -271,7 +271,7 @@ end = struct r let create g : t = - { solver=S.create ~log_proof:false (Theory.create g); grid0=g } + { solver=S.create ~store_proof:false (Theory.create g); grid0=g } end let solve_grid (g:Grid.t) : Grid.t option = From 6dbaa2d335d87dd09c74f3848948f0033c917374 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 19:44:02 -0600 Subject: [PATCH 100/182] feat: expose `eval_atom` --- src/core/Internal.ml | 9 +++++++-- src/core/Solver_intf.ml | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 5b98b9b3..2ce6dfe9 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1667,12 +1667,15 @@ module Make(Plugin : PLUGIN) f e done - let acts_eval_lit st (f:formula) : Solver_intf.lbool = - let a = create_atom st f in + let eval_atom_ a = if Atom.is_true a then Solver_intf.L_true else if Atom.is_false a then Solver_intf.L_false else Solver_intf.L_undefined + let[@inline] acts_eval_lit st (f:formula) : Solver_intf.lbool = + let a = create_atom st f in + eval_atom_ a + let[@inline] acts_mk_lit st f : unit = ignore (create_atom st f : atom) @@ -2088,6 +2091,8 @@ module Make(Plugin : PLUGIN) b && lev = 0 with UndecidedLit -> false + let[@inline] eval_atom _st a : Solver_intf.lbool = eval_atom_ a + let export (st:t) : clause Solver_intf.export = let hyps = hyps st in let history = history st in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 2a005f21..4f50a6e7 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -468,6 +468,9 @@ module type S = sig (** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e. it must hold in all models *) + val eval_atom : t -> atom -> lbool + (** Evaluate atom in current state *) + val export : t -> clause export end From d089db3e4d58ed3aedf075dcd3337057d656a325 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 19:46:58 -0600 Subject: [PATCH 101/182] feat: expose printers --- src/core/Msat.ml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 4cf87e24..2ef93ebe 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -53,6 +53,17 @@ type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) type negated = Solver_intf.negated = Negated | Same_sign +(** Print {!negated} values *) +let pp_negated out = function + | Negated -> Format.fprintf out "negated" + | Same_sign -> Format.fprintf out "same-sign" + +(** Print {!lbool} values *) +let pp_lbool out = function + | L_true -> Format.fprintf out "true" + | L_false -> Format.fprintf out "false" + | L_undefined -> Format.fprintf out "undefined" + module Make_mcsat = Solver.Make_mcsat module Make_cdcl_t = Solver.Make_cdcl_t module Make_pure_sat = Solver.Make_pure_sat From ba4c360cbdc5c0f70ffba0ce9f416f35eaad343b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 21:53:04 -0600 Subject: [PATCH 102/182] fix: when simplifying, copy flags properly --- src/core/Internal.ml | 57 +++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 2ce6dfe9..08ebcf24 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -339,16 +339,16 @@ module Make(Plugin : PLUGIN) let make_a = let n = ref 0 in - fun atoms premise -> + fun ~flags atoms premise -> let name = !n in incr n; { name; atoms = atoms; - flags = 0; + flags; activity = 0.; cpremise = premise} - let make l premise = make_a (Array.of_list l) premise + let make ~flags l premise = make_a ~flags (Array.of_list l) premise let empty = make [] (History []) let name = name_of_clause @@ -362,6 +362,9 @@ module Make(Plugin : PLUGIN) let flag_removable = 0b100 let flag_dead = 0b1000 + let[@inline] make_removable l premise = make ~flags:flag_removable l premise + let[@inline] make_permanent l premise = make ~flags:0 l premise + let[@inline] visited c = (c.flags land flag_visited) <> 0 let[@inline] set_visited c b = if b then c.flags <- c.flags lor flag_visited @@ -502,7 +505,7 @@ module Make(Plugin : PLUGIN) ) else ( assert (a.neg.is_true); let r = History (c :: (Array.fold_left aux [] c.atoms)) in - let c' = Clause.make [a.neg] r in + let c' = Clause.make_permanent [a.neg] r in a.var.reason <- Some (Bcp c'); Log.debugf 5 (fun k -> k "(@[proof.analyze.new-reason@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c'); @@ -517,7 +520,7 @@ module Make(Plugin : PLUGIN) ) else ( Log.debugf 1 (fun k -> k "(@[sat.prove-unsat@ :from %a@])" Clause.debug conflict); let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.atoms in - let res = Clause.make [] (History (conflict :: l)) in + let res = Clause.make_permanent [] (History (conflict :: l)) in Log.debugf 1 (fun k -> k "(@[sat.proof-found@ %a@])" Clause.debug res); res ) @@ -553,7 +556,7 @@ module Make(Plugin : PLUGIN) begin match r with | [] -> (l, c, d, a) | _ -> - let new_clause = Clause.make l (History [c; d]) in + let new_clause = Clause.make ~flags:c.flags l (History [c; d]) in chain_res (new_clause, l) r end | _ -> @@ -981,12 +984,13 @@ module Make(Plugin : PLUGIN) if Var.seen_both a.var then trivial := true; Var.clear a.var) !res; - if !trivial then + if !trivial then ( raise Trivial - else if !duplicates = [] then + ) else if !duplicates = [] then ( clause - else - Clause.make !res (History [clause]) + ) else ( + Clause.make ~flags:clause.flags !res (History [clause]) + ) (* Partition literals for new clauses, into: - true literals (maybe makes the clause trivial if the lit is proved true at level 0) @@ -1117,10 +1121,10 @@ module Make(Plugin : PLUGIN) (* Recover the right theory state. *) let n = decision_level st - lvl in assert (n>0); - Plugin.pop_levels st.th n; (* Resize the vectors according to their new size. *) Vec.shrink st.trail !head; Vec.shrink st.elt_levels lvl; + Plugin.pop_levels st.th n; ); () @@ -1161,7 +1165,7 @@ module Make(Plugin : PLUGIN) with only one formula (which is [a]). So we explicitly create that clause and set it as the cause for the propagation of [a], that way we can rebuild the whole resolution tree when we want to prove [a]. *) - let c' = Clause.make l (History (cl :: history)) in + let c' = Clause.make ~flags:cl.flags l (History (cl :: history)) in Log.debugf debug (fun k -> k "(@[sat.simplified-reason@ %a@ %a@])" Clause.debug cl Clause.debug c'); Bcp c' @@ -1411,14 +1415,12 @@ module Make(Plugin : PLUGIN) (* incompatible at level 0 *) report_unsat st (US_false confl) ) else ( - let uclause = Clause.make cr.cr_learnt proof in + let uclause = Clause.make_removable cr.cr_learnt proof in (* no need to attach [uclause], it is true at level 0 *) - Clause.set_removable uclause true; enqueue_bool st fuip ~level:0 (Bcp uclause) ) | fuip :: _ -> - let lclause = Clause.make cr.cr_learnt proof in - Clause.set_removable lclause true; + let lclause = Clause.make_removable cr.cr_learnt proof in if Array.length lclause.atoms > 2 then ( Vec.push st.clauses_learnt lclause; (* potentially gc'able *) ); @@ -1475,7 +1477,7 @@ module Make(Plugin : PLUGIN) c ) else ( let proof = if st.store_proof then History (c::history) else Empty_premise in - Clause.make atoms proof + Clause.make ~flags:c.flags atoms proof ) in Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); @@ -1624,14 +1626,15 @@ module Make(Plugin : PLUGIN) let acts_add_clause st ?(keep=false) (l:formula list) (lemma:lemma): unit = let atoms = List.rev_map (create_atom st) l in - let c = Clause.make atoms (Lemma lemma) in - if not keep then Clause.set_removable c true; + let flags = if keep then 0 else Clause.flag_removable in + let c = Clause.make ~flags atoms (Lemma lemma) in Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c let acts_raise st (l:formula list) proof : 'a = let atoms = List.rev_map (create_atom st) l in - let c = Clause.make atoms (Lemma proof) in + (* conflicts can be removed *) + let c = Clause.make_removable atoms (Lemma proof) in raise_notrace (Th_conflict c) let acts_propagate (st:t) f = function @@ -1642,7 +1645,7 @@ module Make(Plugin : PLUGIN) let l = List.rev_map (mk_atom st) causes in if List.for_all (fun a -> a.is_true) l then ( let p = mk_atom st f in - let c = Clause.make (p :: List.map Atom.neg l) (Lemma proof) in + let c = Clause.make_removable (p :: List.map Atom.neg l) (Lemma proof) in if p.is_true then () else if p.neg.is_true then ( Vec.push st.clauses_to_add c @@ -1971,7 +1974,7 @@ module Make(Plugin : PLUGIN) List.iter (fun l -> let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make atoms (Hyp lemma) in + let c = Clause.make_permanent atoms (Hyp lemma) in Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); Vec.push st.clauses_to_add c) cnf @@ -2048,16 +2051,16 @@ module Make(Plugin : PLUGIN) let core = List.rev core in (* increasing trail order *) assert (Atom.equal first @@ List.hd core); let proof_of (a:atom) = match Atom.reason a with - | Some (Decision | Semantic) -> Clause.make [a] Local + | Some (Decision | Semantic) -> Clause.make_removable [a] Local | Some (Bcp c) -> c | None -> assert false in let other_lits = List.filter (fun a -> not (Atom.equal a first)) core in let hist = - Clause.make [first] Local :: + Clause.make_permanent [first] Local :: proof_of first :: List.map proof_of other_lits in - Clause.make [] (History hist) + Clause.make_permanent [] (History hist) ) in fun () -> Lazy.force c in @@ -2068,11 +2071,11 @@ module Make(Plugin : PLUGIN) { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } let[@inline] add_clause_a st c lemma : unit = - let c = Clause.make_a c (Hyp lemma) in + let c = Clause.make_a ~flags:0 c (Hyp lemma) in add_clause_ st c let[@inline] add_clause st c lemma : unit = - let c = Clause.make c (Hyp lemma) in + let c = Clause.make_permanent c (Hyp lemma) in add_clause_ st c let solve ?(assumptions=[]) (st:t) : res = From 7891f2b69e7d20447a108ab5216eb63610d27c9e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Feb 2019 21:59:24 -0600 Subject: [PATCH 103/182] refactor: cleaner choice of which vector to add a clause to --- src/core/Internal.ml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 08ebcf24..823e8f96 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1455,8 +1455,12 @@ module Make(Plugin : PLUGIN) record_learnt_clause st confl cr (* Get the correct vector to insert a clause in. *) - let[@inline] clause_vector st c = - if Clause.removable c then st.clauses_learnt else st.clauses_hyps + let[@inline] add_clause_to_vec st c = + if Clause.removable c then ( + Vec.push st.clauses_learnt c + ) else ( + Vec.push st.clauses_hyps c + ) (* Add a new clause, simplifying, propagating, and backtracking if the clause is false in the current trail *) @@ -1465,9 +1469,9 @@ module Make(Plugin : PLUGIN) (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) Array.iter (fun x -> insert_var_order st (Elt.of_var x.var)) init.atoms; - let vec = clause_vector st init in try let c = eliminate_duplicates init in + assert (c.flags = init.flags); Log.debugf debug (fun k -> k "(@[sat.dups-removed@ %a@])" Clause.debug c); let atoms, history = partition c.atoms in let clause = @@ -1480,6 +1484,7 @@ module Make(Plugin : PLUGIN) Clause.make ~flags:c.flags atoms proof ) in + assert (clause.flags = init.flags); Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); match atoms with | [] -> @@ -1494,11 +1499,11 @@ module Make(Plugin : PLUGIN) ) else ( Log.debugf debug (fun k->k "(@[sat.add-clause.unit-clause@ :propagating %a@])" Atom.debug a); - Vec.push vec clause; + add_clause_to_vec st clause; enqueue_bool st a ~level:0 (Bcp clause) ) | a::b::_ -> - Vec.push vec clause; + add_clause_to_vec st clause; if a.neg.is_true then ( (* Atoms need to be sorted in decreasing order of decision level, or we might watch the wrong literals. *) From 75476b8dd73cfc8bbcfa25d692846e2e9463dec8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 2 Feb 2019 18:55:05 -0600 Subject: [PATCH 104/182] test: use `mdx` to ensure the readme code snippets compile --- README.md | 99 +++++++++++++++++++++++++++++++++++++------------------ dune | 8 +++++ msat.opam | 1 + 3 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 dune diff --git a/README.md b/README.md index 403ab049..3f7f0168 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,17 @@ See https://gbury.github.io/mSAT/ Once the package is on [opam](http://opam.ocaml.org), just `opam install msat`. For the development version, use: - opam pin add msat https://github.com/Gbury/mSAT.git +``` +opam pin add msat https://github.com/Gbury/mSAT.git +``` ### Manual installation -You will need ocamlfind and ocamlbuild. The command is: +You will need `dune` and `sequence`. The command is: - make install +``` +$ make install +``` ## USAGE @@ -47,43 +51,66 @@ as a functor which takes two modules : ### Sat Solver -A ready-to-use SAT solver is available in the Sat module. It can be used +A ready-to-use SAT solver is available in the `Msat_sat` module +using the `msat.sat` library. It can be loaded as shown in the following code : ```ocaml -(* Module initialization *) -module Sat = Msat.Sat.Make() -module E = Msat.Sat.Expr (* expressions *) +# #require "msat.sat";; +# #print_depth 0;; (* do not print details *) +``` + +Then we can create a solver and create some boolean variables: + +```ocaml +module Sat = Msat_sat +module E = Sat.Int_lit (* expressions *) + +let solver = Sat.create() (* We create here two distinct atoms *) let a = E.fresh () (* A 'new_atom' is always distinct from any other atom *) let b = E.make 1 (* Atoms can be created from integers *) - -(* We can try and check the satisfiability of some clauses -- - here, the clause [a or b]. - Sat.assume adds a list of clauses to the solver. *) -let() = Sat.assume [[a; b]] -let res = Sat.solve () (* Should return (Sat.Sat _) *) - -(* The Sat solver has an incremental mutable state, so we still have - the clause [a or b] in our assumptions. - We add [not a] and [not b] to the state. *) -let () = Sat.assume [[E.neg a]; [E.neg b]] -let res = Sat.solve () (* Should return (Sat.Unsat _) *) ``` +We can try and check the satisfiability of some clauses — here, the clause `a or b`. +`Sat.assume` adds a list of clauses to the solver. Calling `Sat.solve` +will check the satisfiability of the current set of clauses, here "Sat". + +```ocaml +# Sat.assume solver [[a; b]] ();; +- : unit = () +# let res = Sat.solve solver;; +val res : Sat.res = Sat.Sat ... +``` + +The Sat solver has an incremental mutable state, so we still have +the clause `a or b` in our assumptions. +We add `not a` and `not b` to the state, and get "Unsat". + +```ocaml +# Sat.assume solver [[E.neg a]; [E.neg b]] () ;; +- : unit = () +# let res = Sat.solve solver ;; +val res : Sat.res = Sat.Unsat ... +``` #### Formulas API Writing clauses by hand can be tedious and error-prone. -The functor `Msat.Tseitin.Make` proposes a formula AST (parametrized by +The functor `Msat_tseitin.Make` in the library `msat.tseitin` +proposes a formula AST (parametrized by atoms) and a function to convert these formulas into clauses: +```ocaml +# #require "msat.tseitin";; +``` + ```ocaml (* Module initialization *) -module Sat = Msat.Sat.Make() -module E = Msat.Sat.Expr (* expressions *) -module F = Msat.Tseitin.Make(E) +module F = Msat_tseitin.Make(E) + +let solver = Sat.create () (* We create here two distinct atoms *) let a = E.fresh () (* A fresh atom is always distinct from any other atom *) @@ -94,14 +121,22 @@ let p = F.make_atom a let q = F.make_atom b let r = F.make_and [p; q] let s = F.make_or [F.make_not p; F.make_not q] - -(* We can try and check the satisfiability of the given formulas *) -let () = Sat.assume (F.make_cnf r) -let _ = Sat.solve () (* Should return (Sat.Sat _) *) - -(* The Sat solver has an incremental mutable state, so we still have - * the formula 'r' in our assumptions *) -let () = Sat.assume (F.make_cnf s) -let _ = Sat.solve () (* Should return (Sat.Unsat _) *) +``` + +We can try and check the satisfiability of the given formulas, by turning +it into clauses using `make_cnf`: + +```ocaml +# Sat.assume solver (F.make_cnf r) ();; +- : unit = () +# Sat.solve solver;; +- : Sat.res = Sat.Sat ... +``` + +```ocaml +# Sat.assume solver (F.make_cnf s) ();; +- : unit = () +# Sat.solve solver ;; +- : Sat.res = Sat.Unsat ... ``` diff --git a/dune b/dune new file mode 100644 index 00000000..d5e5e21b --- /dev/null +++ b/dune @@ -0,0 +1,8 @@ + +(alias + (name runtest) + (deps README.md) + (action (progn + (run mdx test %{deps}) + (diff? %{deps} %{deps}.corrected)))) + diff --git a/msat.opam b/msat.opam index c0d0a3a5..c2fca559 100644 --- a/msat.opam +++ b/msat.opam @@ -15,6 +15,7 @@ depends: [ "dune" {build} "sequence" "containers" {with-test} + "mdx" {with-test} ] tags: [ "sat" "smt" ] homepage: "https://github.com/Gbury/mSAT" From c376f1d763370e52bb989e8de443b2b6867fcdeb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 2 Feb 2019 19:10:17 -0600 Subject: [PATCH 105/182] fix: msat.tseitin depends on msat --- src/tseitin/dune | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tseitin/dune b/src/tseitin/dune index a9057a53..0a35225d 100644 --- a/src/tseitin/dune +++ b/src/tseitin/dune @@ -3,6 +3,7 @@ (name msat_tseitin) (public_name msat.tseitin) (synopsis "Tseitin transformation for msat") + (libraries msat) (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) From 1632c1a619f42239cd19ad5b9b6f61c296dbe6e8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 2 Feb 2019 19:28:12 -0600 Subject: [PATCH 106/182] doc: check something in the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3f7f0168..e0ef842f 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ We can try and check the satisfiability of some clauses — here, the clause `a will check the satisfiability of the current set of clauses, here "Sat". ```ocaml +# a <> b;; +- : bool = true # Sat.assume solver [[a; b]] ();; - : unit = () # let res = Sat.solve solver;; From ffa769c48c98bb87ca0f8eafa2d7385064439c69 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 2 Feb 2019 21:22:27 -0600 Subject: [PATCH 107/182] doc: add a section and test on the sudoku solver --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index e0ef842f..4dc23a6d 100644 --- a/README.md +++ b/README.md @@ -142,3 +142,44 @@ it into clauses using `make_cnf`: - : Sat.res = Sat.Unsat ... ``` +### CDCL(T): a Sudoku solver as an example + +The directory `src/sudoku/` contains a simple Sudoku solver that +uses the interface `Msat.Make_cdcl_t`. +In essence, it implements the logical theory `CDCL(Sudoku)`. +The script `sudoku_solve.sh` compiles and runs the solver. + +It's able to parse sudoku grids denoted as 81 integers +(see `tests/sudoku/sudoku.txt` for example). + +Here is a sample grid and the output from the solver (in roughly .5s): + +```sh +$ echo '..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9' > sudoku.txt +$ ./sudoku_solve.sh sudoku.txt +... +######################### +solve grid: + ......... + .....3.85 + ..1.2.... + ...5.7... + ..4...1.. + .9....... + 5......73 + ..2.1.... + ....4...9 + +... + 987654321 + 246173985 + 351928746 + 128537694 + 634892157 + 795461832 + 519286473 + 472319568 + 863745219 + +################### +``` From 8d012d2f49c77943067f5b1a2dc50b92b70f0e60 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 4 Feb 2019 20:35:28 -0600 Subject: [PATCH 108/182] refactor: fix problems from review - use `cid` instead of `name` for clauses - has the name of the clause, not its content - simplified some things --- src/core/Internal.ml | 48 +++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 823e8f96..d5675bfc 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -11,6 +11,9 @@ module type PLUGIN = sig include Solver_intf.PLUGIN_MCSAT end +let invalid_argf fmt = + Format.kasprintf (fun msg -> invalid_arg ("msat: " ^ msg)) fmt + module Make(Plugin : PLUGIN) = struct module Term = Plugin.Term @@ -55,7 +58,7 @@ module Make(Plugin : PLUGIN) } and clause = { - name : int; + cid: int; atoms : atom array; mutable cpremise : premise; mutable activity : float; @@ -114,11 +117,11 @@ module Make(Plugin : PLUGIN) let iter_elt st f = Vec.iter f st.vars let name_of_clause c = match c.cpremise with - | Hyp _ -> "H" ^ string_of_int c.name - | Lemma _ -> "T" ^ string_of_int c.name - | Local -> "L" ^ string_of_int c.name - | History _ -> "C" ^ string_of_int c.name - | Empty_premise -> string_of_int c.name + | Hyp _ -> "H" ^ string_of_int c.cid + | Lemma _ -> "T" ^ string_of_int c.cid + | Local -> "L" ^ string_of_int c.cid + | History _ -> "C" ^ string_of_int c.cid + | Empty_premise -> string_of_int c.cid module Lit = struct type t = lit @@ -156,9 +159,10 @@ module Make(Plugin : PLUGIN) (v.lid+1) debug_assign v Term.pp v.term end - let seen_var = 0x1 - let seen_pos = 0x2 - let seen_neg = 0x4 + (* some boolean flags for variables, used as masks *) + let seen_var = 0b1 + let seen_pos = 0b10 + let seen_neg = 0b100 module Var = struct type t = var @@ -234,8 +238,7 @@ module Make(Plugin : PLUGIN) let[@inline] is_false a = a.neg.is_true let[@inline] seen a = - let pos = equal a (abs a) in - if pos + if sign a then (seen_pos land a.var.v_fields <> 0) else (seen_neg land a.var.v_fields <> 0) @@ -340,9 +343,9 @@ module Make(Plugin : PLUGIN) let make_a = let n = ref 0 in fun ~flags atoms premise -> - let name = !n in + let cid = !n in incr n; - { name; + { cid; atoms = atoms; flags; activity = 0.; @@ -352,10 +355,10 @@ module Make(Plugin : PLUGIN) let empty = make [] (History []) let name = name_of_clause - let[@inline] equal c1 c2 = c1==c2 + let[@inline] equal c1 c2 = c1.cid = c2.cid + let[@inline] hash c = Hashtbl.hash c.cid let[@inline] atoms c = c.atoms let[@inline] atoms_l c = Array.to_list c.atoms - let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms let flag_attached = 0b1 let flag_visited = 0b10 @@ -706,10 +709,10 @@ module Make(Plugin : PLUGIN) let debug = 50 let var_decay : float = 1. /. 0.95 - (* inverse of the activity factor for variables. Default 1/0.999 *) + (* inverse of the activity factor for variables. Default 1/0.95 *) let clause_decay : float = 1. /. 0.999 - (* inverse of the activity factor for clauses. Default 1/0.95 *) + (* inverse of the activity factor for clauses. Default 1/0.999 *) let restart_inc : float = 1.5 (* multiplicative factor for restart limit, default 1.5 *) @@ -1261,9 +1264,7 @@ module Make(Plugin : PLUGIN) | Solver_intf.Unknown -> None | Solver_intf.Valued (b, l) -> if l = [] then ( - raise (Invalid_argument ( - Format.asprintf "msat:core/internal.ml: %s" - "semantic propagation at level 0 are currently forbidden")); + invalid_argf "semantic propagation at level 0 currently forbidden: %a" Atom.pp a; ); let atom = if b then a else a.neg in enqueue_semantic st atom l; @@ -1660,7 +1661,7 @@ module Make(Plugin : PLUGIN) enqueue_bool st p ~level (Bcp c) ) ) else ( - invalid_arg "Msat.Internal.slice_propagate" + invalid_argf "slice.acts_propagate: Consequence should contain only true literals" ) let[@specialise] acts_iter st ~full head f : unit = @@ -1715,10 +1716,7 @@ module Make(Plugin : PLUGIN) (* Assert that the conflict is indeeed a conflict *) let check_is_conflict_ (c:Clause.t) : unit = if not @@ Array.for_all (Atom.is_false) c.atoms then ( - let msg = - Format.asprintf "msat:core/internal: invalid conflict %a" Clause.debug c - in - raise (Invalid_argument msg); + invalid_argf "conflict should be false: %a" Clause.debug c ) (* some boolean literals were decided/propagated within Msat. Now we From 1ccc292d7930beea417c4efcbb103233068c0459 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 6 Feb 2019 22:01:33 -0600 Subject: [PATCH 109/182] refactor: move `Backtrackable_ref` into its own sub-library --- src/{core => backtrack}/Backtrackable_ref.ml | 0 src/{core => backtrack}/Backtrackable_ref.mli | 0 src/backtrack/Msat_backtrack.ml | 2 ++ src/backtrack/dune | 11 +++++++++++ src/core/Msat.ml | 2 -- src/sudoku/dune | 2 +- src/sudoku/sudoku_solve.ml | 2 +- 7 files changed, 15 insertions(+), 4 deletions(-) rename src/{core => backtrack}/Backtrackable_ref.ml (100%) rename src/{core => backtrack}/Backtrackable_ref.mli (100%) create mode 100644 src/backtrack/Msat_backtrack.ml create mode 100644 src/backtrack/dune diff --git a/src/core/Backtrackable_ref.ml b/src/backtrack/Backtrackable_ref.ml similarity index 100% rename from src/core/Backtrackable_ref.ml rename to src/backtrack/Backtrackable_ref.ml diff --git a/src/core/Backtrackable_ref.mli b/src/backtrack/Backtrackable_ref.mli similarity index 100% rename from src/core/Backtrackable_ref.mli rename to src/backtrack/Backtrackable_ref.mli diff --git a/src/backtrack/Msat_backtrack.ml b/src/backtrack/Msat_backtrack.ml new file mode 100644 index 00000000..14857855 --- /dev/null +++ b/src/backtrack/Msat_backtrack.ml @@ -0,0 +1,2 @@ + +module Ref = Backtrackable_ref diff --git a/src/backtrack/dune b/src/backtrack/dune new file mode 100644 index 00000000..48740ab9 --- /dev/null +++ b/src/backtrack/dune @@ -0,0 +1,11 @@ + +(library + (name msat_backtrack) + (public_name msat.backtrack) + (libraries msat) + (synopsis "backtrackable data structures for msat") + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 + -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 2ef93ebe..a913e968 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -68,8 +68,6 @@ module Make_mcsat = Solver.Make_mcsat module Make_cdcl_t = Solver.Make_cdcl_t module Make_pure_sat = Solver.Make_pure_sat -module Backtrackable_ref = Backtrackable_ref - (**/**) module Vec = Vec module Log = Log diff --git a/src/sudoku/dune b/src/sudoku/dune index 0800a980..ad4a2b92 100644 --- a/src/sudoku/dune +++ b/src/sudoku/dune @@ -2,7 +2,7 @@ (executable (name sudoku_solve) (modes native) - (libraries msat sequence containers) + (libraries msat msat.backtrack sequence containers) (flags :standard -warn-error -a -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 2b2f6de4..ffbba32c 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -125,7 +125,7 @@ end = struct a end -module B_ref = Msat.Backtrackable_ref +module B_ref = Msat_backtrack.Ref module Solver : sig type t From 7583e78bd2b53a7dcbb680be8b0f7220a1fc9237 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 9 Feb 2019 17:10:39 -0600 Subject: [PATCH 110/182] fix(propagate): insert propagated literal itself --- src/core/Internal.ml | 47 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index d5675bfc..3bf3bc39 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -874,7 +874,7 @@ module Make(Plugin : PLUGIN) When we add a variable (which wraps a formula), we also need to add all its subterms. *) - let rec insert_var_order st (elt:elt) : unit = + let rec insert_elt_order st (elt:elt) : unit = H.insert st.order elt; if Plugin.mcsat then ( match elt with @@ -882,8 +882,11 @@ module Make(Plugin : PLUGIN) | E_var v -> insert_subterms_order st v ) + and insert_var_order st (v:var) : unit = + insert_elt_order st (E_var v) + and insert_subterms_order st (v:var) : unit = - iter_sub (fun t -> insert_var_order st (Elt.of_lit t)) v + iter_sub (fun t -> insert_elt_order st (Elt.of_lit t)) v (* Add new litterals/atoms on which to decide on, even if there is no clause that constrains it. @@ -892,13 +895,13 @@ module Make(Plugin : PLUGIN) let make_term st t = let l = Lit.make st.st t in if l.l_level < 0 then ( - insert_var_order st (E_lit l) + insert_elt_order st (E_lit l) ) let make_atom st (p:formula) : atom = let a = mk_atom st p in if a.var.v_level < 0 then ( - insert_var_order st (E_var a.var); + insert_elt_order st (E_var a.var); ) else ( assert (a.is_true || a.neg.is_true); ); @@ -1100,7 +1103,7 @@ module Make(Plugin : PLUGIN) ) else ( l.assigned <- None; l.l_level <- -1; - insert_var_order st (Elt.of_lit l) + insert_elt_order st (Elt.of_lit l) ) (* A variable is not true/false anymore, one of two things can happen: *) | Atom a -> @@ -1118,7 +1121,7 @@ module Make(Plugin : PLUGIN) a.neg.is_true <- false; a.var.v_level <- -1; a.var.reason <- None; - insert_var_order st (Elt.of_var a.var) + insert_elt_order st (Elt.of_var a.var) ) done; (* Recover the right theory state. *) @@ -1469,7 +1472,7 @@ module Make(Plugin : PLUGIN) Log.debugf debug (fun k -> k "(@[sat.add-clause@ @[%a@]@])" Clause.debug init); (* Insertion of new lits is done before simplification. Indeed, else a lit in a trivial clause could end up being not decided on, which is a bug. *) - Array.iter (fun x -> insert_var_order st (Elt.of_var x.var)) init.atoms; + Array.iter (fun x -> insert_elt_order st (Elt.of_var x.var)) init.atoms; try let c = eliminate_duplicates init in assert (c.flags = init.flags); @@ -1648,20 +1651,20 @@ module Make(Plugin : PLUGIN) let a = mk_atom st f in enqueue_semantic st a l | Solver_intf.Consequence (causes, proof) -> - let l = List.rev_map (mk_atom st) causes in - if List.for_all (fun a -> a.is_true) l then ( - let p = mk_atom st f in - let c = Clause.make_removable (p :: List.map Atom.neg l) (Lemma proof) in - if p.is_true then () - else if p.neg.is_true then ( - Vec.push st.clauses_to_add c - ) else ( - insert_subterms_order st p.var; - let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in - enqueue_bool st p ~level (Bcp c) - ) - ) else ( + let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) causes in + if List.exists Atom.is_true l then ( invalid_argf "slice.acts_propagate: Consequence should contain only true literals" + ); + let p = mk_atom st f in + if Atom.is_true p then () + else if Atom.is_false p then ( + let c = Clause.make_removable (p :: l) (Lemma proof) in + raise_notrace (Th_conflict c) + ) else ( + insert_var_order st p.var; + let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in + let c = Clause.make_removable (p :: l) (Lemma proof) in + enqueue_bool st p ~level (Bcp c) ) let[@specialise] acts_iter st ~full head f : unit = @@ -1736,7 +1739,7 @@ module Make(Plugin : PLUGIN) propagate st | exception Th_conflict c -> check_is_conflict_ c; - Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; + Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; Some c ) @@ -1964,7 +1967,7 @@ module Make(Plugin : PLUGIN) flush_clauses st; | exception Th_conflict c -> check_is_conflict_ c; - Array.iter (fun a -> insert_var_order st (Elt.of_var a.var)) c.atoms; + Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c; flush_clauses st; From 8ad78b2acd1257d97f474af7b42a792f7d491225 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 9 Feb 2019 17:12:33 -0600 Subject: [PATCH 111/182] refactor: a bit of cleanup for mcsat --- src/core/Internal.ml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 3bf3bc39..adaef24d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -850,20 +850,23 @@ module Make(Plugin : PLUGIN) | None -> assert false ) + let mk_atom_mcsat_ st a = + match a.var.v_assignable with + | Some _ -> () + | None -> + let l = ref [] in + Plugin.iter_assignable st.th + (fun t -> l := Lit.make st.st t :: !l) + a.var.pa.lit; + a.var.v_assignable <- Some !l; + () + (* When we have a new literal, we need to first create the list of its subterms. *) let mk_atom st (f:formula) : atom = let res = Atom.make st.st f in if Plugin.mcsat then ( - begin match res.var.v_assignable with - | Some _ -> () - | None -> - let l = ref [] in - Plugin.iter_assignable st.th - (fun t -> l := Lit.make st.st t :: !l) - res.var.pa.lit; - res.var.v_assignable <- Some !l; - end; + mk_atom_mcsat_ st res; ); res From 96c4d83781cff28f11b2ff1a0fb73dd0d22add8e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 9 Feb 2019 21:43:57 -0600 Subject: [PATCH 112/182] detail: add debug message --- src/core/Internal.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index adaef24d..e8cfee72 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1647,6 +1647,7 @@ module Make(Plugin : PLUGIN) let atoms = List.rev_map (create_atom st) l in (* conflicts can be removed *) let c = Clause.make_removable atoms (Lemma proof) in + Log.debugf 5 (fun k->k "(@[@{sat.th.raise-conflict@}@ %a@])" Clause.debug c); raise_notrace (Th_conflict c) let acts_propagate (st:t) f = function From 110eda2f053224e2a2a45e74a05aec58035cb452 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Feb 2019 16:58:41 -0600 Subject: [PATCH 113/182] feat: re-export exn --- src/core/Msat.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/Msat.ml b/src/core/Msat.ml index a913e968..156da28e 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -64,6 +64,8 @@ let pp_lbool out = function | L_false -> Format.fprintf out "false" | L_undefined -> Format.fprintf out "undefined" +exception No_proof = Solver_intf.No_proof + module Make_mcsat = Solver.Make_mcsat module Make_cdcl_t = Solver.Make_cdcl_t module Make_pure_sat = Solver.Make_pure_sat From ea98f6f027857f4a848548c2186a422203333796 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Feb 2019 16:58:49 -0600 Subject: [PATCH 114/182] refactor: return an array from conflict analysis --- src/core/Internal.ml | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e8cfee72..16bf0300 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -366,6 +366,7 @@ module Make(Plugin : PLUGIN) let flag_dead = 0b1000 let[@inline] make_removable l premise = make ~flags:flag_removable l premise + let[@inline] make_removable_a l premise = make_a ~flags:flag_removable l premise let[@inline] make_permanent l premise = make ~flags:0 l premise let[@inline] visited c = (c.flags land flag_visited) <> 0 @@ -1280,10 +1281,12 @@ module Make(Plugin : PLUGIN) and a boolean stating whether it is a UIP ("Unique Implication Point") precond: the atom list is sorted by decreasing decision level *) - let backtrack_lvl _st : atom list -> int * bool = function - | [] | [_] -> + let backtrack_lvl _st (arr: atom array) : int * bool = + if Array.length arr <= 1 then ( 0, true - | a :: b :: _ -> + ) else ( + let a = arr.(0) in + let b = arr.(1) in assert(a.var.v_level > 0); if a.var.v_level > b.var.v_level then ( (* backtrack below [a], so we can propagate [not a] *) @@ -1293,6 +1296,7 @@ module Make(Plugin : PLUGIN) assert (a.var.v_level >= 0); max (a.var.v_level - 1) 0, false ) + ) (* result of conflict analysis, containing the learnt clause and some additional info. @@ -1302,7 +1306,7 @@ module Make(Plugin : PLUGIN) (boolean conflict i.e hypothesis, or theory lemma) *) type conflict_res = { cr_backtrack_lvl : int; (* level to backtrack to *) - cr_learnt: atom list; (* lemma learnt from conflict *) + cr_learnt: atom array; (* lemma learnt from conflict *) cr_history: clause list; (* justification *) cr_is_uip: bool; (* conflict is UIP? *) } @@ -1403,10 +1407,12 @@ module Make(Plugin : PLUGIN) (* put high-level literals first, so that: - they make adequate watch lits - the first literal is the UIP, if any *) - let l = List.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) !learnt in - let level, is_uip = backtrack_lvl st l in + let a = Array.of_list !learnt in + Array.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) a; + (* put_high_level_atoms_first a; *) + let level, is_uip = backtrack_lvl st a in { cr_backtrack_lvl = level; - cr_learnt = l; + cr_learnt = a; cr_history = List.rev !history; cr_is_uip = is_uip; } @@ -1415,19 +1421,20 @@ module Make(Plugin : PLUGIN) let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = let proof = if st.store_proof then History cr.cr_history else Empty_premise in begin match cr.cr_learnt with - | [] -> assert false - | [fuip] -> + | [| |] -> assert false + | [|fuip|] -> assert (cr.cr_backtrack_lvl = 0 && decision_level st = 0); if fuip.neg.is_true then ( (* incompatible at level 0 *) report_unsat st (US_false confl) ) else ( - let uclause = Clause.make_removable cr.cr_learnt proof in + let uclause = Clause.make_removable_a cr.cr_learnt proof in (* no need to attach [uclause], it is true at level 0 *) enqueue_bool st fuip ~level:0 (Bcp uclause) ) - | fuip :: _ -> - let lclause = Clause.make_removable cr.cr_learnt proof in + | _ -> + let fuip = cr.cr_learnt.(0) in + let lclause = Clause.make_removable_a cr.cr_learnt proof in if Array.length lclause.atoms > 2 then ( Vec.push st.clauses_learnt lclause; (* potentially gc'able *) ); From 7673bddf8222877f002f280d9bdb97121e192057 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Feb 2019 18:03:11 -0600 Subject: [PATCH 115/182] fix: catch `E_unsat` in assume, if one adds an empty clause --- src/core/Internal.ml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 16bf0300..ca61e3f9 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1988,13 +1988,19 @@ module Make(Plugin : PLUGIN) with E_sat -> () let assume st cnf lemma = - List.iter - (fun l -> - let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make_permanent atoms (Hyp lemma) in - Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); - Vec.push st.clauses_to_add c) - cnf + try + List.iter + (fun l -> + let atoms = List.rev_map (mk_atom st) l in + let c = Clause.make_permanent atoms (Hyp lemma) in + Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); + Vec.push st.clauses_to_add c) + cnf + with + | E_unsat (US_false c) -> + st.unsat_at_0 <- Some c + | E_unsat (US_local _) -> + assert false (* assumptions should only be present in [solve] *) (* Check satisfiability *) let check_clause c = From 4127db2153bdb8f686fffcd2c775ba8718f8fca6 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 11 Feb 2019 08:44:15 -0600 Subject: [PATCH 116/182] Revert "fix: catch `E_unsat` in assume, if one adds an empty clause" This reverts commit 5d7e34584bdbfd8326fbbf7f3314d93ac79597ce. --- src/core/Internal.ml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index ca61e3f9..16bf0300 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1988,19 +1988,13 @@ module Make(Plugin : PLUGIN) with E_sat -> () let assume st cnf lemma = - try - List.iter - (fun l -> - let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make_permanent atoms (Hyp lemma) in - Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); - Vec.push st.clauses_to_add c) - cnf - with - | E_unsat (US_false c) -> - st.unsat_at_0 <- Some c - | E_unsat (US_local _) -> - assert false (* assumptions should only be present in [solve] *) + List.iter + (fun l -> + let atoms = List.rev_map (mk_atom st) l in + let c = Clause.make_permanent atoms (Hyp lemma) in + Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); + Vec.push st.clauses_to_add c) + cnf (* Check satisfiability *) let check_clause c = From 7f05da56cc702bcaf31fb9828c0fbfd3e90883e5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 11 Feb 2019 08:50:12 -0600 Subject: [PATCH 117/182] fix: cache E_unsat in direct `add_clause` functions --- src/core/Internal.ml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 16bf0300..a13f210f 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -2087,13 +2087,21 @@ module Make(Plugin : PLUGIN) in { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } - let[@inline] add_clause_a st c lemma : unit = - let c = Clause.make_a ~flags:0 c (Hyp lemma) in - add_clause_ st c + let add_clause_a st c lemma : unit = + try + let c = Clause.make_a ~flags:0 c (Hyp lemma) in + add_clause_ st c + with + | E_unsat (US_false c) -> + st.unsat_at_0 <- Some c - let[@inline] add_clause st c lemma : unit = - let c = Clause.make_permanent c (Hyp lemma) in - add_clause_ st c + let add_clause st c lemma : unit = + try + let c = Clause.make_permanent c (Hyp lemma) in + add_clause_ st c + with + | E_unsat (US_false c) -> + st.unsat_at_0 <- Some c let solve ?(assumptions=[]) (st:t) : res = cancel_until st 0; From c39431315fb678789eb244704cda78eb45c7e6af Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 15 Feb 2019 17:50:10 -0600 Subject: [PATCH 118/182] fix: fix test on dune 1.7 --- dune | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dune b/dune index d5e5e21b..4331a5ff 100644 --- a/dune +++ b/dune @@ -1,8 +1,8 @@ (alias (name runtest) - (deps README.md) + (deps README.md ./sudoku_solve.sh) (action (progn - (run mdx test %{deps}) - (diff? %{deps} %{deps}.corrected)))) + (run mdx test README.md) + (diff? README.md README.md.corrected)))) From b2cec9eaa23c9e4d4123b8afbb8936a80e2c78dc Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 11 Feb 2019 21:16:59 -0600 Subject: [PATCH 119/182] perf: use mutable flags on atoms to perform proof checking --- src/core/Internal.ml | 148 +++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 77 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index a13f210f..a1c5c338 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -302,6 +302,8 @@ module Make(Plugin : PLUGIN) let debug_a out vec = Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec + + module Set = Set.Make(struct type t=atom let compare=compare end) end (* Elements *) @@ -420,73 +422,58 @@ module Make(Plugin : PLUGIN) type formula = Formula.t type lemma = Plugin.proof - let merge = List.merge Atom.compare - let error_res_f msg = Format.kasprintf (fun s -> raise (Resolution_error s)) msg - let _c = ref 0 - let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c) + let[@inline] cleanup_ (a:atom) = Var.clear a.var (* Compute resolution of 2 clauses *) - let resolve l = - let rec aux resolved acc = function - | [] -> resolved, acc - | [a] -> resolved, a :: acc - | a :: b :: r -> - if Atom.equal a b then - aux resolved (a :: acc) r - else if Atom.equal a.neg b then - aux (a.var.pa :: resolved) acc r - else - aux resolved (a :: acc) (b :: r) + let resolve (c1:clause) (c2:clause) : atom list * atom list = + (* invariants: only atoms in [c2] are marked, and the pivot is + cleared when traversing [c1] *) + Array.iter Atom.mark c2.atoms; + let pivots = ref [] in + let l = + Array.fold_left + (fun l a -> + if Atom.seen a then l + else if Atom.seen a.neg then ( + pivots := a.var.pa :: !pivots; + cleanup_ a; + l + ) else a::l) + [] c1.atoms in - let resolved, new_clause = aux [] [] l in - resolved, List.rev new_clause - - (* Compute the set of doublons of a clause *) - let list c = List.sort Atom.compare (Array.to_list c.atoms) - - let analyze cl = - let rec aux duplicates free = function - | [] -> duplicates, free - | [ x ] -> duplicates, x :: free - | x :: ((y :: r) as l) -> - if x == y then - count duplicates (x :: free) x [y] r - else - aux duplicates (x :: free) l - and count duplicates free x acc = function - | (y :: r) when x == y -> - count duplicates free x (y :: acc) r - | l -> - aux (acc :: duplicates) free l + let l = + Array.fold_left (fun l a -> if Atom.seen a then a::l else l) l c2.atoms in - let doublons, acc = aux [] [] cl in - doublons, List.rev acc + Array.iter cleanup_ c2.atoms; + !pivots, l - let to_list c = - let cl = list c in - let dups, l = analyze cl in - let conflicts, _ = resolve l in - if dups <> [] then - Log.debug 3 "(@[sat.input-clause@ :has-duplicates@])"; - if conflicts <> [] then - Log.debug 3 "(@[sat.input-clause@ :is-tautology@])"; - cl - - (* Comparison of clauses *) - let cmp_cl c d = - let rec aux = function - | [], [] -> 0 - | a :: r, a' :: r' -> - begin match Atom.compare a a' with - | 0 -> aux (r, r') - | x -> x - end - | _ :: _ , [] -> -1 - | [], _ :: _ -> 1 + (* [find_dups c] returns a list of duplicate atoms, and the deduplicated list *) + let find_dups (c:clause) : atom list * atom list = + let res = + Array.fold_left + (fun (dups,l) a -> + if Atom.seen a then ( + a::dups, l + ) else ( + Atom.mark a; + dups, a::l + )) + ([], []) c.atoms in - aux (c, d) + Array.iter cleanup_ c.atoms; + res + + (* do [c1] and [c2] have the same lits, modulo reordering and duplicates? *) + let same_lits (c1:atom array) (c2:atom array): bool = + let subset a b = + Array.iter Atom.mark b; + let res = Array.for_all Atom.seen a in + Array.iter cleanup_ b; + res + in + subset c1 c2 && subset c2 c1 let prove conclusion = match conclusion.cpremise with @@ -550,18 +537,25 @@ module Make(Plugin : PLUGIN) let[@inline] conclusion (p:t) : clause = p - let rec chain_res (c, cl) = function + type res_step = { + rs_res: atom list; + rs_c1: clause; + rs_c2: clause; + rs_pivot: atom; + } + + let rec chain_res (c:clause) (hist:_ list) : res_step = + match hist with | d :: r -> Log.debugf 5 (fun k -> k "(@[sat.analyze.resolving@ :c1 %a@ :c2 %a@])" Clause.debug c Clause.debug d); - let dl = to_list d in - begin match resolve (merge cl dl) with - | [ a ], l -> + begin match resolve c d with + | [a], l -> begin match r with - | [] -> (l, c, d, a) + | [] -> {rs_res=l; rs_c1=c; rs_c2=d; rs_pivot=a} | _ -> let new_clause = Clause.make ~flags:c.flags l (History [c; d]) in - chain_res (new_clause, l) r + chain_res new_clause r end | _ -> error_res_f "@[<2>clause mismatch while resolving@ %a@ and %a@]" @@ -582,18 +576,18 @@ module Make(Plugin : PLUGIN) | History [] -> error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion | History [c] -> - let duplicates, res = analyze (list c) in - assert (cmp_cl res (list conclusion) = 0); - { conclusion; step = Duplicate (c, List.concat duplicates) } - | History ( c :: ([_] as r)) -> - let (l, c', d', a) = chain_res (c, to_list c) r in - assert (cmp_cl l (to_list conclusion) = 0); - { conclusion; step = Resolution (c', d', a); } - | History ( c :: r ) -> - let (l, c', d', a) = chain_res (c, to_list c) r in - conclusion.cpremise <- History [c'; d']; - assert (cmp_cl l (to_list conclusion) = 0); - { conclusion; step = Resolution (c', d', a); } + let duplicates, res = find_dups c in + assert (same_lits (Array.of_list res) conclusion.atoms); + { conclusion; step = Duplicate (c, duplicates) } + | History (c :: ([_] as r)) -> + let rs = chain_res c r in + assert (same_lits (Array.of_list rs.rs_res) conclusion.atoms); + { conclusion; step = Resolution (rs.rs_c1, rs.rs_c2, rs.rs_pivot); } + | History (c :: r) -> + let rs = chain_res c r in + conclusion.cpremise <- History [rs.rs_c1; rs.rs_c2]; + assert (same_lits (Array.of_list rs.rs_res) conclusion.atoms); + { conclusion; step = Resolution (rs.rs_c1, rs.rs_c2, rs.rs_pivot); } | Empty_premise -> raise Solver_intf.No_proof (* Proof nodes manipulation *) From e30c54e11bcec2bc24e04ed1b79403a4be32867f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 15 Feb 2019 19:06:39 -0600 Subject: [PATCH 120/182] refactor: use hyper-res steps in proofs - accelerates proof checking significantly - provide a way to expand hyper-res steps into individual resolutions (eg for the Coq backend) --- src/backend/Coq.ml | 3 +- src/backend/Dot.ml | 23 +++++--- src/core/Internal.ml | 128 ++++++++++++++++++++++++++-------------- src/core/Solver_intf.ml | 15 ++++- 4 files changed, 112 insertions(+), 57 deletions(-) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml index e4e9f56b..ffb736fd 100644 --- a/src/backend/Coq.ml +++ b/src/backend/Coq.ml @@ -134,7 +134,8 @@ module Make(S : Msat.S)(A : Arg with type hyp := S.clause let c = P.conclusion p in let () = elim_duplicate fmt clause c l in clean t fmt [c] - | P.Resolution (p1, p2, a) -> + | P.Hyper_res hr -> + let (p1, p2, a) = P.res_of_hyper_res hr in let c1 = P.conclusion p1 in let c2 = P.conclusion p2 in if resolution fmt clause c1 c2 a then clean t fmt [c1; c2] diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index ea1badcd..2ae46139 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -58,10 +58,9 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom module P = S.Proof let node_id n = Clause.name n.P.conclusion - - let res_node_id n = (node_id n) ^ "_res" - let proof_id p = node_id (P.expand p) + let res_nn_id n1 n2 = node_id n1 ^ "_" ^ node_id n2 ^ "_res" + let res_np_id n1 n2 = node_id n1 ^ "_" ^ proof_id n2 ^ "_res" let print_clause fmt c = let v = Clause.atoms c in @@ -80,9 +79,11 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom let print_edges fmt n = match P.(n.step) with - | P.Resolution (p1, p2, _) -> - print_edge fmt (res_node_id n) (proof_id p1); - print_edge fmt (res_node_id n) (proof_id p2) + | P.Hyper_res {P.hr_init; hr_steps} -> + print_edge fmt (res_np_id n hr_init) (proof_id hr_init); + List.iter + (fun (_,p2) -> print_edge fmt (res_np_id n p2) (proof_id p2)) + hr_steps; | _ -> () let table_options fmt color = @@ -129,11 +130,15 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom ((fun fmt () -> (Format.fprintf fmt "%s" (node_id n))) :: List.map (ttify A.print_atom) l); print_edge fmt (node_id n) (node_id (P.expand p)) - | P.Resolution (_, _, a) -> + | P.Hyper_res {P.hr_init; hr_steps} -> print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Resolution" "GREY" [(fun fmt () -> (Format.fprintf fmt "%s" (node_id n)))]; - print_dot_res_node fmt (res_node_id n) a; - print_edge fmt (node_id n) (res_node_id n) + print_edge fmt (node_id n) (res_np_id n hr_init); + List.iter + (fun (a,p2) -> + print_dot_res_node fmt (res_np_id n p2) a; + print_edge fmt (node_id n) (res_np_id n p2)) + hr_steps let print_node fmt n = print_contents fmt n; diff --git a/src/core/Internal.ml b/src/core/Internal.ml index a1c5c338..03511c7d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -302,6 +302,8 @@ module Make(Plugin : PLUGIN) let debug_a out vec = Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec + let debug_l out l = + List.iter (fun a -> Format.fprintf out "%a@ " debug a) l module Set = Set.Make(struct type t=atom let compare=compare end) end @@ -360,6 +362,7 @@ module Make(Plugin : PLUGIN) let[@inline] equal c1 c2 = c1.cid = c2.cid let[@inline] hash c = Hashtbl.hash c.cid let[@inline] atoms c = c.atoms + let[@inline] atoms_seq c = Sequence.of_array c.atoms let[@inline] atoms_l c = Array.to_list c.atoms let flag_attached = 0b1 @@ -424,9 +427,10 @@ module Make(Plugin : PLUGIN) let error_res_f msg = Format.kasprintf (fun s -> raise (Resolution_error s)) msg - let[@inline] cleanup_ (a:atom) = Var.clear a.var + let[@inline] clear_var_of_ (a:atom) = Var.clear a.var - (* Compute resolution of 2 clauses *) + (* Compute resolution of 2 clauses. + returns [pivots, resulting_atoms] *) let resolve (c1:clause) (c2:clause) : atom list * atom list = (* invariants: only atoms in [c2] are marked, and the pivot is cleared when traversing [c1] *) @@ -438,7 +442,7 @@ module Make(Plugin : PLUGIN) if Atom.seen a then l else if Atom.seen a.neg then ( pivots := a.var.pa :: !pivots; - cleanup_ a; + clear_var_of_ a; l ) else a::l) [] c1.atoms @@ -446,7 +450,7 @@ module Make(Plugin : PLUGIN) let l = Array.fold_left (fun l a -> if Atom.seen a then a::l else l) l c2.atoms in - Array.iter cleanup_ c2.atoms; + Array.iter clear_var_of_ c2.atoms; !pivots, l (* [find_dups c] returns a list of duplicate atoms, and the deduplicated list *) @@ -462,15 +466,15 @@ module Make(Plugin : PLUGIN) )) ([], []) c.atoms in - Array.iter cleanup_ c.atoms; + Array.iter clear_var_of_ c.atoms; res (* do [c1] and [c2] have the same lits, modulo reordering and duplicates? *) - let same_lits (c1:atom array) (c2:atom array): bool = + let same_lits (c1:atom Sequence.t) (c2:atom Sequence.t): bool = let subset a b = - Array.iter Atom.mark b; - let res = Array.for_all Atom.seen a in - Array.iter cleanup_ b; + Sequence.iter Atom.mark b; + let res = Sequence.for_all Atom.seen a in + Sequence.iter clear_var_of_ b; res in subset c1 c2 && subset c2 c1 @@ -533,7 +537,12 @@ module Make(Plugin : PLUGIN) | Assumption | Lemma of lemma | Duplicate of t * atom list - | Resolution of t * t * atom + | Hyper_res of hyper_res_step + + and hyper_res_step = { + hr_init: t; + hr_steps: (atom * t) list; (* list of pivot+clause to resolve against [init] *) + } let[@inline] conclusion (p:t) : clause = p @@ -544,31 +553,51 @@ module Make(Plugin : PLUGIN) rs_pivot: atom; } - let rec chain_res (c:clause) (hist:_ list) : res_step = - match hist with - | d :: r -> - Log.debugf 5 - (fun k -> k "(@[sat.analyze.resolving@ :c1 %a@ :c2 %a@])" Clause.debug c Clause.debug d); - begin match resolve c d with - | [a], l -> - begin match r with - | [] -> {rs_res=l; rs_c1=c; rs_c2=d; rs_pivot=a} - | _ -> - let new_clause = Clause.make ~flags:c.flags l (History [c; d]) in - chain_res new_clause r - end - | _ -> - error_res_f "@[<2>clause mismatch while resolving@ %a@ and %a@]" - Clause.debug c Clause.debug d - end - | _ -> - error_res_f "bad history" + (* find pivots for resolving [l] with [init], and also return + the atoms of the conclusion *) + let find_pivots (init:clause) (l:clause list) : _ * (atom * t) list = + Log.debugf 15 + (fun k->k "(@[proof.find-pivots@ :init %a@ :l %a@])" + Clause.debug init (Format.pp_print_list Clause.debug) l); + Array.iter Atom.mark init.atoms; + let steps = + List.map + (fun c -> + let pivot = + match + Sequence.of_array c.atoms + |> Sequence.filter (fun a -> Atom.seen (Atom.neg a)) + |> Sequence.to_list + with + | [a] -> a + | [] -> + error_res_f "(@[proof.expand.pivot_missing@ %a@])" Clause.debug c + | pivots -> + error_res_f "(@[proof.expand.multiple_pivots@ %a@ :pivots %a@])" + Clause.debug c Atom.debug_l pivots + in + Array.iter Atom.mark c.atoms; (* add atoms to result *) + clear_var_of_ pivot; + Atom.abs pivot, c) + l + in + (* cleanup *) + let res = ref [] in + let cleanup_a_ a = + if Atom.seen a then ( + res := a :: !res; + clear_var_of_ a + ) + in + Array.iter cleanup_a_ init.atoms; + List.iter (fun c -> Array.iter cleanup_a_ c.atoms) l; + !res, steps let expand conclusion = Log.debugf 5 (fun k -> k "(@[sat.proof.expand@ @[%a@]@])" Clause.debug conclusion); match conclusion.cpremise with | Lemma l -> - {conclusion; step = Lemma l; } + { conclusion; step = Lemma l; } | Local -> { conclusion; step = Assumption; } | Hyp l -> @@ -577,40 +606,51 @@ module Make(Plugin : PLUGIN) error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion | History [c] -> let duplicates, res = find_dups c in - assert (same_lits (Array.of_list res) conclusion.atoms); + assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); { conclusion; step = Duplicate (c, duplicates) } | History (c :: ([_] as r)) -> - let rs = chain_res c r in - assert (same_lits (Array.of_list rs.rs_res) conclusion.atoms); - { conclusion; step = Resolution (rs.rs_c1, rs.rs_c2, rs.rs_pivot); } + let res, steps = find_pivots c r in + assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); + { conclusion; step = Hyper_res { hr_init=c; hr_steps=steps; }; } | History (c :: r) -> - let rs = chain_res c r in - conclusion.cpremise <- History [rs.rs_c1; rs.rs_c2]; - assert (same_lits (Array.of_list rs.rs_res) conclusion.atoms); - { conclusion; step = Resolution (rs.rs_c1, rs.rs_c2, rs.rs_pivot); } + let res, steps = find_pivots c r in + assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); + { conclusion; step = Hyper_res {hr_init=c; hr_steps=steps}; } | Empty_premise -> raise Solver_intf.No_proof + let rec res_of_hyper_res (hr: hyper_res_step) : _ * _ * atom = + let {hr_init=c1; hr_steps=l} = hr in + match l with + | [] -> assert false + | [a, c2] -> c1, c2, a (* done *) + | (a,c2) :: steps' -> + (* resolve [c1] with [c2], then resolve that against [steps] *) + let pivots, l = resolve c1 c2 in + assert (match pivots with [a'] -> Atom.equal a a' | _ -> false); + let c_1_2 = Clause.make_removable l (History [c1; c2]) in + res_of_hyper_res {hr_init=c_1_2; hr_steps=steps'} + (* Proof nodes manipulation *) let is_leaf = function | Hypothesis _ | Assumption | Lemma _ -> true | Duplicate _ - | Resolution _ -> false + | Hyper_res _ -> false let parents = function | Hypothesis _ | Assumption | Lemma _ -> [] | Duplicate (p, _) -> [p] - | Resolution (p, p', _) -> [p; p'] + | Hyper_res {hr_init; hr_steps} -> hr_init :: List.map snd hr_steps let expl = function | Hypothesis _ -> "hypothesis" | Assumption -> "assumption" | Lemma _ -> "lemma" | Duplicate _ -> "duplicate" - | Resolution _ -> "resolution" + | Hyper_res _ -> "hyper-resolution" (* Compute unsat-core TODO: replace visited bool by a int unique to each call @@ -658,9 +698,9 @@ module Make(Plugin : PLUGIN) begin match node.step with | Duplicate (p1, _) -> Stack.push (Enter p1) s - | Resolution (p1, p2, _) -> - Stack.push (Enter p2) s; - Stack.push (Enter p1) s + | Hyper_res {hr_init=p1; hr_steps=l} -> + List.iter (fun (_,p2) -> Stack.push (Enter p2) s) l; + Stack.push (Enter p1) s; | Hypothesis _ | Assumption | Lemma _ -> () end end; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 4f50a6e7..8ed3d9f1 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -286,9 +286,12 @@ module type PROOF = sig | Duplicate of t * atom list (** The conclusion is obtained by eliminating multiple occurences of the atom in the conclusion of the provided proof. *) - | Resolution of t * t * atom - (** The conclusion can be deduced by performing a resolution between the conclusions - of the two given proofs. The atom on which to perform the resolution is also given. *) + | Hyper_res of hyper_res_step + + and hyper_res_step = { + hr_init: t; + hr_steps: (atom * t) list; (* list of pivot+clause to resolve against [init] *) + } (** {3 Proof building functions} *) @@ -303,6 +306,12 @@ module type PROOF = sig val prove_atom : atom -> t option (** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *) + val res_of_hyper_res : hyper_res_step -> t * t * atom + (** Turn an hyper resolution step into a resolution step. + The conclusion can be deduced by performing a resolution between the conclusions + of the two given proofs. + The atom on which to perform the resolution is also given. *) + (** {3 Proof Nodes} *) val parents : step -> t list From 92ca9c328f730d43460106989ebf489a44873bea Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 12:16:00 -0600 Subject: [PATCH 121/182] refactor(main): catch resolution errors properly; style --- src/main/main.ml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/main.ml b/src/main/main.ml index 8822b501..be123efb 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -48,14 +48,16 @@ module Process() = struct let t' = Sys.time () -. t in Format.printf "Sat (%f/%f)@." t t' | S.Unsat state -> - if !p_check then begin + if !p_check then ( let p = state.Msat.get_proof () in S.Proof.check p; - if !p_dot_proof <> "" then begin - let fmt = Format.formatter_of_out_channel (open_out !p_dot_proof) in - D.pp fmt p - end - end; + if !p_dot_proof <> "" then ( + let oc = open_out !p_dot_proof in + let fmt = Format.formatter_of_out_channel oc in + Format.fprintf fmt "%a@?" D.pp p; + flush oc; close_out_noerr oc; + ) + ); let t' = Sys.time () -. t in Format.printf "Unsat (%f/%f)@." t t' end @@ -171,4 +173,7 @@ let () = | Incorrect_model -> Format.printf "Internal error : incorrect *sat* model@."; exit 4 + | S.Proof.Resolution_error msg -> + Format.printf "Internal error: incorrect *unsat* proof:\n%s@." msg; + exit 5 From 2e2bbfd4d0fff2a0c06fbb93a23e202953b5cd1a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 12:16:19 -0600 Subject: [PATCH 122/182] fix(proof.check): ensure that the proof is an empty clause --- src/core/Internal.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 03511c7d..bb2d52b7 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -712,7 +712,11 @@ module Make(Plugin : PLUGIN) Stack.push (Enter p) s; fold_aux s h f acc - let check p = fold (fun () _ -> ()) () p + let check (p:t) = + if Array.length p.atoms > 0 then ( + error_res_f "@[<2>Proof.check: non empty conclusion for claus@ %a@]" Clause.debug p; + ); + fold (fun () _ -> ()) () p end type proof = Proof.t From cdb52ee757b02ae5f517a37509754240f23c9023 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 12:16:41 -0600 Subject: [PATCH 123/182] fix(proof): unsat conflicts now call `Proof.prove_unsat` this does the last bit of proof recording when a conflict is reached during propagation --- src/core/Internal.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index bb2d52b7..e6f73f72 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -521,12 +521,11 @@ module Make(Plugin : PLUGIN) ) let prove_atom a = - if (a.is_true && a.var.v_level = 0) then + if a.is_true && a.var.v_level = 0 then Some (set_atom_proof a) else None - (* Interface exposed *) type t = clause and proof_node = { conclusion : clause; @@ -652,9 +651,7 @@ module Make(Plugin : PLUGIN) | Duplicate _ -> "duplicate" | Hyper_res _ -> "hyper-resolution" - (* Compute unsat-core - TODO: replace visited bool by a int unique to each call - of unsat_core, so that the cleanup can be removed ? *) + (* Compute unsat-core by accumulating the leaves *) let unsat_core proof = let rec aux res acc = function | [] -> res, acc @@ -1187,10 +1184,13 @@ module Make(Plugin : PLUGIN) in multiple places (adding new clauses, or solving for instance). *) let report_unsat st (us:unsat_cause) : _ = Log.debugf info (fun k -> k "(@[sat.unsat-conflict@ %a@])" pp_unsat_cause us); - begin match us with - | US_false c -> st.unsat_at_0 <- Some c; - | _ -> () - end; + let us = match us with + | US_false c -> + let c = if st.store_proof then Proof.prove_unsat c else c in + st.unsat_at_0 <- Some c; + US_false c + | _ -> us + in raise (E_unsat us) (* Simplification of boolean propagation reasons. From 596034d16a4470daf795d1d9d011258da49d2666 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 12:33:43 -0600 Subject: [PATCH 124/182] fix(dot): proper labelling of hyper-res nodes --- src/backend/Dot.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 2ae46139..0aa2cf1b 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -79,11 +79,12 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom let print_edges fmt n = match P.(n.step) with - | P.Hyper_res {P.hr_init; hr_steps} -> - print_edge fmt (res_np_id n hr_init) (proof_id hr_init); + | P.Hyper_res {P.hr_steps=[];_} -> () (* NOTE: should never happen *) + | P.Hyper_res {P.hr_init; hr_steps=((_,p0)::_) as l} -> + print_edge fmt (res_np_id n p0) (proof_id hr_init); List.iter (fun (_,p2) -> print_edge fmt (res_np_id n p2) (proof_id p2)) - hr_steps; + l; | _ -> () let table_options fmt color = @@ -130,15 +131,14 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom ((fun fmt () -> (Format.fprintf fmt "%s" (node_id n))) :: List.map (ttify A.print_atom) l); print_edge fmt (node_id n) (node_id (P.expand p)) - | P.Hyper_res {P.hr_init; hr_steps} -> + | P.Hyper_res {P.hr_steps=l; _} -> print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Resolution" "GREY" [(fun fmt () -> (Format.fprintf fmt "%s" (node_id n)))]; - print_edge fmt (node_id n) (res_np_id n hr_init); List.iter (fun (a,p2) -> print_dot_res_node fmt (res_np_id n p2) a; print_edge fmt (node_id n) (res_np_id n p2)) - hr_steps + l let print_node fmt n = print_contents fmt n; From b1c687faac22eaea466bef4cd87fd8dc8aa32b7a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 16:55:59 -0600 Subject: [PATCH 125/182] chore: make default target `build`, not `dev` --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b24f052f..a8967cda 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,11 @@ OPTS= -j $(J) LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) -dev: build-dev test - build: @dune build $(OPTS) @install --profile=release +dev: build-dev test + build-dev: @dune build $(OPTS) @install From c2a6c2d47bed3664ce9ee7af6f60b496b8341442 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 16:58:29 -0600 Subject: [PATCH 126/182] refactor(propagate): make propagation clause lazy --- src/core/Internal.ml | 28 ++++++++++++++++++---------- src/core/Msat.ml | 2 +- src/core/Solver_intf.ml | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e6f73f72..9ad1838d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -68,6 +68,7 @@ module Make(Plugin : PLUGIN) and reason = | Decision | Bcp of clause + | Bcp_lazy of clause lazy_t | Semantic (* TODO: remove, replace with user-provided proof trackng device? @@ -282,6 +283,8 @@ module Make(Plugin : PLUGIN) Format.fprintf fmt "@@%d" n | n, Some Bcp c -> Format.fprintf fmt "->%d/%s" n (name_of_clause c) + | n, Some (Bcp_lazy _) -> + Format.fprintf fmt "->%d/" n | n, Some Semantic -> Format.fprintf fmt "::%d" n @@ -1061,7 +1064,7 @@ module Make(Plugin : PLUGIN) let l = a.var.v_level in if l = 0 then ( match a.var.reason with - | Some (Bcp cl) -> + | Some (Bcp cl | Bcp_lazy (lazy cl)) -> partition_aux trues unassigned falses (cl :: history) (i + 1) (* A var false at level 0 can be eliminated from the clause, but we need to kepp in mind that we used another clause to simplify it. *) @@ -1699,21 +1702,26 @@ module Make(Plugin : PLUGIN) | Solver_intf.Eval l -> let a = mk_atom st f in enqueue_semantic st a l - | Solver_intf.Consequence (causes, proof) -> - let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) causes in - if List.exists Atom.is_true l then ( - invalid_argf "slice.acts_propagate: Consequence should contain only true literals" - ); + | Solver_intf.Consequence mk_expl -> let p = mk_atom st f in if Atom.is_true p then () else if Atom.is_false p then ( + let lits, proof = mk_expl() in + let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in + if List.exists Atom.is_true l then ( + invalid_argf "slice.acts_propagate: Consequence should contain only true literals" + ); let c = Clause.make_removable (p :: l) (Lemma proof) in raise_notrace (Th_conflict c) ) else ( insert_var_order st p.var; - let level = List.fold_left (fun acc a -> max acc a.var.v_level) 0 l in - let c = Clause.make_removable (p :: l) (Lemma proof) in - enqueue_bool st p ~level (Bcp c) + let c = lazy ( + let lits, proof = mk_expl () in + let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in + Clause.make_removable (p :: l) (Lemma proof) + ) in + let level = decision_level st in + enqueue_bool st p ~level (Bcp_lazy c) ) let[@specialise] acts_iter st ~full head f : unit = @@ -2107,7 +2115,7 @@ module Make(Plugin : PLUGIN) assert (Atom.equal first @@ List.hd core); let proof_of (a:atom) = match Atom.reason a with | Some (Decision | Semantic) -> Clause.make_removable [a] Local - | Some (Bcp c) -> c + | Some (Bcp c | Bcp_lazy (lazy c)) -> c | None -> assert false in let other_lits = List.filter (fun a -> not (Atom.equal a first)) core in diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 156da28e..0aee8e57 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -39,7 +39,7 @@ type ('term, 'formula, 'value) assumption = ('term, 'formula, 'value) Solver_int type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.reason = | Eval of 'term list - | Consequence of 'formula list * 'proof + | Consequence of (unit -> 'formula list * 'proof) type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) Solver_intf.acts = { acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 8ed3d9f1..e7d48900 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -76,7 +76,7 @@ type ('term, 'formula, 'value) assumption = type ('term, 'formula, 'proof) reason = | Eval of 'term list (** The formula can be evalutaed using the terms in the list *) - | Consequence of 'formula list * 'proof + | Consequence of (unit -> 'formula list * 'proof) (** [Consequence (l, p)] means that the formulas in [l] imply the propagated formula [f]. The proof should be a proof of the clause "[l] implies [f]". *) From ed64e6b69d172d51dc94102c9e5beea7ff0d9a3d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 17:51:14 -0600 Subject: [PATCH 127/182] chore: try to fix the mdx test; cleanup makefile --- Makefile | 18 ++---------------- README.md | 6 ++++-- dune | 3 ++- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index a8967cda..c8f09a05 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,9 @@ # copyright (c) 2014, guillaume bury # copyright (c) 2017, simon cruanes -BIN=main.native -TEST_BIN=tests/test_api.native - -NAME=msat J?=3 -TIMEOUT?=30 -TARGETS=src/bin/main.exe OPTS= -j $(J) -LIB=$(addprefix $(NAME), .cma .cmxa .cmxs) - build: @dune build $(OPTS) @install --profile=release @@ -20,16 +12,10 @@ dev: build-dev test build-dev: @dune build $(OPTS) @install -test: +test: build-dev @echo "run tests…" @OCAMLRUNPARAM=b dune runtest --force --no-buffer -enable_log: - cd src/core; ln -sf log_real.ml log.ml - -disable_log: - cd src/core; ln -sf log_dummy.ml log.ml - clean: @dune clean @@ -59,4 +45,4 @@ WATCH=all watch: @dune build @all -w -.PHONY: clean doc all bench install uninstall remove reinstall enable_log disable_log bin test +.PHONY: clean doc all bench install uninstall remove reinstall bin test diff --git a/README.md b/README.md index 4dc23a6d..a35b3d59 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ using the `msat.sat` library. It can be loaded as shown in the following code : ```ocaml +# #require "msat";; # #require "msat.sat";; # #print_depth 0;; (* do not print details *) ``` @@ -147,7 +148,8 @@ it into clauses using `make_cnf`: The directory `src/sudoku/` contains a simple Sudoku solver that uses the interface `Msat.Make_cdcl_t`. In essence, it implements the logical theory `CDCL(Sudoku)`. -The script `sudoku_solve.sh` compiles and runs the solver. +The script `sudoku_solve.sh` compiles and runs the solver, +as does `dune exec src/sudoku/sudoku_solve.exe`. It's able to parse sudoku grids denoted as 81 integers (see `tests/sudoku/sudoku.txt` for example). @@ -156,7 +158,7 @@ Here is a sample grid and the output from the solver (in roughly .5s): ```sh $ echo '..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9' > sudoku.txt -$ ./sudoku_solve.sh sudoku.txt +$ dune exec src/sudoku/sudoku_solve.exe -- sudoku.txt ... ######################### solve grid: diff --git a/dune b/dune index 4331a5ff..18913452 100644 --- a/dune +++ b/dune @@ -1,7 +1,8 @@ (alias (name runtest) - (deps README.md ./sudoku_solve.sh) + (deps README.md) + (locks test) (action (progn (run mdx test README.md) (diff? README.md README.md.corrected)))) From 28afd6eefe64fad75f56193012bdfa9ea36af033 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 19:17:34 -0600 Subject: [PATCH 128/182] fix lazy propagation --- src/core/Internal.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 9ad1838d..7ff26748 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -495,7 +495,7 @@ module Make(Plugin : PLUGIN) in assert (a.var.v_level >= 0); match (a.var.reason) with - | Some (Bcp c) -> + | Some (Bcp c | Bcp_lazy (lazy c)) -> Log.debugf 5 (fun k->k "(@[proof.analyze.clause@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c); if Array.length c.atoms = 1 then ( Log.debugf 5 (fun k -> k "(@[proof.analyze.old-reason@ %a@])" Atom.debug a); @@ -1203,7 +1203,7 @@ module Make(Plugin : PLUGIN) need to rebuild a clause with correct history, in order to be able to build a correct proof at the end of proof search. *) let simpl_reason : reason -> reason = function - | (Bcp cl) as r -> + | (Bcp cl | Bcp_lazy (lazy cl)) as r -> let l, history = partition cl.atoms in begin match l with | [_] -> @@ -1229,7 +1229,7 @@ module Make(Plugin : PLUGIN) Clause.debug cl); assert false end - | r -> r + | (Decision | Semantic) as r -> r (* Boolean propagation. Wrapper function for adding a new propagated formula. *) @@ -1394,8 +1394,8 @@ module Make(Plugin : PLUGIN) if q.var.v_level <= 0 then ( assert (q.neg.is_true); match q.var.reason with - | Some Bcp cl -> history := cl :: !history - | _ -> assert false + | Some (Bcp cl | Bcp_lazy (lazy cl)) -> history := cl :: !history + | Some (Decision | Semantic) | None -> assert false ); if not (Var.marked q.var) then ( Var.mark q.var; @@ -1437,11 +1437,11 @@ module Make(Plugin : PLUGIN) assert (n > 0); learnt := p.neg :: !learnt; c := None - | n, Some Bcp cl -> + | n, Some (Bcp cl | Bcp_lazy (lazy cl)) -> assert (n > 0); assert (p.var.v_level >= conflict_level); c := Some cl - | _ -> assert false + | _, (None | Some Decision) -> assert false done; Vec.iter Var.clear to_unmark; Vec.clear to_unmark; @@ -1842,7 +1842,7 @@ module Make(Plugin : PLUGIN) match Atom.reason a' with | Some Semantic -> () | Some Decision -> core := a' :: !core - | Some (Bcp c) -> + | Some (Bcp c | Bcp_lazy (lazy c)) -> Array.iter (fun a -> let v = a.var in @@ -1851,7 +1851,7 @@ module Make(Plugin : PLUGIN) Var.mark v; )) c.atoms - | _ -> () + | None -> () ); end; decr idx From 591298e2962fc8bf59e816f1d7305cf015c9b622 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 19:17:52 -0600 Subject: [PATCH 129/182] refactor: remove dimacs backend --- src/backend/Dimacs.ml | 138 ----------------------------------------- src/backend/Dimacs.mli | 55 ---------------- 2 files changed, 193 deletions(-) delete mode 100644 src/backend/Dimacs.ml delete mode 100644 src/backend/Dimacs.mli diff --git a/src/backend/Dimacs.ml b/src/backend/Dimacs.ml deleted file mode 100644 index cb17851d..00000000 --- a/src/backend/Dimacs.ml +++ /dev/null @@ -1,138 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -open Msat - -module type ARG = sig - type clause - val lits : clause -> int list -end - -module type S = sig - type st - - type clause - (** The type of clauses *) - - val export : - st -> - Format.formatter -> - hyps:clause Vec.t -> - history:clause Vec.t -> - local:clause Vec.t -> - unit - - val export_icnf : - Format.formatter -> - hyps:clause Vec.t -> - history:clause Vec.t -> - local:clause Vec.t -> - unit - -end - -module Make(St : Msat.S)(A: ARG with type clause = St.clause) = struct - type st = St.t - - let pp_dimacs fmt c = - let lits = A.lits c in - List.iter (fun p -> Format.fprintf fmt "%d " p) lits; - Format.fprintf fmt "0" - - - (* Dimacs & iCNF export *) - let export_vec name fmt vec = - Format.fprintf fmt "c %s@,%a@," name (Vec.pp ~sep:"" pp_dimacs) vec - - let export_assumption fmt vec = - Format.fprintf fmt "c Local assumptions@,a %a@," pp_dimacs vec - - let export_icnf_aux r name map_filter fmt vec = - let aux fmt _ = - for i = !r to (Vec.size vec) - 1 do - let x = Vec.get vec i in - match map_filter x with - | None -> () - | Some _ -> Format.fprintf fmt "%a@," pp_dimacs (Vec.get vec i) - done; - r := Vec.size vec - in - Format.fprintf fmt "c %s@,%a" name aux vec - - (* TODO - let map_filter_learnt c = - match P.Clause.premise c with - | St.Hyp | St.Local -> assert false - | St.Lemma _ -> Some c - | St.History l -> - begin match l with - | [] -> assert false - | d :: _ -> - begin match St.Clause.premise d with - | St.Lemma _ -> Some d - | St.Hyp | St.Local | St.History _ -> None - end - end - - let filter_vec learnt = - let lemmas = Vec.create() in - Vec.iter (fun c -> - match map_filter_learnt c with - | None -> () - | Some d -> Vec.push lemmas d - ) learnt; - lemmas - *) - - let export st fmt ~hyps ~history ~local = - assert false - (* FIXME - assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); - (* Learnt clauses, then filtered to only keep only - the theory lemmas; all other learnt clauses should be logical - consequences of the rest. *) - let lemmas = filter_vec history in - let lemmas = history in - (* Local assertions *) - assert (Vec.for_all (fun c -> St.Local = St.Clause.premise c) local); - (* Number of atoms and clauses *) - let n = St.nb_elt st in - let m = Vec.size local + Vec.size hyps + Vec.size lemmas in - Format.fprintf fmt - "@[p cnf %d %d@,%a%a%a@]@." n m - (export_vec "Local assumptions") local - (export_vec "Hypotheses") hyps - (export_vec "Lemmas") lemmas - *) - - (* Refs to remember what portion of a problem has been printed *) - let icnf_hyp = ref 0 - let icnf_lemmas = ref 0 - - let export_icnf fmt ~hyps ~history ~local = - assert false - (* FIXME - assert (Vec.for_all (fun c -> St.Clause.premise c = St.Hyp) hyps); - let lemmas = history in - (* Local assertions *) - let l = List.map - (fun c -> match St.Clause.premise c, St.Clause.atoms c with - | St.Local, [| a |] -> a - | _ -> assert false) - (Vec.to_list local) - in - let local = St.Clause.make l St.Local in - (* Number of atoms and clauses *) - Format.fprintf fmt - "@[%s@,%a%a%a@]@." - (if !icnf_hyp = 0 && !icnf_lemmas = 0 then "p inccnf" else "") - (export_icnf_aux icnf_hyp "Hypotheses" (fun x -> Some x)) hyps - (export_icnf_aux icnf_lemmas "Lemmas" map_filter_learnt) lemmas - export_assumption local - *) - -end - diff --git a/src/backend/Dimacs.mli b/src/backend/Dimacs.mli deleted file mode 100644 index aada5010..00000000 --- a/src/backend/Dimacs.mli +++ /dev/null @@ -1,55 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Dimacs backend for problems - - This module provides functiosn to export problems to the dimacs and - iCNF formats. -*) - -open Msat - -module type ARG = sig - type clause - val lits : clause -> int list -end - -module type S = sig - type st - - type clause - (** The type of clauses *) - - val export : - st -> - Format.formatter -> - hyps:clause Vec.t -> - history:clause Vec.t -> - local:clause Vec.t -> - unit - (** Export the given clause vectors to the dimacs format. - The arguments should be transmitted directly from the corresponding - function of the {Internal} module. *) - - val export_icnf : - Format.formatter -> - hyps:clause Vec.t -> - history:clause Vec.t -> - local:clause Vec.t -> - unit - (** Export the given clause vectors to the dimacs format. - The arguments should be transmitted directly from the corresponding - function of the {Internal} module. - This function may be called multiple times in order to add - new clauses (and new local hyps) to the problem. - *) - -end - -module Make(St: Msat.S)(A: ARG with type clause = St.clause) - : S with type clause := St.clause and type st = St.t -(** Functor to create a module for exporting probems to the dimacs (& iCNF) formats. *) - From efe93c364702213d875d9c30822a18f1b35102ac Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Feb 2019 20:41:37 -0600 Subject: [PATCH 130/182] feat: `Proof.check_empty_conclusion` as a separate function this allows the validity checking of proofs of 0-level lits --- src/core/Internal.ml | 7 ++++--- src/core/Solver_intf.ml | 6 +++++- src/main/main.ml | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 7ff26748..12259a04 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -712,11 +712,12 @@ module Make(Plugin : PLUGIN) Stack.push (Enter p) s; fold_aux s h f acc - let check (p:t) = + let check_empty_conclusion (p:t) = if Array.length p.atoms > 0 then ( error_res_f "@[<2>Proof.check: non empty conclusion for claus@ %a@]" Clause.debug p; - ); - fold (fun () _ -> ()) () p + ) + + let check (p:t) = fold (fun () _ -> ()) () p end type proof = Proof.t diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index e7d48900..e099485f 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -349,8 +349,12 @@ module type PROOF = sig (** {3 Misc} *) + val check_empty_conclusion : t -> unit + (** Check that the proof's conclusion is the empty clause, + @raise Resolution_error otherwise *) + val check : t -> unit - (** Check the contents of a proof. Mainly for internal use *) + (** Check the contents of a proof. Mainly for internal use. *) module Tbl : Hashtbl.S with type key = t end diff --git a/src/main/main.ml b/src/main/main.ml index be123efb..a8988f48 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -50,6 +50,7 @@ module Process() = struct | S.Unsat state -> if !p_check then ( let p = state.Msat.get_proof () in + S.Proof.check_empty_conclusion p; S.Proof.check p; if !p_dot_proof <> "" then ( let oc = open_out !p_dot_proof in From 5bfd975ed3ada41f19b3e6cbcc259e149352b0d7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 21 Feb 2019 17:50:35 -0600 Subject: [PATCH 131/182] refactor: move constant parameters outside of the solver --- src/core/Internal.ml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 12259a04..bb517489 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -822,16 +822,16 @@ module Make(Plugin : PLUGIN) mutable clause_incr : float; (* increment for clauses' activity *) - - mutable restart_first : int; - (* intial restart limit, default 100 *) - - mutable learntsize_factor : float; - (* initial limit for the number of learnt clauses, 1/3 of initial - number of clauses by default *) } type solver = t + (* intial restart limit *) + let restart_first = 100 + + (* initial limit for the number of learnt clauses, 1/3 of initial + number of clauses by default *) + let learntsize_factor = 1. /. 3. + (* Starting environment. *) let create_ ~st ~store_proof (th:theory) : t = { st; th; @@ -856,10 +856,6 @@ module Make(Plugin : PLUGIN) var_incr = 1.; clause_incr = 1.; store_proof; - - restart_first = 100; - - learntsize_factor = 1. /. 3. ; } let create ?(store_proof=true) ?(size=`Big) (th:theory) : t = @@ -2004,8 +2000,8 @@ module Make(Plugin : PLUGIN) check_unsat_ st; try flush_clauses st; (* add initial clauses *) - let n_of_conflicts = ref (float_of_int st.restart_first) in - let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. st.learntsize_factor) in + let n_of_conflicts = ref (float_of_int restart_first) in + let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. learntsize_factor) in while true do begin try search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) From 34f64d2d699f9ab87ed2962544f621398e3e448f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 21 Feb 2019 17:51:52 -0600 Subject: [PATCH 132/182] detail: sudoku solver prints total time --- src/sudoku/sudoku_solve.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index ffbba32c..389fce6a 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -308,7 +308,9 @@ let solve_file file = | Some g' -> Format.printf "@[@[<2>solution (in %.3fs):@ %a@]@,###################@]@." (Sys.time()-.start) Grid.pp g') - grids + grids; + Format.printf "@.solved %d grids (in %.3fs)@." (List.length grids) (Sys.time()-.start); + () let () = Fmt.set_color_default true; From 7a050df9029099e1dd5e7a69061563723f695977 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 25 Feb 2019 13:41:00 -0600 Subject: [PATCH 133/182] fix readme to account for new sudoku output --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a35b3d59..9ceb0d81 100644 --- a/README.md +++ b/README.md @@ -184,4 +184,5 @@ solve grid: 863745219 ################### +... ``` From 2aa9b3d4bc88b43399727447e2f81f2e2d56132e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 26 Feb 2019 19:03:47 -0600 Subject: [PATCH 134/182] refactor: modifications asked by @gbury in review --- src/backend/Dot.ml | 2 +- src/core/Internal.ml | 6 +----- src/core/Solver_intf.ml | 10 ++++++++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 0aa2cf1b..9097cc45 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -79,7 +79,7 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom let print_edges fmt n = match P.(n.step) with - | P.Hyper_res {P.hr_steps=[];_} -> () (* NOTE: should never happen *) + | P.Hyper_res {P.hr_steps=[];_} -> assert false (* NOTE: should never happen *) | P.Hyper_res {P.hr_init; hr_steps=((_,p0)::_) as l} -> print_edge fmt (res_np_id n p0) (proof_id hr_init); List.iter diff --git a/src/core/Internal.ml b/src/core/Internal.ml index bb517489..b6da8fce 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -610,10 +610,6 @@ module Make(Plugin : PLUGIN) let duplicates, res = find_dups c in assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); { conclusion; step = Duplicate (c, duplicates) } - | History (c :: ([_] as r)) -> - let res, steps = find_pivots c r in - assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); - { conclusion; step = Hyper_res { hr_init=c; hr_steps=steps; }; } | History (c :: r) -> let res, steps = find_pivots c r in assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); @@ -714,7 +710,7 @@ module Make(Plugin : PLUGIN) let check_empty_conclusion (p:t) = if Array.length p.atoms > 0 then ( - error_res_f "@[<2>Proof.check: non empty conclusion for claus@ %a@]" Clause.debug p; + error_res_f "@[<2>Proof.check: non empty conclusion for clause@ %a@]" Clause.debug p; ) let check (p:t) = fold (fun () _ -> ()) () p diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index e099485f..e6ffffb1 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -79,6 +79,16 @@ type ('term, 'formula, 'proof) reason = | Consequence of (unit -> 'formula list * 'proof) (** [Consequence (l, p)] means that the formulas in [l] imply the propagated formula [f]. The proof should be a proof of the clause "[l] implies [f]". + + invariant: in [Consequence (l,p)], all elements of [l] must be true in + the current trail. + + note on lazyiness: the justification is suspended (using [unit -> …]) + to avoid potentially costly computations that might never be used + if this literal is backtracked without participating in a conflict. + However, if the theory isn't robust wrt backtracking an subsequent changes, + it can be easier to produce the explanation eagerly when + propagating, and then use [Consequence (fun () -> expl, proof)]. *) (** The type of reasons for propagations of a formula [f]. *) From 7e9693348aa12b9e6690715bedc030e8877e4d18 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 26 Feb 2019 19:10:06 -0600 Subject: [PATCH 135/182] fix: ensure that the mdx test doesn't run too early --- dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dune b/dune index 18913452..79a54003 100644 --- a/dune +++ b/dune @@ -1,7 +1,7 @@ (alias (name runtest) - (deps README.md) + (deps README.md src/core/msat.cma src/sat/msat_sat.cma src/sudoku/sudoku_solve.exe) (locks test) (action (progn (run mdx test README.md) From d3702d1e1f76cdc4a664926093ac2150940f9f7b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 1 Mar 2019 19:13:24 -0600 Subject: [PATCH 136/182] refactor: fix issues found by @gbury --- src/core/Internal.ml | 19 ++++++++++++++++--- src/core/Solver_intf.ml | 13 +++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index b6da8fce..fad444d3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1691,6 +1691,14 @@ module Make(Plugin : PLUGIN) Log.debugf 5 (fun k->k "(@[@{sat.th.raise-conflict@}@ %a@])" Clause.debug c); raise_notrace (Th_conflict c) + let check_consequence_lits_false_ l : unit = + match List.find Atom.is_true l with + | a -> + invalid_argf + "slice.acts_propagate:@ Consequence should contain only true literals, but %a isn't" + Atom.debug (Atom.neg a) + | exception Not_found -> () + let acts_propagate (st:t) f = function | Solver_intf.Eval l -> let a = mk_atom st f in @@ -1701,9 +1709,7 @@ module Make(Plugin : PLUGIN) else if Atom.is_false p then ( let lits, proof = mk_expl() in let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in - if List.exists Atom.is_true l then ( - invalid_argf "slice.acts_propagate: Consequence should contain only true literals" - ); + check_consequence_lits_false_ l; let c = Clause.make_removable (p :: l) (Lemma proof) in raise_notrace (Th_conflict c) ) else ( @@ -1711,6 +1717,13 @@ module Make(Plugin : PLUGIN) let c = lazy ( let lits, proof = mk_expl () in let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in + (* note: we can check that invariant here in the [lazy] block, + as conflict analysis will run in an environment where + the literals should be true anyway, since it's an extension of the + current trail + (otherwise the propagated lit would have been backtracked and + discarded already.) *) + check_consequence_lits_false_ l; Clause.make_removable (p :: l) (Lemma proof) ) in let level = decision_level st in diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index e6ffffb1..5815e659 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -80,15 +80,20 @@ type ('term, 'formula, 'proof) reason = (** [Consequence (l, p)] means that the formulas in [l] imply the propagated formula [f]. The proof should be a proof of the clause "[l] implies [f]". - invariant: in [Consequence (l,p)], all elements of [l] must be true in + invariant: in [Consequence (fun () -> l,p)], all elements of [l] must be true in the current trail. - note on lazyiness: the justification is suspended (using [unit -> …]) + {b note} on lazyiness: the justification is suspended (using [unit -> …]) to avoid potentially costly computations that might never be used if this literal is backtracked without participating in a conflict. - However, if the theory isn't robust wrt backtracking an subsequent changes, + Therefore the function that produces [(l,p)] needs only be safe in + trails (partial models) that are conservative extensions of the current + trail. + If the theory isn't robust w.r.t. extensions of the trail (e.g. if + its internal state undergoes significant changes), it can be easier to produce the explanation eagerly when - propagating, and then use [Consequence (fun () -> expl, proof)]. + propagating, and then use [Consequence (fun () -> expl, proof)] with + the already produced [(expl,proof)] tuple. *) (** The type of reasons for propagations of a formula [f]. *) From 6e8cedd7905c6058d0faf0a2aee7cc8496ccb3c4 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Mar 2019 11:27:37 -0500 Subject: [PATCH 137/182] cleanup some files --- .ocamlinit | 2 -- VERSION | 1 - 2 files changed, 3 deletions(-) delete mode 100644 .ocamlinit delete mode 100644 VERSION diff --git a/.ocamlinit b/.ocamlinit deleted file mode 100644 index 1b93b2ce..00000000 --- a/.ocamlinit +++ /dev/null @@ -1,2 +0,0 @@ -#directory "_build/src";; -#load "msat.cma";; diff --git a/VERSION b/VERSION deleted file mode 100644 index 5a2a5806..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.6 From fb219fb41528c9532b1df2b263cc0e71605846eb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Mar 2019 11:33:44 -0500 Subject: [PATCH 138/182] refactor: use `iter` instead of `sequence` --- README.md | 2 +- msat.opam | 2 +- src/core/Internal.ml | 20 ++++++++--------- src/core/Vec.ml | 8 +++---- src/core/Vec.mli | 2 +- src/core/dune | 2 +- src/sudoku/sudoku_solve.ml | 44 +++++++++++++++++++------------------- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 9ceb0d81..c741988b 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ opam pin add msat https://github.com/Gbury/mSAT.git ### Manual installation -You will need `dune` and `sequence`. The command is: +You will need `dune` and `iter`. The command is: ``` $ make install diff --git a/msat.opam b/msat.opam index c2fca559..698d0cd4 100644 --- a/msat.opam +++ b/msat.opam @@ -13,7 +13,7 @@ build: [ depends: [ "ocaml" { >= "4.03" } "dune" {build} - "sequence" + "iter" { >= "1.2" } "containers" {with-test} "mdx" {with-test} ] diff --git a/src/core/Internal.ml b/src/core/Internal.ml index fad444d3..2342f42d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -365,7 +365,7 @@ module Make(Plugin : PLUGIN) let[@inline] equal c1 c2 = c1.cid = c2.cid let[@inline] hash c = Hashtbl.hash c.cid let[@inline] atoms c = c.atoms - let[@inline] atoms_seq c = Sequence.of_array c.atoms + let[@inline] atoms_seq c = Iter.of_array c.atoms let[@inline] atoms_l c = Array.to_list c.atoms let flag_attached = 0b1 @@ -473,11 +473,11 @@ module Make(Plugin : PLUGIN) res (* do [c1] and [c2] have the same lits, modulo reordering and duplicates? *) - let same_lits (c1:atom Sequence.t) (c2:atom Sequence.t): bool = + let same_lits (c1:atom Iter.t) (c2:atom Iter.t): bool = let subset a b = - Sequence.iter Atom.mark b; - let res = Sequence.for_all Atom.seen a in - Sequence.iter clear_var_of_ b; + Iter.iter Atom.mark b; + let res = Iter.for_all Atom.seen a in + Iter.iter clear_var_of_ b; res in subset c1 c2 && subset c2 c1 @@ -567,9 +567,9 @@ module Make(Plugin : PLUGIN) (fun c -> let pivot = match - Sequence.of_array c.atoms - |> Sequence.filter (fun a -> Atom.seen (Atom.neg a)) - |> Sequence.to_list + Iter.of_array c.atoms + |> Iter.filter (fun a -> Atom.seen (Atom.neg a)) + |> Iter.to_list with | [a] -> a | [] -> @@ -608,11 +608,11 @@ module Make(Plugin : PLUGIN) error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion | History [c] -> let duplicates, res = find_dups c in - assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); + assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); { conclusion; step = Duplicate (c, duplicates) } | History (c :: r) -> let res, steps = find_pivots c r in - assert (same_lits (Sequence.of_list res) (Clause.atoms_seq conclusion)); + assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); { conclusion; step = Hyper_res {hr_init=c; hr_steps=steps}; } | Empty_premise -> raise Solver_intf.No_proof diff --git a/src/core/Vec.ml b/src/core/Vec.ml index 0f2fb07b..782a8b6a 100644 --- a/src/core/Vec.ml +++ b/src/core/Vec.ml @@ -90,10 +90,10 @@ let[@inline] iteri f t = let[@inline] to_seq a k = iter k a -let exists p t = Sequence.exists p @@ to_seq t -let for_all p t = Sequence.for_all p @@ to_seq t -let fold f acc a = Sequence.fold f acc @@ to_seq a -let to_list a = Sequence.to_list @@ to_seq a +let exists p t = Iter.exists p @@ to_seq t +let for_all p t = Iter.for_all p @@ to_seq t +let fold f acc a = Iter.fold f acc @@ to_seq a +let to_list a = Iter.to_list @@ to_seq a let to_array a = Array.sub a.data 0 a.sz let of_list l : _ t = diff --git a/src/core/Vec.mli b/src/core/Vec.mli index 74919698..d51a2b69 100644 --- a/src/core/Vec.mli +++ b/src/core/Vec.mli @@ -16,7 +16,7 @@ val to_array : 'a t -> 'a array val of_list : 'a list -> 'a t -val to_seq : 'a t -> 'a Sequence.t +val to_seq : 'a t -> 'a Iter.t val clear : 'a t -> unit (** Set size to 0, doesn't free elements *) diff --git a/src/core/dune b/src/core/dune index 6ec93b3a..85a23d9a 100644 --- a/src/core/dune +++ b/src/core/dune @@ -2,7 +2,7 @@ (library (name msat) (public_name msat) - (libraries sequence) + (libraries iter) (synopsis "core data structures and algorithms for msat") (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 389fce6a..616b02a4 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -36,13 +36,13 @@ module Grid : sig val set : t -> int -> int -> Cell.t -> t (** A set of related cells *) - type set = (int*int*Cell.t) Sequence.t + type set = (int*int*Cell.t) Iter.t - val rows : t -> set Sequence.t - val cols : t -> set Sequence.t - val squares : t -> set Sequence.t + val rows : t -> set Iter.t + val cols : t -> set Iter.t + val squares : t -> set Iter.t - val all_cells : t -> (int*int*Cell.t) Sequence.t + val all_cells : t -> (int*int*Cell.t) Iter.t val parse : string -> t val is_full : t -> bool @@ -60,9 +60,9 @@ end = struct s' (** A set of related cells *) - type set = (int*int*Cell.t) Sequence.t + type set = (int*int*Cell.t) Iter.t - open Sequence.Infix + open Iter.Infix let all_cells (g:t) = 0 -- 8 >>= fun i -> @@ -90,17 +90,17 @@ end = struct let is_valid g = let all_distinct (s:set) = (s >|= fun (_,_,c) -> c) - |> Sequence.diagonal - |> Sequence.for_all (fun (c1,c2) -> Cell.neq c1 c2) + |> Iter.diagonal + |> Iter.for_all (fun (c1,c2) -> Cell.neq c1 c2) in - Sequence.for_all all_distinct @@ rows g && - Sequence.for_all all_distinct @@ cols g && - Sequence.for_all all_distinct @@ squares g + Iter.for_all all_distinct @@ rows g && + Iter.for_all all_distinct @@ cols g && + Iter.for_all all_distinct @@ squares g let matches ~pat:g1 g2 : bool = all_cells g1 - |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) - |> Sequence.for_all (fun (x,y,c) -> Cell.equal c @@ get g2 x y) + |> Iter.filter (fun (_,_,c) -> Cell.is_full c) + |> Iter.for_all (fun (x,y,c) -> Cell.equal c @@ get g2 x y) let pp out g = Fmt.fprintf out "@["; @@ -186,11 +186,11 @@ end = struct let[@inline] all_diff kind f = let pairs = f (grid self) - |> Sequence.flat_map + |> Iter.flat_map (fun set -> set - |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) - |> Sequence.diagonal) + |> Iter.filter (fun (_,_,c) -> Cell.is_full c) + |> Iter.diagonal) in pairs (fun ((x1,y1,c1),(x2,y2,c2)) -> @@ -208,7 +208,7 @@ end = struct let trail_ (acts:_ Msat.acts) = acts.acts_iter_assumptions - |> Sequence.map + |> Iter.map (function | Assign _ -> assert false | Lit f -> f) @@ -255,10 +255,10 @@ end = struct let solve (self:t) : _ option = let assumptions = Grid.all_cells self.grid0 - |> Sequence.filter (fun (_,_,c) -> Cell.is_full c) - |> Sequence.map (fun (x,y,c) -> F.make true x y c) - |> Sequence.map (S.make_atom self.solver) - |> Sequence.to_rev_list + |> Iter.filter (fun (_,_,c) -> Cell.is_full c) + |> Iter.map (fun (x,y,c) -> F.make true x y c) + |> Iter.map (S.make_atom self.solver) + |> Iter.to_rev_list in Log.debugf 2 (fun k->k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list S.Atom.pp) assumptions); From 47a7142a3c7f9987305dd34cbcc64c9c8c323775 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Mar 2019 11:36:01 -0500 Subject: [PATCH 139/182] prepare for 0.8 --- msat.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msat.opam b/msat.opam index 698d0cd4..6f63c67d 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "dev" +version: "0.8" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ From 9aa4159f2b7e0d596b4701af219b968e69a029b3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Mar 2019 12:11:59 -0500 Subject: [PATCH 140/182] chore: fix travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dc316dd1..cd4165ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: - export OPAMVERBOSE=1 - opam switch ${OCAML_VERSION} - eval `opam config env` - - opam install ocamlfind dune sequence + - opam install ocamlfind dune iter - if ${RUN_TEST}; then opam install containers ; fi install: - make build From 338a84bf3aaec2538bdc6d358631db22bad56243 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Mar 2019 13:54:38 -0500 Subject: [PATCH 141/182] chore: use `iter`, not sequence, in dune --- src/sudoku/dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sudoku/dune b/src/sudoku/dune index ad4a2b92..000c407f 100644 --- a/src/sudoku/dune +++ b/src/sudoku/dune @@ -2,7 +2,7 @@ (executable (name sudoku_solve) (modes native) - (libraries msat msat.backtrack sequence containers) + (libraries msat msat.backtrack iter containers) (flags :standard -warn-error -a -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) From f199dd50a6aeece1f2aef3c83765989a7a8ea41b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 3 Apr 2019 16:55:39 -0500 Subject: [PATCH 142/182] feat: package for msat-bin, with gzip input --- dune | 1 + msat-bin.opam | 22 ++++++++++++++++++++++ src/main/dune | 5 +++-- src/main/main.ml | 8 +++++++- tests/dune | 4 ++++ 5 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 msat-bin.opam diff --git a/dune b/dune index 79a54003..78c4bdf2 100644 --- a/dune +++ b/dune @@ -1,6 +1,7 @@ (alias (name runtest) + (package msat) (deps README.md src/core/msat.cma src/sat/msat_sat.cma src/sudoku/sudoku_solve.exe) (locks test) (action (progn diff --git a/msat-bin.opam b/msat-bin.opam new file mode 100644 index 00000000..fd79290d --- /dev/null +++ b/msat-bin.opam @@ -0,0 +1,22 @@ +opam-version: "2.0" +name: "msat-bin" +synopsis: "SAT solver binary based on the msat library" +license: "Apache" +version: "0.8" +author: ["Simon Cruanes" "Guillaume Bury"] +maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] +build: [ + ["dune" "build" "@install" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] +] +depends: [ + "ocaml" { >= "4.03" } + "dune" {build} + "msat" { >= "0.8" < "0.9" } + "camlzip" +] +tags: [ "sat" ] +homepage: "https://github.com/Gbury/mSAT" +dev-repo: "git+https://github.com/Gbury/mSAT.git" +bug-reports: "https://github.com/Gbury/mSAT/issues/" + diff --git a/src/main/dune b/src/main/dune index bb0eb7d4..96d0cc4a 100644 --- a/src/main/dune +++ b/src/main/dune @@ -2,8 +2,9 @@ ; main binary (executable (name main) - ;(package msat) - (libraries containers msat msat_sat msat.backend) + (public_name msat) + (package msat-bin) + (libraries containers camlzip msat msat.sat msat.backend) (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) (ocamlopt_flags :standard -O3 -color always -unbox-closures -unbox-closures-factor 20) diff --git a/src/main/main.ml b/src/main/main.ml index a8988f48..ddc1d856 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -73,7 +73,13 @@ let parse_file f = let module L = Lexing in CCIO.with_in f (fun ic -> - let buf = L.from_channel ic in + let buf = + if CCString.suffix ~suf:".gz" f + then ( + let gic = Gzip.open_in_chan ic in + L.from_function (fun bytes len -> Gzip.input gic bytes 0 len) + ) else L.from_channel ic + in buf.L.lex_curr_p <- {buf.L.lex_curr_p with L.pos_fname=f;}; Dimacs_parse.file Dimacs_lex.token buf) diff --git a/tests/dune b/tests/dune index 2dbfc6a6..0a32129d 100644 --- a/tests/dune +++ b/tests/dune @@ -9,24 +9,28 @@ (alias (name runtest) + (package msat) (deps test_api.exe) (locks test) (action (run %{deps}))) (alias (name runtest) + (package msat) (deps ./icnf-solve/icnf_solve.exe Makefile (source_tree regression)) (locks test) (action (run make test-icnf))) (alias (name runtest) + (package msat) (deps ./../src/sudoku/sudoku_solve.exe Makefile (source_tree sudoku)) (locks test) (action (run make test-sudoku))) (alias (name runtest) + (package msat-bin) (deps ./../src/main/main.exe ./run (source_tree .)) (locks test) (action (run /usr/bin/time -f "%e" ./run sat))) From 6bd3b2e67b801145bb7e6ea8509389569791812a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 4 Apr 2019 10:18:22 -0500 Subject: [PATCH 143/182] chore: remove all deps on menhir --- .travis.yml | 2 +- dune-project | 1 - src/main/Dimacs_parse.mly | 17 +++++------------ src/main/dune | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd4165ea..5d0a8753 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: - export OPAMVERBOSE=1 - opam switch ${OCAML_VERSION} - eval `opam config env` - - opam install ocamlfind dune iter + - opam install ocamlfind dune iter camlzip - if ${RUN_TEST}; then opam install containers ; fi install: - make build diff --git a/dune-project b/dune-project index 977e7d75..7655de07 100644 --- a/dune-project +++ b/dune-project @@ -1,2 +1 @@ (lang dune 1.1) -(using menhir 1.0) diff --git a/src/main/Dimacs_parse.mly b/src/main/Dimacs_parse.mly index 3ad9341b..408b8db9 100644 --- a/src/main/Dimacs_parse.mly +++ b/src/main/Dimacs_parse.mly @@ -21,21 +21,14 @@ prelude: | P CNF LIT LIT { () } - | error - { - failwith @@ Format.asprintf "expected prelude %a" pp_pos ($startpos,$endpos) - } clauses: - | l=clause* { l } - | error - { - failwith @@ Format.asprintf "expected list of clauses %a" - pp_pos ($startpos,$endpos) - } + | { [] } + | clause clauses { $1 :: $2 } file: - | prelude l=clauses EOF { l } + | prelude clauses EOF { $2 } clause: - | l=LIT+ ZERO { l } + | ZERO { [] } + | LIT clause { $1 :: $2 } diff --git a/src/main/dune b/src/main/dune index 96d0cc4a..073ec30f 100644 --- a/src/main/dune +++ b/src/main/dune @@ -10,5 +10,5 @@ -unbox-closures -unbox-closures-factor 20) ) -(menhir (modules Dimacs_parse)) +(ocamlyacc (modules Dimacs_parse)) (ocamllex (modules Dimacs_lex)) From 9c78c6f7bb51a80936e81bb31e90313e43419f26 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 15 May 2019 13:02:40 -0500 Subject: [PATCH 144/182] perf: some basic optimizations --- src/core/Internal.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 2342f42d..8ce7d391 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1366,7 +1366,9 @@ module Make(Plugin : PLUGIN) assert (decision_level st > 0); Vec.clear to_unmark; let conflict_level = - Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms + if Plugin.mcsat + then Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms + else decision_level st in Log.debugf debug (fun k -> k "(@[sat.analyze-conflict@ :c-level %d@ :clause %a@])" conflict_level Clause.debug c_clause); @@ -1613,7 +1615,7 @@ module Make(Plugin : PLUGIN) Vec.push ak.neg.watched c; assert (Vec.get a.watched i == c); Vec.fast_remove a.watched i; - raise Exit + raise_notrace Exit ) done; (* no watch lit found *) @@ -1816,14 +1818,11 @@ module Make(Plugin : PLUGIN) if st.elt_head = Vec.size st.trail then ( theory_propagate st ) else ( - let num_props = ref 0 in match while st.elt_head < Vec.size st.trail do begin match Vec.get st.trail st.elt_head with | Lit _ -> () - | Atom a -> - incr num_props; - propagate_atom st a + | Atom a -> propagate_atom st a end; st.elt_head <- st.elt_head + 1; done; From 40464e4fe713abcc1b9ee68e44c88288e041e5d5 Mon Sep 17 00:00:00 2001 From: Arnaud Spiwack Date: Sat, 22 Jun 2019 00:55:01 +0200 Subject: [PATCH 145/182] style: fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit English doesn't allow “allows to” as a form (it needs to be “allows to” or something like this). A workaround is to use the phrase "make it possible to”, but in this case, I don't think it was warranted, so I simply used a more direct phrase. --- src/tseitin/Tseitin_intf.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tseitin/Tseitin_intf.ml b/src/tseitin/Tseitin_intf.ml index 6ead857f..99805c35 100644 --- a/src/tseitin/Tseitin_intf.ml +++ b/src/tseitin/Tseitin_intf.ml @@ -36,8 +36,7 @@ end module type S = sig (** CNF conversion - This modules allows to convert arbitrary boolean formulas - into CNF. + This modules converts arbitrary boolean formulas into CNF. *) type atom From 87a2936f75568711400785eafb00cbcb2704f85d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Nov 2019 16:56:36 -0600 Subject: [PATCH 146/182] doc: add an index file --- src/dune | 4 ++ src/index.mld | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/dune create mode 100644 src/index.mld diff --git a/src/dune b/src/dune new file mode 100644 index 00000000..05c64298 --- /dev/null +++ b/src/dune @@ -0,0 +1,4 @@ + +(documentation + (package msat) + (mld_files :standard)) diff --git a/src/index.mld b/src/index.mld new file mode 100644 index 00000000..38b0a9cc --- /dev/null +++ b/src/index.mld @@ -0,0 +1,121 @@ + + +{1 mSAT: a Modular SAT Solver} + +(The entry point of this library is the module: {!module-Msat}.) + +A modular implementation of the SMT algorithm can be found in the {!Msat.Solver} module, +as a functor which takes two modules : + +- A representation of formulas (which implements the `Formula_intf.S` signature) + +- A theory (which implements the `Theory_intf.S` signature) to check consistence of assertions. + +- A dummy empty module to ensure generativity of the solver (solver modules heavily relies on +side effects to their internal state) + +{3 Sat Solver} + +A ready-to-use SAT solver is available in the {!Msat_sat} module +using the [msat.sat] library (see {!module-Msat_sat}). It can be loaded +as shown in the following code : + +{[ +# #require "msat";; +# #require "msat.sat";; +# #print_depth 0;; (* do not print details *) +]} + +Then we can create a solver and create some boolean variables: + +{[ +module Sat = Msat_sat +module E = Sat.Int_lit (* expressions *) + +let solver = Sat.create() + +(* We create here two distinct atoms *) +let a = E.fresh () (* A 'new_atom' is always distinct from any other atom *) +let b = E.make 1 (* Atoms can be created from integers *) +]} + +We can try and check the satisfiability of some clauses — here, the clause [a or b]. +[Sat.assume] adds a list of clauses to the solver. Calling [Sat.solve] +will check the satisfiability of the current set of clauses, here "Sat". + +{[ +# a <> b;; +- : bool = true +# Sat.assume solver [[a; b]] ();; +- : unit = () +# let res = Sat.solve solver;; +val res : Sat.res = Sat.Sat ... +]} + +The Sat solver has an incremental mutable state, so we still have +the clause `a or b` in our assumptions. +We add `not a` and `not b` to the state, and get "Unsat". + +{[ +# Sat.assume solver [[E.neg a]; [E.neg b]] () ;; +- : unit = () +# let res = Sat.solve solver ;; +val res : Sat.res = Sat.Unsat ... +]} + +{3 Formulas API} + +Writing clauses by hand can be tedious and error-prone. +The functor {!Msat_tseitin.Make} in the library [msat.tseitin] (see {!module-Msat_tseitin}). +proposes a formula AST (parametrized by +atoms) and a function to convert these formulas into clauses: + +{[ +# #require "msat.tseitin";; +]} + +{[ +(* Module initialization *) +module F = Msat_tseitin.Make(E) + +let solver = Sat.create () + +(* We create here two distinct atoms *) +let a = E.fresh () (* A fresh atom is always distinct from any other atom *) +let b = E.make 1 (* Atoms can be created from integers *) + +(* Let's create some formulas *) +let p = F.make_atom a +let q = F.make_atom b +let r = F.make_and [p; q] +let s = F.make_or [F.make_not p; F.make_not q] +]} + +We can try and check the satisfiability of the given formulas, by turning +it into clauses using `make_cnf`: + +{[ +# Sat.assume solver (F.make_cnf r) ();; +- : unit = () +# Sat.solve solver;; +- : Sat.res = Sat.Sat ... +]} + +{[ +# Sat.assume solver (F.make_cnf s) ();; +- : unit = () +# Sat.solve solver ;; +- : Sat.res = Sat.Unsat ... +]} + +{3 Backtracking utils} + +The library {!module-Msat_backtrack} contains some backtrackable +data structures that are useful for implementing theories. + +{3 Library msat.backend} + +This is used for proof backends: + +The entry point of this library is the module: +{!module-Msat_backend}. From ca9d5447e09f6fa032e55bf28989b02d60e2bfa8 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Nov 2019 14:02:36 -0600 Subject: [PATCH 147/182] fix(heap): handle case with one element properly --- src/core/Heap.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Heap.ml b/src/core/Heap.ml index 08aab303..12fa608c 100644 --- a/src/core/Heap.ml +++ b/src/core/Heap.ml @@ -126,10 +126,11 @@ module Make(Elt : RANKED) = struct let remove_min ({heap} as s) = if Vec.size heap=0 then raise Not_found; let x = Vec.get heap 0 in - Elt.set_idx x _absent_index; let new_hd = Vec.pop heap in (* new head *) Vec.set heap 0 new_hd; Elt.set_idx new_hd 0; + (* remove [x]. do it after [new_hd.idx<-0] in case [x==new_hd] *) + Elt.set_idx x _absent_index; (* enforce heap property again *) if Vec.size heap > 1 then ( percolate_down s new_hd; From 0266a39b04b32ed1072030fa49a3f3f695815634 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Nov 2019 14:03:52 -0600 Subject: [PATCH 148/182] fix deprecation warnings related to pervasives --- src/core/Internal.ml | 4 ++-- src/sat/Int_lit.ml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 8ce7d391..486248b1 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -232,7 +232,7 @@ module Make(Plugin : PLUGIN) let[@inline] equal a b = a == b let[@inline] sign a = a == abs a let[@inline] hash a = Hashtbl.hash a.aid - let[@inline] compare a b = Pervasives.compare a.aid b.aid + let[@inline] compare a b = compare a.aid b.aid let[@inline] reason a = Var.reason a.var let[@inline] id a = a.aid let[@inline] is_true a = a.is_true @@ -1871,7 +1871,7 @@ module Make(Plugin : PLUGIN) Log.debugf 3 (fun k->k "(@[sat.gc.start :keep %d :out-of %d@])" n_of_learnts (Vec.size v)); assert (Vec.size v > n_of_learnts); (* sort by decreasing activity *) - Vec.sort v (fun c1 c2 -> Pervasives.compare c2.activity c1.activity); + Vec.sort v (fun c1 c2 -> compare c2.activity c1.activity); let n_collected = ref 0 in while Vec.size v > n_of_learnts do let c = Vec.pop v in diff --git a/src/sat/Int_lit.ml b/src/sat/Int_lit.ml index cdac09d4..9f01aaeb 100644 --- a/src/sat/Int_lit.ml +++ b/src/sat/Int_lit.ml @@ -44,7 +44,7 @@ let set_sign b i = if b then abs i else neg (abs i) let hash (a:int) = a land max_int let equal (a:int) b = a=b -let compare (a:int) b = Pervasives.compare a b +let compare (a:int) b = compare a b let make i = _make (2 * i) From 99fed971d6ce4984e96d173017f4633f80dd8396 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Nov 2019 14:44:01 -0600 Subject: [PATCH 149/182] fix heap --- src/core/Heap.ml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/core/Heap.ml b/src/core/Heap.ml index 12fa608c..ed9884bb 100644 --- a/src/core/Heap.ml +++ b/src/core/Heap.ml @@ -124,17 +124,22 @@ module Make(Elt : RANKED) = struct *) let remove_min ({heap} as s) = - if Vec.size heap=0 then raise Not_found; - let x = Vec.get heap 0 in - let new_hd = Vec.pop heap in (* new head *) - Vec.set heap 0 new_hd; - Elt.set_idx new_hd 0; - (* remove [x]. do it after [new_hd.idx<-0] in case [x==new_hd] *) - Elt.set_idx x _absent_index; - (* enforce heap property again *) - if Vec.size heap > 1 then ( - percolate_down s new_hd; - ); - x + match Vec.size heap with + | 0 -> raise Not_found + | 1 -> + let x = Vec.pop heap in + Elt.set_idx x _absent_index; + x + | _ -> + let x = Vec.get heap 0 in + let new_hd = Vec.pop heap in (* heap.last() *) + Vec.set heap 0 new_hd; + Elt.set_idx x _absent_index; + Elt.set_idx new_hd 0; + (* enforce heap property again *) + if Vec.size heap > 1 then ( + percolate_down s new_hd; + ); + x end [@@inline] From 88550716d8ecd31100ff143b9e7fafb63f27fe00 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 14 Dec 2019 19:44:38 -0600 Subject: [PATCH 150/182] prepare for 0.8.1 --- CHANGELOG.md | 12 ++++++++++++ README.md | 2 +- dune | 3 +-- msat-bin.opam | 7 ++++--- msat.opam | 4 ++-- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c6e5267..49a40c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # CHANGES +## 0.8.1 + +- fixes in `Heap` +- package for `msat-bin` +- use `iter` instead of `sequence` in dune and opam files +- more docs + +## 0.8 + +big refactoring, change of API with fewer functions, etc. +see `git log` for more details. + ## 0.6.1 - add simple functor for DOT backend diff --git a/README.md b/README.md index c741988b..8cf81dcc 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ It's able to parse sudoku grids denoted as 81 integers Here is a sample grid and the output from the solver (in roughly .5s): -```sh +```sh non-deterministic=command $ echo '..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9' > sudoku.txt $ dune exec src/sudoku/sudoku_solve.exe -- sudoku.txt ... diff --git a/dune b/dune index 78c4bdf2..ed665db4 100644 --- a/dune +++ b/dune @@ -1,8 +1,7 @@ (alias (name runtest) - (package msat) - (deps README.md src/core/msat.cma src/sat/msat_sat.cma src/sudoku/sudoku_solve.exe) + (deps README.md src/core/msat.cma src/sat/msat_sat.cma (source_tree src)) (locks test) (action (progn (run mdx test README.md) diff --git a/msat-bin.opam b/msat-bin.opam index fd79290d..6b347ca2 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" license: "Apache" -version: "0.8" +version: "0.8.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ @@ -11,8 +11,9 @@ build: [ ] depends: [ "ocaml" { >= "4.03" } - "dune" {build} - "msat" { >= "0.8" < "0.9" } + "dune" { >= "1.1" } + "msat" { = version } + "containers" { >= "2.0" } "camlzip" ] tags: [ "sat" ] diff --git a/msat.opam b/msat.opam index 6f63c67d..5c1118b8 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "0.8" +version: "0.8.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ @@ -12,7 +12,7 @@ build: [ ] depends: [ "ocaml" { >= "4.03" } - "dune" {build} + "dune" { >= "1.1" } "iter" { >= "1.2" } "containers" {with-test} "mdx" {with-test} From 407a7e83f74b3cbdcdb9e64c640b6c8c3cb493af Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 14 Jan 2020 22:56:01 -0600 Subject: [PATCH 151/182] fix: allow conflicts below decision level in `Make_cdcl_t` --- src/core/Internal.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 486248b1..e0be3c08 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -8,6 +8,10 @@ module type PLUGIN = sig val mcsat : bool (** Is this a mcsat plugin? *) + val has_theory : bool + (** Is this a CDCL(T) plugin or mcsat plugin? + i.e does it have theories *) + include Solver_intf.PLUGIN_MCSAT end @@ -1366,7 +1370,7 @@ module Make(Plugin : PLUGIN) assert (decision_level st > 0); Vec.clear to_unmark; let conflict_level = - if Plugin.mcsat + if Plugin.mcsat || Plugin.has_theory then Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms else decision_level st in @@ -2194,6 +2198,7 @@ module Make_cdcl_t(Plugin : Solver_intf.PLUGIN_CDCL_T) = let eval _ _ = Solver_intf.Unknown let assign _ t = t let mcsat = false + let has_theory = true let iter_assignable _ _ _ = () end) [@@inline][@@specialise] @@ -2202,6 +2207,7 @@ module Make_mcsat(Plugin : Solver_intf.PLUGIN_MCSAT) = Make(struct include Plugin let mcsat = true + let has_theory = false end) [@@inline][@@specialise] @@ -2219,6 +2225,7 @@ module Make_pure_sat(Plugin : Solver_intf.PLUGIN_SAT) = let eval () _ = Solver_intf.Unknown let assign () t = t let mcsat = false + let has_theory = false let iter_assignable () _ _ = () let mcsat = false end) From c4a4edde3a987013a6416782d029e702adca9495 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 14 Jan 2020 23:00:07 -0600 Subject: [PATCH 152/182] chore: fix opam file --- msat-bin.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msat-bin.opam b/msat-bin.opam index 6b347ca2..cdb349a3 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -7,7 +7,7 @@ author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ ["dune" "build" "@install" "-p" name "-j" jobs] - ["dune" "runtest" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] depends: [ "ocaml" { >= "4.03" } From 65bd7e7744657d2536b08aef3dd090c7678020bb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 24 Feb 2020 17:38:56 -0600 Subject: [PATCH 153/182] prepare for 0.8.2 --- CHANGELOG.md | 5 +++++ msat-bin.opam | 2 +- msat.opam | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a40c29..c8ed0200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGES +## 0.8.2 + +- fix opam file +- fix: allow conflicts below decision level in `Make_cdcl_t` + ## 0.8.1 - fixes in `Heap` diff --git a/msat-bin.opam b/msat-bin.opam index cdb349a3..063eac4e 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" license: "Apache" -version: "0.8.1" +version: "0.8.2" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ diff --git a/msat.opam b/msat.opam index 5c1118b8..8f66242a 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "0.8.1" +version: "0.8.2" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ @@ -17,7 +17,7 @@ depends: [ "containers" {with-test} "mdx" {with-test} ] -tags: [ "sat" "smt" ] +tags: [ "sat" "smt" "cdcl" "functor" ] homepage: "https://github.com/Gbury/mSAT" dev-repo: "git+https://github.com/Gbury/mSAT.git" bug-reports: "https://github.com/Gbury/mSAT/issues/" From b4ef8e4e670b1e368ba9fbdc93bc39e595f293bc Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 24 Feb 2020 17:46:35 -0600 Subject: [PATCH 154/182] chore: use a different travis script --- .travis.yml | 51 ++++++++++++++++----------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d0a8753..69defb26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,37 +1,18 @@ language: c +install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-docker.sh +script: bash -ex .travis-docker.sh +services: +- docker env: - - RUN_TEST=true OCAML_VERSION=4.03.0 - - RUN_TEST=true OCAML_VERSION=4.03.0+flambda - - RUN_TEST=true OCAML_VERSION=4.04.0 - - RUN_TEST=true OCAML_VERSION=4.04.0+flambda - - RUN_TEST=true OCAML_VERSION=4.06.0 -addons: - apt: - sources: - - avsm - packages: - - opam - - time -before_install: - # Download and use opam2 - - wget -O ${HOME}/opam https://github.com/ocaml/opam/releases/download/2.0.0-beta6/opam-2.0.0-beta6-x86_64-linux - - chmod +x ${HOME}/opam - # Some opam boilerplate - - export OPAMYES=1 - - export OPAMJOBS=2 - # Init opam, and the default switch with the right ocaml version - - ${HOME}/opam init --compiler=${OCAML_VERSION} - - eval `${HOME}/opam config env` - - export OPAMVERBOSE=1 - - opam switch ${OCAML_VERSION} - - eval `opam config env` - - opam install ocamlfind dune iter camlzip - - if ${RUN_TEST}; then opam install containers ; fi -install: - - make build -script: - # Build and launch the tests - - if [ "$TO_TEST" = "tests" ]; then make lib && make bin && make test; fi - # Try and install the package with opam - - if [ "$TO_TEST" = "install" ]; then ${HOME}/opam install msat.dev; fi - + global: + - PINS="msat:. msat-bin:." + - DISTRO="ubuntu-16.04" + matrix: + - PACKAGE="msat" CAML_VERSION="4.03" + - PACKAGE="msat" CAML_VERSION="4.04" + #- PACKAGE="msat" CAML_VERSION="4.05" + - PACKAGE="msat" CAML_VERSION="4.06" + #- PACKAGE="msat" CAML_VERSION="4.08" + - PACKAGE="msat" CAML_VERSION="4.09" + - PACKAGE="msat" CAML_VERSION="4.10" + - PACKAGE="msat-bin" CAML_VERSION="4.06" TESTS=false From 8ef4913c92eb03bef8db170ab37ed20796ebaa93 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 28 Feb 2020 09:29:48 -0600 Subject: [PATCH 155/182] chore: disable opam testing for msat-bin --- dune | 1 + msat-bin.opam | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dune b/dune index ed665db4..8b682629 100644 --- a/dune +++ b/dune @@ -3,6 +3,7 @@ (name runtest) (deps README.md src/core/msat.cma src/sat/msat_sat.cma (source_tree src)) (locks test) + (package msat) (action (progn (run mdx test README.md) (diff? README.md README.md.corrected)))) diff --git a/msat-bin.opam b/msat-bin.opam index 063eac4e..4443f8ee 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -7,7 +7,7 @@ author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ ["dune" "build" "@install" "-p" name "-j" jobs] - ["dune" "runtest" "-p" name "-j" jobs] {with-test} + #["dune" "runtest" "-p" name "-j" jobs] {with-test} ] depends: [ "ocaml" { >= "4.03" } From 707085b2c162fcdb2cd742156f6a56af36d8d5d9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 15 May 2020 18:20:31 -0400 Subject: [PATCH 156/182] add basic config for benchpress --- tests/benchpress.sexp | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/benchpress.sexp diff --git a/tests/benchpress.sexp b/tests/benchpress.sexp new file mode 100644 index 00000000..0b48e773 --- /dev/null +++ b/tests/benchpress.sexp @@ -0,0 +1,13 @@ + +(prover + (name msat) + (synopsis "msat for pure sat problems") + (version "git:.") + (sat "^Sat") + (unsat "^Unsat") + (cmd "$cur_dir/../msat.exe -time $timeout $file")) + +(dir + (path $cur_dir) + (pattern ".*\\.cnf") + (expect (const unknown))) From 8eb32fa9abdb30665db7030c4861fe2229c0f671 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 8 Sep 2020 22:42:38 -0400 Subject: [PATCH 157/182] fix: support containers 2.8.1 and above --- msat-bin.opam | 2 +- msat.opam | 2 +- src/sudoku/sudoku_solve.ml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/msat-bin.opam b/msat-bin.opam index 4443f8ee..0dd5a5be 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -13,7 +13,7 @@ depends: [ "ocaml" { >= "4.03" } "dune" { >= "1.1" } "msat" { = version } - "containers" { >= "2.0" } + "containers" { >= "2.8.1" } "camlzip" ] tags: [ "sat" ] diff --git a/msat.opam b/msat.opam index 8f66242a..630e9bdf 100644 --- a/msat.opam +++ b/msat.opam @@ -14,7 +14,7 @@ depends: [ "ocaml" { >= "4.03" } "dune" { >= "1.1" } "iter" { >= "1.2" } - "containers" {with-test} + "containers" {with-test & >= "2.8.1" & < "4.0" } "mdx" {with-test} ] tags: [ "sat" "smt" "cdcl" "functor" ] diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index 616b02a4..e17e0b26 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -234,7 +234,7 @@ end = struct let partial_check (self:t) acts : unit = Log.debugf 4 - (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.seq F.pp) (trail_ acts)); + (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.iter F.pp) (trail_ acts)); add_slice self acts; check_ self acts From 9ef84f1df3c809a58656546edc0cb58e9d23999e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Sep 2020 15:38:07 -0400 Subject: [PATCH 158/182] fix test on 2.8.1 --- src/sudoku/sudoku_solve.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sudoku/sudoku_solve.ml b/src/sudoku/sudoku_solve.ml index e17e0b26..a75ace03 100644 --- a/src/sudoku/sudoku_solve.ml +++ b/src/sudoku/sudoku_solve.ml @@ -206,7 +206,7 @@ end = struct all_diff "squares" Grid.squares; () - let trail_ (acts:_ Msat.acts) = + let trail_ (acts:_ Msat.acts) = acts.acts_iter_assumptions |> Iter.map (function @@ -234,7 +234,8 @@ end = struct let partial_check (self:t) acts : unit = Log.debugf 4 - (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.iter F.pp) (trail_ acts)); + (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" + (Fmt.list F.pp) (trail_ acts |> Iter.to_list)); add_slice self acts; check_ self acts From 4bce0f32d41a4ad13003b4fdb4100bba91a000e2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Sep 2020 15:39:52 -0400 Subject: [PATCH 159/182] prepare for 0.8.3 --- CHANGELOG.md | 4 ++++ msat-bin.opam | 4 ++-- msat.opam | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ed0200..6073b0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGES +## 0.8.3 + +- support containers 3.0 + ## 0.8.2 - fix opam file diff --git a/msat-bin.opam b/msat-bin.opam index 0dd5a5be..a1224fc5 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" license: "Apache" -version: "0.8.2" +version: "0.8.3" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ @@ -13,7 +13,7 @@ depends: [ "ocaml" { >= "4.03" } "dune" { >= "1.1" } "msat" { = version } - "containers" { >= "2.8.1" } + "containers" { >= "2.8.1" & < "4.0" } "camlzip" ] tags: [ "sat" ] diff --git a/msat.opam b/msat.opam index 630e9bdf..b3d1bd80 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "0.8.2" +version: "0.8.3" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ From d9bf16dfde85ba6ba34ae6032e47922011cdb718 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 28 Dec 2019 08:07:22 -0600 Subject: [PATCH 160/182] feat: allow to set the default polarity of variables at creation time --- src/core/Internal.ml | 25 +++++++++++++++---------- src/core/Msat.ml | 2 +- src/core/Solver_intf.ml | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index e0be3c08..bda30191 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -168,6 +168,7 @@ module Make(Plugin : PLUGIN) let seen_var = 0b1 let seen_pos = 0b10 let seen_neg = 0b100 + let default_pol_true = 0b1000 module Var = struct type t = var @@ -180,8 +181,11 @@ module Make(Plugin : PLUGIN) let[@inline] mark v = v.v_fields <- v.v_fields lor seen_var let[@inline] unmark v = v.v_fields <- v.v_fields land (lnot seen_var) let[@inline] marked v = (v.v_fields land seen_var) <> 0 + let[@inline] set_default_pol_true v = v.v_fields <- v.v_fields lor default_pol_true + let[@inline] set_default_pol_false v = v.v_fields <- v.v_fields land (lnot default_pol_true) + let[@inline] default_pol v = (v.v_fields land default_pol_true) <> 0 - let make (st:st) (t:formula) : var * Solver_intf.negated = + let make ?(default_pol=true) (st:st) (t:formula) : var * Solver_intf.negated = let lit, negated = Formula.norm t in try MF.find st.f_map lit, negated @@ -214,6 +218,7 @@ module Make(Plugin : PLUGIN) aid = cpt_double + 1 (* aid = vid*2+1 *) } in MF.add st.f_map lit var; st.cpt_mk_var <- st.cpt_mk_var + 1; + if default_pol then set_default_pol_true var; Vec.push st.vars (E_var var); var, negated @@ -255,8 +260,8 @@ module Make(Plugin : PLUGIN) a.var.v_fields <- seen_neg lor a.var.v_fields ) - let[@inline] make st lit = - let var, negated = Var.make st lit in + let[@inline] make ?default_pol st lit = + let var, negated = Var.make ?default_pol st lit in match negated with | Solver_intf.Negated -> var.na | Solver_intf.Same_sign -> var.pa @@ -899,8 +904,8 @@ module Make(Plugin : PLUGIN) (* When we have a new literal, we need to first create the list of its subterms. *) - let mk_atom st (f:formula) : atom = - let res = Atom.make st.st f in + let mk_atom ?default_pol st (f:formula) : atom = + let res = Atom.make ?default_pol st.st f in if Plugin.mcsat then ( mk_atom_mcsat_ st res; ); @@ -1668,8 +1673,8 @@ module Make(Plugin : PLUGIN) aux 0 (* Propagation (boolean and theory) *) - let create_atom st f = - let a = mk_atom st f in + let create_atom ?default_pol st f = + let a = mk_atom ?default_pol st f in ignore (th_eval st a); a @@ -1757,8 +1762,8 @@ module Make(Plugin : PLUGIN) let a = create_atom st f in eval_atom_ a - let[@inline] acts_mk_lit st f : unit = - ignore (create_atom st f : atom) + let[@inline] acts_mk_lit st ?default_pol f : unit = + ignore (create_atom ?default_pol st f : atom) let[@inline] acts_mk_term st t : unit = make_term st t @@ -1939,7 +1944,7 @@ module Make(Plugin : PLUGIN) enqueue_assign st l value current_level ) | E_var v -> - pick_branch_aux st v.pa + pick_branch_aux st (if Var.default_pol v then v.pa else v.na) | exception Not_found -> raise_notrace E_sat end diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 0aee8e57..6de87bec 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -44,7 +44,7 @@ type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.re type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) Solver_intf.acts = { acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; acts_eval_lit: 'formula -> lbool; - acts_mk_lit: 'formula -> unit; + acts_mk_lit: ?default_pol:bool -> 'formula -> unit; acts_mk_term: 'term -> unit; acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 5815e659..351850c7 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -108,7 +108,7 @@ type ('term, 'formula, 'value, 'proof) acts = { acts_eval_lit: 'formula -> lbool; (** Obtain current value of the given literal *) - acts_mk_lit: 'formula -> unit; + acts_mk_lit: ?default_pol:bool -> 'formula -> unit; (** Map the given formula to a literal, which will be decided by the SAT solver. *) From 79b15858047eb1c8cc9b1d69b51034a0e616d494 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 29 Oct 2019 14:21:25 -0500 Subject: [PATCH 161/182] feat: allow the theory to ask for some literals to be decided on it's part of the actions provided to the theory. close #19 --- src/core/Internal.ml | 30 ++++++++++++++++++++---------- src/core/Msat.ml | 2 +- src/core/Solver_intf.ml | 5 +++++ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index bda30191..6f71d5e3 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -787,9 +787,11 @@ module Make(Plugin : PLUGIN) mutable unsat_at_0: clause option; (* conflict at level 0, if any *) - mutable next_decision : atom option; + mutable next_decisions : atom list; (* When the last conflict was a semantic one (mcsat), - this stores the next decision to make *) + this stores the next decision to make; + if some theory wants atoms to be decided on (for theory combination), + store them here. *) trail : trail_elt Vec.t; (* decision stack + propagated elements (atoms or assignments). *) @@ -841,7 +843,7 @@ module Make(Plugin : PLUGIN) let create_ ~st ~store_proof (th:theory) : t = { st; th; unsat_at_0=None; - next_decision = None; + next_decisions = []; clauses_hyps = Vec.create(); clauses_learnt = Vec.create(); @@ -1175,6 +1177,7 @@ module Make(Plugin : PLUGIN) Vec.shrink st.trail !head; Vec.shrink st.elt_levels lvl; Plugin.pop_levels st.th n; + st.next_decisions <- []; ); () @@ -1489,7 +1492,7 @@ module Make(Plugin : PLUGIN) enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) ) else ( assert Plugin.mcsat; - st.next_decision <- Some fuip.neg + st.next_decisions <- [fuip.neg]; ) end; var_decay_activity st; @@ -1502,7 +1505,7 @@ module Make(Plugin : PLUGIN) *) let add_boolean_conflict st (confl:clause): unit = Log.debugf info (fun k -> k "(@[sat.add-bool-conflict@ %a@])" Clause.debug confl); - st.next_decision <- None; + st.next_decisions <- []; assert (decision_level st >= 0); if decision_level st = 0 || Array.for_all (fun a -> a.var.v_level <= 0) confl.atoms then ( @@ -1695,6 +1698,11 @@ module Make(Plugin : PLUGIN) Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c + let acts_add_decision_lit (st:t) (f:formula) : unit = + let a = create_atom st f in + Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); + st.next_decisions <- a :: st.next_decisions + let acts_raise st (l:formula list) proof : 'a = let atoms = List.rev_map (create_atom st) l in (* conflicts can be removed *) @@ -1776,6 +1784,7 @@ module Make(Plugin : PLUGIN) acts_add_clause = acts_add_clause st; acts_propagate = acts_propagate st; acts_raise_conflict=acts_raise st; + acts_add_decision_lit=acts_add_decision_lit st; } (* full slice, for [if_sat] final check *) @@ -1788,6 +1797,7 @@ module Make(Plugin : PLUGIN) acts_add_clause = acts_add_clause st; acts_propagate = acts_propagate st; acts_raise_conflict=acts_raise st; + acts_add_decision_lit=acts_add_decision_lit st; } (* Assert that the conflict is indeeed a conflict *) @@ -1914,12 +1924,12 @@ module Make(Plugin : PLUGIN) ) and pick_branch_lit st = - match st.next_decision with - | Some atom -> + match st.next_decisions with + | atom :: tl -> assert Plugin.mcsat; - st.next_decision <- None; + st.next_decisions <- tl; pick_branch_aux st atom - | None when decision_level st < Vec.size st.assumptions -> + | [] when decision_level st < Vec.size st.assumptions -> (* use an assumption *) let a = Vec.get st.assumptions (decision_level st) in if Atom.is_true a then ( @@ -1932,7 +1942,7 @@ module Make(Plugin : PLUGIN) ) else ( pick_branch_aux st a ) - | None -> + | [] -> begin match H.remove_min st.order with | E_lit l -> if Lit.level l >= 0 then ( diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 6de87bec..07ea45c3 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -1,4 +1,3 @@ - (** Main API *) @@ -49,6 +48,7 @@ type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; acts_propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; + acts_add_decision_lit: 'formula -> unit; } type negated = Solver_intf.negated = Negated | Same_sign diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 351850c7..cbf909c0 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -131,6 +131,11 @@ type ('term, 'formula, 'value, 'proof) acts = { acts_propagate: 'formula -> ('term, 'formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) + + acts_add_decision_lit: 'formula -> unit; + (** Ask the SAT solver to decide on the given formula before it can + answer [SAT]. This will be removed on backtrack. + Useful for theory combination. *) } (** The type for a slice of assertions to assume/propagate in the theory. *) From 2286a72437de8f59426bb802c0da1194a1212cfd Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 29 Oct 2019 14:34:13 -0500 Subject: [PATCH 162/182] fix: in final-check, resume if there are new decisions to do --- src/core/Internal.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 6f71d5e3..749626d2 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -2041,7 +2041,9 @@ module Make(Plugin : PLUGIN) begin match Plugin.final_check st.th (full_slice st) with | () -> if st.elt_head = Vec.size st.trail && - Vec.is_empty st.clauses_to_add then ( + Vec.is_empty st.clauses_to_add && + st.next_decisions = [] + then ( raise_notrace E_sat ); (* otherwise, keep on *) From 919c1e60117fc21acdb66b869a72e44c54c1aaf3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 29 Oct 2019 14:35:26 -0500 Subject: [PATCH 163/182] fix: only push atoms that don't have a value into `next_decisions` --- src/core/Internal.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 749626d2..36d9c24d 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -246,6 +246,7 @@ module Make(Plugin : PLUGIN) let[@inline] id a = a.aid let[@inline] is_true a = a.is_true let[@inline] is_false a = a.neg.is_true + let has_value a = is_true a || is_false a let[@inline] seen a = if sign a @@ -1700,8 +1701,10 @@ module Make(Plugin : PLUGIN) let acts_add_decision_lit (st:t) (f:formula) : unit = let a = create_atom st f in - Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); - st.next_decisions <- a :: st.next_decisions + if not (Atom.has_value a) then ( + Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); + st.next_decisions <- a :: st.next_decisions + ) let acts_raise st (l:formula list) proof : 'a = let atoms = List.rev_map (create_atom st) l in From 4097ed9dc2fd7236deed35ddf96026b54ceefad4 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 28 Dec 2019 07:47:53 -0600 Subject: [PATCH 164/182] refactor: account for @gbury's review --- src/core/Internal.ml | 2 +- src/core/Solver_intf.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 36d9c24d..82c1b036 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1493,6 +1493,7 @@ module Make(Plugin : PLUGIN) enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) ) else ( assert Plugin.mcsat; + assert (st.next_decisions = []); st.next_decisions <- [fuip.neg]; ) end; @@ -1929,7 +1930,6 @@ module Make(Plugin : PLUGIN) and pick_branch_lit st = match st.next_decisions with | atom :: tl -> - assert Plugin.mcsat; st.next_decisions <- tl; pick_branch_aux st atom | [] when decision_level st < Vec.size st.assumptions -> diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index cbf909c0..5f1b7d44 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -134,7 +134,7 @@ type ('term, 'formula, 'value, 'proof) acts = { acts_add_decision_lit: 'formula -> unit; (** Ask the SAT solver to decide on the given formula before it can - answer [SAT]. This will be removed on backtrack. + answer [SAT]. The order of decisions is still unspecified. Useful for theory combination. *) } (** The type for a slice of assertions to assume/propagate in the theory. *) From 764695bb6568c40dd5dbd23e1f9d15a2d25c7422 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 14 Nov 2020 18:58:40 -0500 Subject: [PATCH 165/182] feat: pass a sign along with the formula in `acts_add_decision_lit` --- src/core/Internal.ml | 3 ++- src/core/Msat.ml | 2 +- src/core/Solver_intf.ml | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 82c1b036..4076ae6f 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1700,8 +1700,9 @@ module Make(Plugin : PLUGIN) Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); Vec.push st.clauses_to_add c - let acts_add_decision_lit (st:t) (f:formula) : unit = + let acts_add_decision_lit (st:t) (f:formula) (sign:bool) : unit = let a = create_atom st f in + let a = if sign then a else Atom.neg a in if not (Atom.has_value a) then ( Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); st.next_decisions <- a :: st.next_decisions diff --git a/src/core/Msat.ml b/src/core/Msat.ml index 07ea45c3..fb049615 100644 --- a/src/core/Msat.ml +++ b/src/core/Msat.ml @@ -48,7 +48,7 @@ type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; acts_propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; - acts_add_decision_lit: 'formula -> unit; + acts_add_decision_lit: 'formula -> bool -> unit; } type negated = Solver_intf.negated = Negated | Same_sign diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index 5f1b7d44..e144df1a 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -120,7 +120,7 @@ type ('term, 'formula, 'value, 'proof) acts = { (** Add a clause to the solver. @param keep if true, the clause will be kept by the solver. Otherwise the solver is allowed to GC the clause and propose this - partial model again. + partial model again. *) acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; @@ -132,10 +132,10 @@ type ('term, 'formula, 'value, 'proof) acts = { (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) - acts_add_decision_lit: 'formula -> unit; - (** Ask the SAT solver to decide on the given formula before it can - answer [SAT]. The order of decisions is still unspecified. - Useful for theory combination. *) + acts_add_decision_lit: 'formula -> bool -> unit; + (** Ask the SAT solver to decide on the given formula with given sign + before it can answer [SAT]. The order of decisions is still unspecified. + Useful for theory combination. This will be undone on backtracking. *) } (** The type for a slice of assertions to assume/propagate in the theory. *) From 14e07f7a8ad0a1754feffd4e56d9fa0429f43b10 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 23 Dec 2020 14:22:06 -0500 Subject: [PATCH 166/182] prepare for 0.9 --- CHANGELOG.md | 5 +++++ msat-bin.opam | 2 +- msat.opam | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6073b0d6..18112e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGES +## 0.9 + +- feat: allow the theory to ask for some literals to be decided on +- feat: allow to set the default polarity of variables at creation time + ## 0.8.3 - support containers 3.0 diff --git a/msat-bin.opam b/msat-bin.opam index a1224fc5..99259285 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" license: "Apache" -version: "0.8.3" +version: "0.9" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ diff --git a/msat.opam b/msat.opam index b3d1bd80..8b99d2b8 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "0.8.3" +version: "0.9" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ From 6df35161b038cf91308b79f072e8532bb22e4ba6 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 16 Feb 2021 18:06:38 -0500 Subject: [PATCH 167/182] typo --- src/core/Internal.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 4076ae6f..dc01b58c 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1987,7 +1987,7 @@ module Make(Plugin : PLUGIN) assert (st.elt_head = st.th_head); if Vec.size st.trail = nb_elt st.st then raise_notrace E_sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( - Log.debug info "(@[sat.restarting@])"; + Log.debug info "(sat.restarting)"; cancel_until st 0; raise_notrace Restart ); From a64202c6ec2ba5e3a1ea9408dbd4f7d98112ae6d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 23 Mar 2021 15:28:34 -0400 Subject: [PATCH 168/182] feat: add `on_conflict` callback --- src/core/Internal.ml | 15 ++++++++++++++- src/core/Solver_intf.ml | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index dc01b58c..65a5c678 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -830,6 +830,8 @@ module Make(Plugin : PLUGIN) mutable clause_incr : float; (* increment for clauses' activity *) + + mutable on_conflict : (atom array -> unit); } type solver = t @@ -840,6 +842,8 @@ module Make(Plugin : PLUGIN) number of clauses by default *) let learntsize_factor = 1. /. 3. + let _nop_on_conflict (_:atom array) = () + (* Starting environment. *) let create_ ~st ~store_proof (th:theory) : t = { st; th; @@ -864,6 +868,7 @@ module Make(Plugin : PLUGIN) var_incr = 1.; clause_incr = 1.; store_proof; + on_conflict = _nop_on_conflict; } let create ?(store_proof=true) ?(size=`Big) (th:theory) : t = @@ -1981,6 +1986,7 @@ module Make(Plugin : PLUGIN) ) else ( add_clause_ st confl ); + st.on_conflict confl.atoms; | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); @@ -2056,6 +2062,7 @@ module Make(Plugin : PLUGIN) check_is_conflict_ c; Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); + st.on_conflict c.atoms; Vec.push st.clauses_to_add c; flush_clauses st; end; @@ -2179,14 +2186,20 @@ module Make(Plugin : PLUGIN) | E_unsat (US_false c) -> st.unsat_at_0 <- Some c - let solve ?(assumptions=[]) (st:t) : res = + let solve ?on_conflict ?(assumptions=[]) (st:t) : res = cancel_until st 0; Vec.clear st.assumptions; List.iter (Vec.push st.assumptions) assumptions; + begin match on_conflict with + | None -> () + | Some f -> st.on_conflict <- f; + end; try solve_ st; + st.on_conflict <- _nop_on_conflict; Sat (mk_sat st) with E_unsat us -> + st.on_conflict <- _nop_on_conflict; Unsat (mk_unsat st us) let true_at_level0 st a = diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index e144df1a..a772e7c9 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -481,7 +481,10 @@ module type S = sig val add_clause_a : t -> atom array -> lemma -> unit (** Lower level addition of clauses *) - val solve : ?assumptions:atom list -> t -> res + val solve : + ?on_conflict:(atom array -> unit) -> + ?assumptions:atom list -> + t -> res (** Try and solves the current set of clauses. @param assumptions additional atomic assumptions to be temporarily added. The assumptions are just used for this call to [solve], they are From db042f7b884ba4475011f9229dd40ff8c2fd2883 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 24 Mar 2021 11:28:02 -0400 Subject: [PATCH 169/182] fix: termination issue when using `add_decision_lit` --- src/core/Internal.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index 65a5c678..cee6eae4 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -1991,7 +1991,6 @@ module Make(Plugin : PLUGIN) | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); assert (st.elt_head = st.th_head); - if Vec.size st.trail = nb_elt st.st then raise_notrace E_sat; if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( Log.debug info "(sat.restarting)"; cancel_until st 0; @@ -2047,7 +2046,9 @@ module Make(Plugin : PLUGIN) n_of_conflicts := !n_of_conflicts *. restart_inc; n_of_learnts := !n_of_learnts *. learntsize_inc | E_sat -> - assert (st.elt_head = Vec.size st.trail); + assert (st.elt_head = Vec.size st.trail && + Vec.is_empty st.clauses_to_add && + st.next_decisions=[]); begin match Plugin.final_check st.th (full_slice st) with | () -> if st.elt_head = Vec.size st.trail && From 457aa157294da61d5497bd62758d8d4f60caa29a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 24 Mar 2021 13:53:20 -0400 Subject: [PATCH 170/182] prepare for 0.9.1 --- CHANGELOG.md | 5 +++++ msat-bin.opam | 2 +- msat.opam | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18112e88..0efb4bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGES +## 0.9.1 + +- add `on_conflit` callback +- fix termination issue when using `push_decision_lit` from plugin + ## 0.9 - feat: allow the theory to ask for some literals to be decided on diff --git a/msat-bin.opam b/msat-bin.opam index 99259285..ebba2627 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" license: "Apache" -version: "0.9" +version: "0.9.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ diff --git a/msat.opam b/msat.opam index 8b99d2b8..380c02e7 100644 --- a/msat.opam +++ b/msat.opam @@ -2,7 +2,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" license: "Apache" -version: "0.9" +version: "0.9.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] build: [ From b6ea55e7abb71e8fca294a167bf7d8091cbd82a5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 24 Mar 2021 15:18:52 -0400 Subject: [PATCH 171/182] merge opam fix from opam-repo --- msat-bin.opam | 2 +- msat.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/msat-bin.opam b/msat-bin.opam index ebba2627..c31e7e3b 100644 --- a/msat-bin.opam +++ b/msat-bin.opam @@ -1,7 +1,7 @@ opam-version: "2.0" name: "msat-bin" synopsis: "SAT solver binary based on the msat library" -license: "Apache" +license: "Apache-2.0" version: "0.9.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] diff --git a/msat.opam b/msat.opam index 380c02e7..69093cae 100644 --- a/msat.opam +++ b/msat.opam @@ -1,7 +1,7 @@ opam-version: "2.0" name: "msat" synopsis: "Library containing a SAT solver that can be parametrized by a theory" -license: "Apache" +license: "Apache-2.0" version: "0.9.1" author: ["Simon Cruanes" "Guillaume Bury"] maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] From 941fe921252ccea2915db493352e3c24eb8e5a05 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 15 Jun 2021 21:17:25 -0400 Subject: [PATCH 172/182] callbacks for conflict/decisions/new-atoms breaking change for `solve`, remove the `on_conflict` callback. it's provided at creation time instead. --- src/core/Internal.ml | 39 +++++++++++++++++++++++---------------- src/core/Solver_intf.ml | 4 +++- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/core/Internal.ml b/src/core/Internal.ml index cee6eae4..7e704403 100644 --- a/src/core/Internal.ml +++ b/src/core/Internal.ml @@ -831,7 +831,9 @@ module Make(Plugin : PLUGIN) mutable clause_incr : float; (* increment for clauses' activity *) - mutable on_conflict : (atom array -> unit); + mutable on_conflict : (atom array -> unit) option; + mutable on_decision : (atom -> unit) option; + mutable on_new_atom: (atom -> unit) option; } type solver = t @@ -868,12 +870,20 @@ module Make(Plugin : PLUGIN) var_incr = 1.; clause_incr = 1.; store_proof; - on_conflict = _nop_on_conflict; + on_conflict = None; + on_decision= None; + on_new_atom = None; } - let create ?(store_proof=true) ?(size=`Big) (th:theory) : t = + let create + ?on_conflict ?on_decision ?on_new_atom + ?(store_proof=true) ?(size=`Big) (th:theory) : t = let st = create_st ~size () in - create_ ~st ~store_proof th + let st = create_ ~st ~store_proof th in + st.on_new_atom <- on_new_atom; + st.on_decision <- on_decision; + st.on_conflict <- on_conflict; + st let[@inline] st t = t.st let[@inline] nb_clauses st = Vec.size st.clauses_hyps @@ -947,13 +957,14 @@ module Make(Plugin : PLUGIN) let make_term st t = let l = Lit.make st.st t in if l.l_level < 0 then ( - insert_elt_order st (E_lit l) + insert_elt_order st (E_lit l); ) let make_atom st (p:formula) : atom = let a = mk_atom st p in if a.var.v_level < 0 then ( insert_elt_order st (E_var a.var); + (match st.on_new_atom with Some f -> f a | None -> ()); ) else ( assert (a.is_true || a.neg.is_true); ); @@ -1923,14 +1934,16 @@ module Make(Plugin : PLUGIN) | Solver_intf.Unknown -> new_decision_level st; let current_level = decision_level st in - enqueue_bool st atom ~level:current_level Decision + enqueue_bool st atom ~level:current_level Decision; + (match st.on_decision with Some f -> f atom | None -> ()); | Solver_intf.Valued (b, l) -> let a = if b then atom else atom.neg in enqueue_semantic st a l ) else ( new_decision_level st; let current_level = decision_level st in - enqueue_bool st atom ~level:current_level Decision + enqueue_bool st atom ~level:current_level Decision; + (match st.on_decision with Some f -> f atom | None -> ()); ) and pick_branch_lit st = @@ -1986,7 +1999,7 @@ module Make(Plugin : PLUGIN) ) else ( add_clause_ st confl ); - st.on_conflict confl.atoms; + (match st.on_conflict with Some f -> f confl.atoms | None -> ()); | None -> (* No Conflict *) assert (st.elt_head = Vec.size st.trail); @@ -2063,7 +2076,7 @@ module Make(Plugin : PLUGIN) check_is_conflict_ c; Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); - st.on_conflict c.atoms; + (match st.on_conflict with Some f -> f c.atoms | None -> ()); Vec.push st.clauses_to_add c; flush_clauses st; end; @@ -2187,20 +2200,14 @@ module Make(Plugin : PLUGIN) | E_unsat (US_false c) -> st.unsat_at_0 <- Some c - let solve ?on_conflict ?(assumptions=[]) (st:t) : res = + let solve ?(assumptions=[]) (st:t) : res = cancel_until st 0; Vec.clear st.assumptions; List.iter (Vec.push st.assumptions) assumptions; - begin match on_conflict with - | None -> () - | Some f -> st.on_conflict <- f; - end; try solve_ st; - st.on_conflict <- _nop_on_conflict; Sat (mk_sat st) with E_unsat us -> - st.on_conflict <- _nop_on_conflict; Unsat (mk_unsat st us) let true_at_level0 st a = diff --git a/src/core/Solver_intf.ml b/src/core/Solver_intf.ml index a772e7c9..ab375a4d 100644 --- a/src/core/Solver_intf.ml +++ b/src/core/Solver_intf.ml @@ -444,6 +444,9 @@ module type S = sig (** Main solver type, containing all state for solving. *) val create : + ?on_conflict:(atom array -> unit) -> + ?on_decision:(atom -> unit) -> + ?on_new_atom:(atom -> unit) -> ?store_proof:bool -> ?size:[`Tiny|`Small|`Big] -> theory -> @@ -482,7 +485,6 @@ module type S = sig (** Lower level addition of clauses *) val solve : - ?on_conflict:(atom array -> unit) -> ?assumptions:atom list -> t -> res (** Try and solves the current set of clauses. From d024a6a3f0d30ced70f41ff1d6dec83133133616 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 01:07:31 -0400 Subject: [PATCH 173/182] prepare for vendoring --- .header | 5 -- .ocp-indent | 4 -- .travis.yml | 18 ------- Makefile | 48 ------------------ TODO.md | 17 ------- articles/icfp_2017.pdf | Bin 155520 -> 0 bytes articles/mcsat-vmcai2013.pdf | Bin 371980 -> 0 bytes articles/mcsat_design.pdf | Bin 378980 -> 0 bytes articles/minisat.pdf | Bin 327416 -> 0 bytes doc/deploy | 28 ---------- doc/index.txt | 10 ---- doc/release | 34 ------------- dune | 10 ---- dune-project | 1 - icnf-solve.sh | 3 -- msat-bin.opam | 23 --------- msat.opam | 24 --------- msat.sh | 3 -- .gitignore => src/sat/.gitignore | 0 CHANGELOG.md => src/sat/CHANGELOG.md | 0 LICENSE => src/sat/LICENSE | 0 README.md => src/sat/README.md | 0 src/sat/dune | 17 +++---- src/{ => sat/src}/backend/Backend_intf.ml | 0 src/{ => sat/src}/backend/Coq.ml | 0 src/{ => sat/src}/backend/Coq.mli | 0 src/{ => sat/src}/backend/Dedukti.ml | 0 src/{ => sat/src}/backend/Dedukti.mli | 0 src/{ => sat/src}/backend/Dot.ml | 0 src/{ => sat/src}/backend/Dot.mli | 0 src/{ => sat/src}/backend/dune | 0 .../src}/backtrack/Backtrackable_ref.ml | 0 .../src}/backtrack/Backtrackable_ref.mli | 0 src/{ => sat/src}/backtrack/Msat_backtrack.ml | 0 src/{ => sat/src}/backtrack/dune | 0 src/{ => sat/src}/core/Heap.ml | 0 src/{ => sat/src}/core/Heap.mli | 0 src/{ => sat/src}/core/Heap_intf.ml | 0 src/{ => sat/src}/core/Internal.ml | 0 src/{ => sat/src}/core/Log.ml | 0 src/{ => sat/src}/core/Log.mli | 0 src/{ => sat/src}/core/Msat.ml | 0 src/{ => sat/src}/core/Solver.ml | 0 src/{ => sat/src}/core/Solver.mli | 0 src/{ => sat/src}/core/Solver_intf.ml | 0 src/{ => sat/src}/core/Vec.ml | 0 src/{ => sat/src}/core/Vec.mli | 0 src/{ => sat/src}/core/dune | 0 src/{ => sat/src}/core/msat.mld | 0 src/{ => sat/src}/dune | 0 src/{ => sat/src}/index.mld | 0 src/{ => sat/src}/main/Dimacs_lex.mll | 0 src/{ => sat/src}/main/Dimacs_parse.mly | 0 src/{ => sat/src}/main/dune | 0 src/{ => sat/src}/main/main.ml | 0 src/sat/{ => src/sat}/Int_lit.ml | 0 src/sat/{ => src/sat}/Int_lit.mli | 0 src/sat/{ => src/sat}/Msat_sat.ml | 0 src/sat/{ => src/sat}/Msat_sat.mli | 0 src/sat/src/sat/dune | 11 ++++ src/{ => sat/src}/sudoku/dune | 0 src/{ => sat/src}/sudoku/sudoku_solve.ml | 0 src/{ => sat/src}/tseitin/Msat_tseitin.ml | 0 src/{ => sat/src}/tseitin/Msat_tseitin.mli | 0 src/{ => sat/src}/tseitin/Tseitin_intf.ml | 0 src/{ => sat/src}/tseitin/dune | 0 sudoku_solve.sh | 4 -- 67 files changed, 19 insertions(+), 241 deletions(-) delete mode 100644 .header delete mode 100644 .ocp-indent delete mode 100644 .travis.yml delete mode 100644 Makefile delete mode 100644 TODO.md delete mode 100644 articles/icfp_2017.pdf delete mode 100644 articles/mcsat-vmcai2013.pdf delete mode 100644 articles/mcsat_design.pdf delete mode 100644 articles/minisat.pdf delete mode 100755 doc/deploy delete mode 100644 doc/index.txt delete mode 100755 doc/release delete mode 100644 dune delete mode 100644 dune-project delete mode 100755 icnf-solve.sh delete mode 100644 msat-bin.opam delete mode 100644 msat.opam delete mode 100755 msat.sh rename .gitignore => src/sat/.gitignore (100%) rename CHANGELOG.md => src/sat/CHANGELOG.md (100%) rename LICENSE => src/sat/LICENSE (100%) rename README.md => src/sat/README.md (100%) rename src/{ => sat/src}/backend/Backend_intf.ml (100%) rename src/{ => sat/src}/backend/Coq.ml (100%) rename src/{ => sat/src}/backend/Coq.mli (100%) rename src/{ => sat/src}/backend/Dedukti.ml (100%) rename src/{ => sat/src}/backend/Dedukti.mli (100%) rename src/{ => sat/src}/backend/Dot.ml (100%) rename src/{ => sat/src}/backend/Dot.mli (100%) rename src/{ => sat/src}/backend/dune (100%) rename src/{ => sat/src}/backtrack/Backtrackable_ref.ml (100%) rename src/{ => sat/src}/backtrack/Backtrackable_ref.mli (100%) rename src/{ => sat/src}/backtrack/Msat_backtrack.ml (100%) rename src/{ => sat/src}/backtrack/dune (100%) rename src/{ => sat/src}/core/Heap.ml (100%) rename src/{ => sat/src}/core/Heap.mli (100%) rename src/{ => sat/src}/core/Heap_intf.ml (100%) rename src/{ => sat/src}/core/Internal.ml (100%) rename src/{ => sat/src}/core/Log.ml (100%) rename src/{ => sat/src}/core/Log.mli (100%) rename src/{ => sat/src}/core/Msat.ml (100%) rename src/{ => sat/src}/core/Solver.ml (100%) rename src/{ => sat/src}/core/Solver.mli (100%) rename src/{ => sat/src}/core/Solver_intf.ml (100%) rename src/{ => sat/src}/core/Vec.ml (100%) rename src/{ => sat/src}/core/Vec.mli (100%) rename src/{ => sat/src}/core/dune (100%) rename src/{ => sat/src}/core/msat.mld (100%) rename src/{ => sat/src}/dune (100%) rename src/{ => sat/src}/index.mld (100%) rename src/{ => sat/src}/main/Dimacs_lex.mll (100%) rename src/{ => sat/src}/main/Dimacs_parse.mly (100%) rename src/{ => sat/src}/main/dune (100%) rename src/{ => sat/src}/main/main.ml (100%) rename src/sat/{ => src/sat}/Int_lit.ml (100%) rename src/sat/{ => src/sat}/Int_lit.mli (100%) rename src/sat/{ => src/sat}/Msat_sat.ml (100%) rename src/sat/{ => src/sat}/Msat_sat.mli (100%) create mode 100644 src/sat/src/sat/dune rename src/{ => sat/src}/sudoku/dune (100%) rename src/{ => sat/src}/sudoku/sudoku_solve.ml (100%) rename src/{ => sat/src}/tseitin/Msat_tseitin.ml (100%) rename src/{ => sat/src}/tseitin/Msat_tseitin.mli (100%) rename src/{ => sat/src}/tseitin/Tseitin_intf.ml (100%) rename src/{ => sat/src}/tseitin/dune (100%) delete mode 100755 sudoku_solve.sh diff --git a/.header b/.header deleted file mode 100644 index fe8863b5..00000000 --- a/.header +++ /dev/null @@ -1,5 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) diff --git a/.ocp-indent b/.ocp-indent deleted file mode 100644 index 1bfc9294..00000000 --- a/.ocp-indent +++ /dev/null @@ -1,4 +0,0 @@ -base=2 -with=0 -type=2 -max_indent=4 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 69defb26..00000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: c -install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-docker.sh -script: bash -ex .travis-docker.sh -services: -- docker -env: - global: - - PINS="msat:. msat-bin:." - - DISTRO="ubuntu-16.04" - matrix: - - PACKAGE="msat" CAML_VERSION="4.03" - - PACKAGE="msat" CAML_VERSION="4.04" - #- PACKAGE="msat" CAML_VERSION="4.05" - - PACKAGE="msat" CAML_VERSION="4.06" - #- PACKAGE="msat" CAML_VERSION="4.08" - - PACKAGE="msat" CAML_VERSION="4.09" - - PACKAGE="msat" CAML_VERSION="4.10" - - PACKAGE="msat-bin" CAML_VERSION="4.06" TESTS=false diff --git a/Makefile b/Makefile deleted file mode 100644 index c8f09a05..00000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# copyright (c) 2014, guillaume bury -# copyright (c) 2017, simon cruanes - -J?=3 -OPTS= -j $(J) - -build: - @dune build $(OPTS) @install --profile=release - -dev: build-dev test - -build-dev: - @dune build $(OPTS) @install - -test: build-dev - @echo "run tests…" - @OCAMLRUNPARAM=b dune runtest --force --no-buffer - -clean: - @dune clean - -install: build-install - @dune install - -uninstall: - @dune uninstall - -doc: - @dune build $(OPTS) @doc - - -reinstall: | uninstall install - -ocp-indent: - @which ocp-indent > /dev/null || { \ - echo 'ocp-indent not found; please run `opam install ocp-indent`'; \ - exit 1 ; \ - } - -reindent: ocp-indent - @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 echo "reindenting: " - @find src '(' -name '*.ml' -or -name '*.mli' ')' -print0 | xargs -0 ocp-indent -i - -WATCH=all -watch: - @dune build @all -w - -.PHONY: clean doc all bench install uninstall remove reinstall bin test diff --git a/TODO.md b/TODO.md deleted file mode 100644 index ec05fdb5..00000000 --- a/TODO.md +++ /dev/null @@ -1,17 +0,0 @@ -# Goals - -## Main goals - -- Add a backend to send proofs to dedukti - * First, pure resolution proofs - * Then, require theories to output lemma proofs for dedukti (in some format yet to be decided) -- Allow to plug one's code into boolean propagation - * react upon propagation (possibly by propagating more, or side-effect) - * more advanced/specific propagation (2-clauses)? - * implement 'constraints' (see https://www.lri.fr/~conchon/TER/2013/3/minisat.pdf ) - -## Long term goals - -- max-sat/max-smt -- coq proofs ? - diff --git a/articles/icfp_2017.pdf b/articles/icfp_2017.pdf deleted file mode 100644 index 6251453fedffe8d9cedacfec893acb1f0c596dcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155520 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
      !C`hJ29V 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 Jimoc){{SxF?-~FA diff --git a/articles/mcsat-vmcai2013.pdf b/articles/mcsat-vmcai2013.pdf deleted file mode 100644 index e89e32d7a8482b538a2a577540120b61ebfaafe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 371980 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

      7X}Kbt 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 diff --git a/articles/mcsat_design.pdf b/articles/mcsat_design.pdf deleted file mode 100644 index 60ed51f0cd22890c76ad1a9795340e0595d9e46c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378980 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%0DRLk5qoXl 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

      $?88`@xj^)<(Px>TuowPYwT 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 XDyb*}W>RBw3j+g7E>%@me>W}wXjeNJ diff --git a/articles/minisat.pdf b/articles/minisat.pdf deleted file mode 100644 index 21de5cdf49381deaf15911ffaa7585dd41104ee7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327416 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


      Bt&)dR`4}g0xTJb7U{AnA=PRe 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%0Da^EQXb>mUvVB@ixM**=9 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 diff --git a/doc/deploy b/doc/deploy deleted file mode 100755 index 8a1cb92c..00000000 --- a/doc/deploy +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -e - -# Ensure we are on master branch -git checkout master - -# Generate documentation -make doc -(cd doc && asciidoc index.txt) - -# Checkout gh-pages -git checkout gh-pages -git pull - -# Copy doc to the right locations -cp doc/index.html ./ -cp msat.docdir/* ./dev/ - -# Add potentially new pages -git add ./dev/* -git add ./index.html - -# Commit it all & push -git commit -m 'Doc update' -git push - -# Get back to master branch -git checkout master - diff --git a/doc/index.txt b/doc/index.txt deleted file mode 100644 index 504ff7c5..00000000 --- a/doc/index.txt +++ /dev/null @@ -1,10 +0,0 @@ - -mSAT -==== -Guillaume Bury - -* link:dev[] -* link:0.5[] -* link:0.5.1[] -* link:0.6[] - diff --git a/doc/release b/doc/release deleted file mode 100755 index c4564484..00000000 --- a/doc/release +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -e - -# Get current verison number -version=`cat VERSION` - -# Enssure we are on master branch -git checkout master - -# Generate documentation -make doc -(cd doc && asciidoc index.txt) - -# Checkout gh-pages -git checkout gh-pages -git pull - -# Create doc folder if it does not exists -mkdir -p ./$version - -# Copy doc to the right locations -cp doc/index.html ./ -cp msat.docdir/* ./$version/ - -# Add potentially new pages -git add ./$version/* -git add ./index.html - -# Commit it all & push -git commit -m "Doc release $version" -git push - -# Get back to master branch -git checkout master - diff --git a/dune b/dune deleted file mode 100644 index 8b682629..00000000 --- a/dune +++ /dev/null @@ -1,10 +0,0 @@ - -(alias - (name runtest) - (deps README.md src/core/msat.cma src/sat/msat_sat.cma (source_tree src)) - (locks test) - (package msat) - (action (progn - (run mdx test README.md) - (diff? README.md README.md.corrected)))) - diff --git a/dune-project b/dune-project deleted file mode 100644 index 7655de07..00000000 --- a/dune-project +++ /dev/null @@ -1 +0,0 @@ -(lang dune 1.1) diff --git a/icnf-solve.sh b/icnf-solve.sh deleted file mode 100755 index b8c59e75..00000000 --- a/icnf-solve.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec dune exec tests/icnf-solve/icnf_solve.exe -- $@ diff --git a/msat-bin.opam b/msat-bin.opam deleted file mode 100644 index c31e7e3b..00000000 --- a/msat-bin.opam +++ /dev/null @@ -1,23 +0,0 @@ -opam-version: "2.0" -name: "msat-bin" -synopsis: "SAT solver binary based on the msat library" -license: "Apache-2.0" -version: "0.9.1" -author: ["Simon Cruanes" "Guillaume Bury"] -maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: [ - ["dune" "build" "@install" "-p" name "-j" jobs] - #["dune" "runtest" "-p" name "-j" jobs] {with-test} -] -depends: [ - "ocaml" { >= "4.03" } - "dune" { >= "1.1" } - "msat" { = version } - "containers" { >= "2.8.1" & < "4.0" } - "camlzip" -] -tags: [ "sat" ] -homepage: "https://github.com/Gbury/mSAT" -dev-repo: "git+https://github.com/Gbury/mSAT.git" -bug-reports: "https://github.com/Gbury/mSAT/issues/" - diff --git a/msat.opam b/msat.opam deleted file mode 100644 index 69093cae..00000000 --- a/msat.opam +++ /dev/null @@ -1,24 +0,0 @@ -opam-version: "2.0" -name: "msat" -synopsis: "Library containing a SAT solver that can be parametrized by a theory" -license: "Apache-2.0" -version: "0.9.1" -author: ["Simon Cruanes" "Guillaume Bury"] -maintainer: ["guillaume.bury@gmail.com" "simon.cruanes.2007@m4x.org"] -build: [ - ["dune" "build" "@install" "-p" name "-j" jobs] - ["dune" "build" "@doc" "-p" name] {with-doc} - ["dune" "runtest" "-p" name] {with-test} -] -depends: [ - "ocaml" { >= "4.03" } - "dune" { >= "1.1" } - "iter" { >= "1.2" } - "containers" {with-test & >= "2.8.1" & < "4.0" } - "mdx" {with-test} -] -tags: [ "sat" "smt" "cdcl" "functor" ] -homepage: "https://github.com/Gbury/mSAT" -dev-repo: "git+https://github.com/Gbury/mSAT.git" -bug-reports: "https://github.com/Gbury/mSAT/issues/" - diff --git a/msat.sh b/msat.sh deleted file mode 100755 index 6428999f..00000000 --- a/msat.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec dune exec --profile=release src/main/main.exe -- $@ diff --git a/.gitignore b/src/sat/.gitignore similarity index 100% rename from .gitignore rename to src/sat/.gitignore diff --git a/CHANGELOG.md b/src/sat/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to src/sat/CHANGELOG.md diff --git a/LICENSE b/src/sat/LICENSE similarity index 100% rename from LICENSE rename to src/sat/LICENSE diff --git a/README.md b/src/sat/README.md similarity index 100% rename from README.md rename to src/sat/README.md diff --git a/src/sat/dune b/src/sat/dune index 624b2a57..8b682629 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -1,11 +1,10 @@ -(library - (name msat_sat) - (public_name msat.sat) - (synopsis "purely boolean interface to Msat") - (libraries msat) - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -color always - -unbox-closures -unbox-closures-factor 20) - ) +(alias + (name runtest) + (deps README.md src/core/msat.cma src/sat/msat_sat.cma (source_tree src)) + (locks test) + (package msat) + (action (progn + (run mdx test README.md) + (diff? README.md README.md.corrected)))) diff --git a/src/backend/Backend_intf.ml b/src/sat/src/backend/Backend_intf.ml similarity index 100% rename from src/backend/Backend_intf.ml rename to src/sat/src/backend/Backend_intf.ml diff --git a/src/backend/Coq.ml b/src/sat/src/backend/Coq.ml similarity index 100% rename from src/backend/Coq.ml rename to src/sat/src/backend/Coq.ml diff --git a/src/backend/Coq.mli b/src/sat/src/backend/Coq.mli similarity index 100% rename from src/backend/Coq.mli rename to src/sat/src/backend/Coq.mli diff --git a/src/backend/Dedukti.ml b/src/sat/src/backend/Dedukti.ml similarity index 100% rename from src/backend/Dedukti.ml rename to src/sat/src/backend/Dedukti.ml diff --git a/src/backend/Dedukti.mli b/src/sat/src/backend/Dedukti.mli similarity index 100% rename from src/backend/Dedukti.mli rename to src/sat/src/backend/Dedukti.mli diff --git a/src/backend/Dot.ml b/src/sat/src/backend/Dot.ml similarity index 100% rename from src/backend/Dot.ml rename to src/sat/src/backend/Dot.ml diff --git a/src/backend/Dot.mli b/src/sat/src/backend/Dot.mli similarity index 100% rename from src/backend/Dot.mli rename to src/sat/src/backend/Dot.mli diff --git a/src/backend/dune b/src/sat/src/backend/dune similarity index 100% rename from src/backend/dune rename to src/sat/src/backend/dune diff --git a/src/backtrack/Backtrackable_ref.ml b/src/sat/src/backtrack/Backtrackable_ref.ml similarity index 100% rename from src/backtrack/Backtrackable_ref.ml rename to src/sat/src/backtrack/Backtrackable_ref.ml diff --git a/src/backtrack/Backtrackable_ref.mli b/src/sat/src/backtrack/Backtrackable_ref.mli similarity index 100% rename from src/backtrack/Backtrackable_ref.mli rename to src/sat/src/backtrack/Backtrackable_ref.mli diff --git a/src/backtrack/Msat_backtrack.ml b/src/sat/src/backtrack/Msat_backtrack.ml similarity index 100% rename from src/backtrack/Msat_backtrack.ml rename to src/sat/src/backtrack/Msat_backtrack.ml diff --git a/src/backtrack/dune b/src/sat/src/backtrack/dune similarity index 100% rename from src/backtrack/dune rename to src/sat/src/backtrack/dune diff --git a/src/core/Heap.ml b/src/sat/src/core/Heap.ml similarity index 100% rename from src/core/Heap.ml rename to src/sat/src/core/Heap.ml diff --git a/src/core/Heap.mli b/src/sat/src/core/Heap.mli similarity index 100% rename from src/core/Heap.mli rename to src/sat/src/core/Heap.mli diff --git a/src/core/Heap_intf.ml b/src/sat/src/core/Heap_intf.ml similarity index 100% rename from src/core/Heap_intf.ml rename to src/sat/src/core/Heap_intf.ml diff --git a/src/core/Internal.ml b/src/sat/src/core/Internal.ml similarity index 100% rename from src/core/Internal.ml rename to src/sat/src/core/Internal.ml diff --git a/src/core/Log.ml b/src/sat/src/core/Log.ml similarity index 100% rename from src/core/Log.ml rename to src/sat/src/core/Log.ml diff --git a/src/core/Log.mli b/src/sat/src/core/Log.mli similarity index 100% rename from src/core/Log.mli rename to src/sat/src/core/Log.mli diff --git a/src/core/Msat.ml b/src/sat/src/core/Msat.ml similarity index 100% rename from src/core/Msat.ml rename to src/sat/src/core/Msat.ml diff --git a/src/core/Solver.ml b/src/sat/src/core/Solver.ml similarity index 100% rename from src/core/Solver.ml rename to src/sat/src/core/Solver.ml diff --git a/src/core/Solver.mli b/src/sat/src/core/Solver.mli similarity index 100% rename from src/core/Solver.mli rename to src/sat/src/core/Solver.mli diff --git a/src/core/Solver_intf.ml b/src/sat/src/core/Solver_intf.ml similarity index 100% rename from src/core/Solver_intf.ml rename to src/sat/src/core/Solver_intf.ml diff --git a/src/core/Vec.ml b/src/sat/src/core/Vec.ml similarity index 100% rename from src/core/Vec.ml rename to src/sat/src/core/Vec.ml diff --git a/src/core/Vec.mli b/src/sat/src/core/Vec.mli similarity index 100% rename from src/core/Vec.mli rename to src/sat/src/core/Vec.mli diff --git a/src/core/dune b/src/sat/src/core/dune similarity index 100% rename from src/core/dune rename to src/sat/src/core/dune diff --git a/src/core/msat.mld b/src/sat/src/core/msat.mld similarity index 100% rename from src/core/msat.mld rename to src/sat/src/core/msat.mld diff --git a/src/dune b/src/sat/src/dune similarity index 100% rename from src/dune rename to src/sat/src/dune diff --git a/src/index.mld b/src/sat/src/index.mld similarity index 100% rename from src/index.mld rename to src/sat/src/index.mld diff --git a/src/main/Dimacs_lex.mll b/src/sat/src/main/Dimacs_lex.mll similarity index 100% rename from src/main/Dimacs_lex.mll rename to src/sat/src/main/Dimacs_lex.mll diff --git a/src/main/Dimacs_parse.mly b/src/sat/src/main/Dimacs_parse.mly similarity index 100% rename from src/main/Dimacs_parse.mly rename to src/sat/src/main/Dimacs_parse.mly diff --git a/src/main/dune b/src/sat/src/main/dune similarity index 100% rename from src/main/dune rename to src/sat/src/main/dune diff --git a/src/main/main.ml b/src/sat/src/main/main.ml similarity index 100% rename from src/main/main.ml rename to src/sat/src/main/main.ml diff --git a/src/sat/Int_lit.ml b/src/sat/src/sat/Int_lit.ml similarity index 100% rename from src/sat/Int_lit.ml rename to src/sat/src/sat/Int_lit.ml diff --git a/src/sat/Int_lit.mli b/src/sat/src/sat/Int_lit.mli similarity index 100% rename from src/sat/Int_lit.mli rename to src/sat/src/sat/Int_lit.mli diff --git a/src/sat/Msat_sat.ml b/src/sat/src/sat/Msat_sat.ml similarity index 100% rename from src/sat/Msat_sat.ml rename to src/sat/src/sat/Msat_sat.ml diff --git a/src/sat/Msat_sat.mli b/src/sat/src/sat/Msat_sat.mli similarity index 100% rename from src/sat/Msat_sat.mli rename to src/sat/src/sat/Msat_sat.mli diff --git a/src/sat/src/sat/dune b/src/sat/src/sat/dune new file mode 100644 index 00000000..624b2a57 --- /dev/null +++ b/src/sat/src/sat/dune @@ -0,0 +1,11 @@ + +(library + (name msat_sat) + (public_name msat.sat) + (synopsis "purely boolean interface to Msat") + (libraries msat) + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) + (ocamlopt_flags :standard -O3 -color always + -unbox-closures -unbox-closures-factor 20) + ) + diff --git a/src/sudoku/dune b/src/sat/src/sudoku/dune similarity index 100% rename from src/sudoku/dune rename to src/sat/src/sudoku/dune diff --git a/src/sudoku/sudoku_solve.ml b/src/sat/src/sudoku/sudoku_solve.ml similarity index 100% rename from src/sudoku/sudoku_solve.ml rename to src/sat/src/sudoku/sudoku_solve.ml diff --git a/src/tseitin/Msat_tseitin.ml b/src/sat/src/tseitin/Msat_tseitin.ml similarity index 100% rename from src/tseitin/Msat_tseitin.ml rename to src/sat/src/tseitin/Msat_tseitin.ml diff --git a/src/tseitin/Msat_tseitin.mli b/src/sat/src/tseitin/Msat_tseitin.mli similarity index 100% rename from src/tseitin/Msat_tseitin.mli rename to src/sat/src/tseitin/Msat_tseitin.mli diff --git a/src/tseitin/Tseitin_intf.ml b/src/sat/src/tseitin/Tseitin_intf.ml similarity index 100% rename from src/tseitin/Tseitin_intf.ml rename to src/sat/src/tseitin/Tseitin_intf.ml diff --git a/src/tseitin/dune b/src/sat/src/tseitin/dune similarity index 100% rename from src/tseitin/dune rename to src/sat/src/tseitin/dune diff --git a/sudoku_solve.sh b/sudoku_solve.sh deleted file mode 100755 index 93e8a2fa..00000000 --- a/sudoku_solve.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -#exec dune exec src/sudoku/sudoku_solve.exe -- $@ -exec dune exec --profile=release src/sudoku/sudoku_solve.exe -- $@ From 4a337a85d3a6851c4062bb016b96e709ad54c983 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 01:29:28 -0400 Subject: [PATCH 174/182] cleanup msat --- src/{sat/src => }/backend/Backend_intf.ml | 0 src/{sat/src => }/backend/Coq.ml | 0 src/{sat/src => }/backend/Coq.mli | 0 src/{sat/src => }/backend/Dedukti.ml | 0 src/{sat/src => }/backend/Dedukti.mli | 0 src/{sat/src => }/backend/Dot.ml | 0 src/{sat/src => }/backend/Dot.mli | 0 src/{sat/src => }/backend/dune | 0 .../src => }/backtrack/Backtrackable_ref.ml | 0 .../src => }/backtrack/Backtrackable_ref.mli | 0 src/{sat/src => }/backtrack/Msat_backtrack.ml | 0 src/{sat/src => }/backtrack/dune | 0 src/msat-solver/dune | 2 +- src/sat/{src/core => }/Heap.ml | 0 src/sat/{src/core => }/Heap.mli | 0 src/sat/{src/core => }/Heap_intf.ml | 0 src/sat/{src/core => }/Internal.ml | 0 src/sat/{src/core => }/Log.ml | 0 src/sat/{src/core => }/Log.mli | 0 src/sat/{src/core => }/Msat.ml | 0 src/sat/{src/core => }/Solver.ml | 0 src/sat/{src/core => }/Solver.mli | 0 src/sat/{src/core => }/Solver_intf.ml | 0 src/sat/{src/core => }/Vec.ml | 0 src/sat/{src/core => }/Vec.mli | 0 src/sat/dune | 18 +- src/sat/{src/core => }/msat.mld | 0 src/sat/src/core/dune | 10 - src/sat/src/dune | 4 - src/sat/src/index.mld | 121 ------- src/sat/src/main/Dimacs_lex.mll | 20 -- src/sat/src/main/Dimacs_parse.mly | 34 -- src/sat/src/main/dune | 14 - src/sat/src/main/main.ml | 186 ---------- src/sat/src/sat/Int_lit.ml | 66 ---- src/sat/src/sat/Int_lit.mli | 33 -- src/sat/src/sat/Msat_sat.ml | 11 - src/sat/src/sat/Msat_sat.mli | 19 - src/sat/src/sat/dune | 11 - src/sat/src/sudoku/dune | 9 - src/sat/src/sudoku/sudoku_solve.ml | 331 ------------------ src/sat/src/tseitin/Msat_tseitin.ml | 326 ----------------- src/sat/src/tseitin/Msat_tseitin.mli | 22 -- src/sat/src/tseitin/Tseitin_intf.ml | 85 ----- src/sat/src/tseitin/dune | 11 - 45 files changed, 10 insertions(+), 1323 deletions(-) rename src/{sat/src => }/backend/Backend_intf.ml (100%) rename src/{sat/src => }/backend/Coq.ml (100%) rename src/{sat/src => }/backend/Coq.mli (100%) rename src/{sat/src => }/backend/Dedukti.ml (100%) rename src/{sat/src => }/backend/Dedukti.mli (100%) rename src/{sat/src => }/backend/Dot.ml (100%) rename src/{sat/src => }/backend/Dot.mli (100%) rename src/{sat/src => }/backend/dune (100%) rename src/{sat/src => }/backtrack/Backtrackable_ref.ml (100%) rename src/{sat/src => }/backtrack/Backtrackable_ref.mli (100%) rename src/{sat/src => }/backtrack/Msat_backtrack.ml (100%) rename src/{sat/src => }/backtrack/dune (100%) rename src/sat/{src/core => }/Heap.ml (100%) rename src/sat/{src/core => }/Heap.mli (100%) rename src/sat/{src/core => }/Heap_intf.ml (100%) rename src/sat/{src/core => }/Internal.ml (100%) rename src/sat/{src/core => }/Log.ml (100%) rename src/sat/{src/core => }/Log.mli (100%) rename src/sat/{src/core => }/Msat.ml (100%) rename src/sat/{src/core => }/Solver.ml (100%) rename src/sat/{src/core => }/Solver.mli (100%) rename src/sat/{src/core => }/Solver_intf.ml (100%) rename src/sat/{src/core => }/Vec.ml (100%) rename src/sat/{src/core => }/Vec.mli (100%) rename src/sat/{src/core => }/msat.mld (100%) delete mode 100644 src/sat/src/core/dune delete mode 100644 src/sat/src/dune delete mode 100644 src/sat/src/index.mld delete mode 100644 src/sat/src/main/Dimacs_lex.mll delete mode 100644 src/sat/src/main/Dimacs_parse.mly delete mode 100644 src/sat/src/main/dune delete mode 100644 src/sat/src/main/main.ml delete mode 100644 src/sat/src/sat/Int_lit.ml delete mode 100644 src/sat/src/sat/Int_lit.mli delete mode 100644 src/sat/src/sat/Msat_sat.ml delete mode 100644 src/sat/src/sat/Msat_sat.mli delete mode 100644 src/sat/src/sat/dune delete mode 100644 src/sat/src/sudoku/dune delete mode 100644 src/sat/src/sudoku/sudoku_solve.ml delete mode 100644 src/sat/src/tseitin/Msat_tseitin.ml delete mode 100644 src/sat/src/tseitin/Msat_tseitin.mli delete mode 100644 src/sat/src/tseitin/Tseitin_intf.ml delete mode 100644 src/sat/src/tseitin/dune diff --git a/src/sat/src/backend/Backend_intf.ml b/src/backend/Backend_intf.ml similarity index 100% rename from src/sat/src/backend/Backend_intf.ml rename to src/backend/Backend_intf.ml diff --git a/src/sat/src/backend/Coq.ml b/src/backend/Coq.ml similarity index 100% rename from src/sat/src/backend/Coq.ml rename to src/backend/Coq.ml diff --git a/src/sat/src/backend/Coq.mli b/src/backend/Coq.mli similarity index 100% rename from src/sat/src/backend/Coq.mli rename to src/backend/Coq.mli diff --git a/src/sat/src/backend/Dedukti.ml b/src/backend/Dedukti.ml similarity index 100% rename from src/sat/src/backend/Dedukti.ml rename to src/backend/Dedukti.ml diff --git a/src/sat/src/backend/Dedukti.mli b/src/backend/Dedukti.mli similarity index 100% rename from src/sat/src/backend/Dedukti.mli rename to src/backend/Dedukti.mli diff --git a/src/sat/src/backend/Dot.ml b/src/backend/Dot.ml similarity index 100% rename from src/sat/src/backend/Dot.ml rename to src/backend/Dot.ml diff --git a/src/sat/src/backend/Dot.mli b/src/backend/Dot.mli similarity index 100% rename from src/sat/src/backend/Dot.mli rename to src/backend/Dot.mli diff --git a/src/sat/src/backend/dune b/src/backend/dune similarity index 100% rename from src/sat/src/backend/dune rename to src/backend/dune diff --git a/src/sat/src/backtrack/Backtrackable_ref.ml b/src/backtrack/Backtrackable_ref.ml similarity index 100% rename from src/sat/src/backtrack/Backtrackable_ref.ml rename to src/backtrack/Backtrackable_ref.ml diff --git a/src/sat/src/backtrack/Backtrackable_ref.mli b/src/backtrack/Backtrackable_ref.mli similarity index 100% rename from src/sat/src/backtrack/Backtrackable_ref.mli rename to src/backtrack/Backtrackable_ref.mli diff --git a/src/sat/src/backtrack/Msat_backtrack.ml b/src/backtrack/Msat_backtrack.ml similarity index 100% rename from src/sat/src/backtrack/Msat_backtrack.ml rename to src/backtrack/Msat_backtrack.ml diff --git a/src/sat/src/backtrack/dune b/src/backtrack/dune similarity index 100% rename from src/sat/src/backtrack/dune rename to src/backtrack/dune diff --git a/src/msat-solver/dune b/src/msat-solver/dune index 2a945b95..8e6100cd 100644 --- a/src/msat-solver/dune +++ b/src/msat-solver/dune @@ -2,5 +2,5 @@ (name Sidekick_msat_solver) (public_name sidekick.msat-solver) (libraries containers iter sidekick.core sidekick.util - sidekick.cc msat msat.backend) + sidekick.cc sidekick.sat) (flags :standard -open Sidekick_util)) diff --git a/src/sat/src/core/Heap.ml b/src/sat/Heap.ml similarity index 100% rename from src/sat/src/core/Heap.ml rename to src/sat/Heap.ml diff --git a/src/sat/src/core/Heap.mli b/src/sat/Heap.mli similarity index 100% rename from src/sat/src/core/Heap.mli rename to src/sat/Heap.mli diff --git a/src/sat/src/core/Heap_intf.ml b/src/sat/Heap_intf.ml similarity index 100% rename from src/sat/src/core/Heap_intf.ml rename to src/sat/Heap_intf.ml diff --git a/src/sat/src/core/Internal.ml b/src/sat/Internal.ml similarity index 100% rename from src/sat/src/core/Internal.ml rename to src/sat/Internal.ml diff --git a/src/sat/src/core/Log.ml b/src/sat/Log.ml similarity index 100% rename from src/sat/src/core/Log.ml rename to src/sat/Log.ml diff --git a/src/sat/src/core/Log.mli b/src/sat/Log.mli similarity index 100% rename from src/sat/src/core/Log.mli rename to src/sat/Log.mli diff --git a/src/sat/src/core/Msat.ml b/src/sat/Msat.ml similarity index 100% rename from src/sat/src/core/Msat.ml rename to src/sat/Msat.ml diff --git a/src/sat/src/core/Solver.ml b/src/sat/Solver.ml similarity index 100% rename from src/sat/src/core/Solver.ml rename to src/sat/Solver.ml diff --git a/src/sat/src/core/Solver.mli b/src/sat/Solver.mli similarity index 100% rename from src/sat/src/core/Solver.mli rename to src/sat/Solver.mli diff --git a/src/sat/src/core/Solver_intf.ml b/src/sat/Solver_intf.ml similarity index 100% rename from src/sat/src/core/Solver_intf.ml rename to src/sat/Solver_intf.ml diff --git a/src/sat/src/core/Vec.ml b/src/sat/Vec.ml similarity index 100% rename from src/sat/src/core/Vec.ml rename to src/sat/Vec.ml diff --git a/src/sat/src/core/Vec.mli b/src/sat/Vec.mli similarity index 100% rename from src/sat/src/core/Vec.mli rename to src/sat/Vec.mli diff --git a/src/sat/dune b/src/sat/dune index 8b682629..85a23d9a 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -1,10 +1,10 @@ -(alias - (name runtest) - (deps README.md src/core/msat.cma src/sat/msat_sat.cma (source_tree src)) - (locks test) - (package msat) - (action (progn - (run mdx test README.md) - (diff? README.md README.md.corrected)))) - +(library + (name msat) + (public_name msat) + (libraries iter) + (synopsis "core data structures and algorithms for msat") + (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (ocamlopt_flags :standard -O3 -bin-annot + -unbox-closures -unbox-closures-factor 20) + ) diff --git a/src/sat/src/core/msat.mld b/src/sat/msat.mld similarity index 100% rename from src/sat/src/core/msat.mld rename to src/sat/msat.mld diff --git a/src/sat/src/core/dune b/src/sat/src/core/dune deleted file mode 100644 index 85a23d9a..00000000 --- a/src/sat/src/core/dune +++ /dev/null @@ -1,10 +0,0 @@ - -(library - (name msat) - (public_name msat) - (libraries iter) - (synopsis "core data structures and algorithms for msat") - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) diff --git a/src/sat/src/dune b/src/sat/src/dune deleted file mode 100644 index 05c64298..00000000 --- a/src/sat/src/dune +++ /dev/null @@ -1,4 +0,0 @@ - -(documentation - (package msat) - (mld_files :standard)) diff --git a/src/sat/src/index.mld b/src/sat/src/index.mld deleted file mode 100644 index 38b0a9cc..00000000 --- a/src/sat/src/index.mld +++ /dev/null @@ -1,121 +0,0 @@ - - -{1 mSAT: a Modular SAT Solver} - -(The entry point of this library is the module: {!module-Msat}.) - -A modular implementation of the SMT algorithm can be found in the {!Msat.Solver} module, -as a functor which takes two modules : - -- A representation of formulas (which implements the `Formula_intf.S` signature) - -- A theory (which implements the `Theory_intf.S` signature) to check consistence of assertions. - -- A dummy empty module to ensure generativity of the solver (solver modules heavily relies on -side effects to their internal state) - -{3 Sat Solver} - -A ready-to-use SAT solver is available in the {!Msat_sat} module -using the [msat.sat] library (see {!module-Msat_sat}). It can be loaded -as shown in the following code : - -{[ -# #require "msat";; -# #require "msat.sat";; -# #print_depth 0;; (* do not print details *) -]} - -Then we can create a solver and create some boolean variables: - -{[ -module Sat = Msat_sat -module E = Sat.Int_lit (* expressions *) - -let solver = Sat.create() - -(* We create here two distinct atoms *) -let a = E.fresh () (* A 'new_atom' is always distinct from any other atom *) -let b = E.make 1 (* Atoms can be created from integers *) -]} - -We can try and check the satisfiability of some clauses — here, the clause [a or b]. -[Sat.assume] adds a list of clauses to the solver. Calling [Sat.solve] -will check the satisfiability of the current set of clauses, here "Sat". - -{[ -# a <> b;; -- : bool = true -# Sat.assume solver [[a; b]] ();; -- : unit = () -# let res = Sat.solve solver;; -val res : Sat.res = Sat.Sat ... -]} - -The Sat solver has an incremental mutable state, so we still have -the clause `a or b` in our assumptions. -We add `not a` and `not b` to the state, and get "Unsat". - -{[ -# Sat.assume solver [[E.neg a]; [E.neg b]] () ;; -- : unit = () -# let res = Sat.solve solver ;; -val res : Sat.res = Sat.Unsat ... -]} - -{3 Formulas API} - -Writing clauses by hand can be tedious and error-prone. -The functor {!Msat_tseitin.Make} in the library [msat.tseitin] (see {!module-Msat_tseitin}). -proposes a formula AST (parametrized by -atoms) and a function to convert these formulas into clauses: - -{[ -# #require "msat.tseitin";; -]} - -{[ -(* Module initialization *) -module F = Msat_tseitin.Make(E) - -let solver = Sat.create () - -(* We create here two distinct atoms *) -let a = E.fresh () (* A fresh atom is always distinct from any other atom *) -let b = E.make 1 (* Atoms can be created from integers *) - -(* Let's create some formulas *) -let p = F.make_atom a -let q = F.make_atom b -let r = F.make_and [p; q] -let s = F.make_or [F.make_not p; F.make_not q] -]} - -We can try and check the satisfiability of the given formulas, by turning -it into clauses using `make_cnf`: - -{[ -# Sat.assume solver (F.make_cnf r) ();; -- : unit = () -# Sat.solve solver;; -- : Sat.res = Sat.Sat ... -]} - -{[ -# Sat.assume solver (F.make_cnf s) ();; -- : unit = () -# Sat.solve solver ;; -- : Sat.res = Sat.Unsat ... -]} - -{3 Backtracking utils} - -The library {!module-Msat_backtrack} contains some backtrackable -data structures that are useful for implementing theories. - -{3 Library msat.backend} - -This is used for proof backends: - -The entry point of this library is the module: -{!module-Msat_backend}. diff --git a/src/sat/src/main/Dimacs_lex.mll b/src/sat/src/main/Dimacs_lex.mll deleted file mode 100644 index 67850c1f..00000000 --- a/src/sat/src/main/Dimacs_lex.mll +++ /dev/null @@ -1,20 +0,0 @@ -{ - open Dimacs_parse -} - -let number = ['1' - '9'] ['0' - '9']* - -rule token = parse - | eof { EOF } - | "c" { comment lexbuf } - | [' ' '\t' '\r'] { token lexbuf } - | 'p' { P } - | "cnf" { CNF } - | '\n' { Lexing.new_line lexbuf; token lexbuf } - | '0' { ZERO } - | '-'? number { LIT (int_of_string (Lexing.lexeme lexbuf)) } - | _ { failwith @@ Printf.sprintf "dimacs.lexer: unexpected char `%s`" (Lexing.lexeme lexbuf) } - -and comment = parse - | '\n' { Lexing.new_line lexbuf; token lexbuf } - | [^'\n'] { comment lexbuf } diff --git a/src/sat/src/main/Dimacs_parse.mly b/src/sat/src/main/Dimacs_parse.mly deleted file mode 100644 index 408b8db9..00000000 --- a/src/sat/src/main/Dimacs_parse.mly +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2005 INRIA */ - -%{ - let lnum pos = pos.Lexing.pos_lnum - let cnum pos = pos.Lexing.pos_cnum - pos.Lexing.pos_bol - let pp_pos out (start,stop) = - Format.fprintf out "(at %d:%d - %d:%d)" - (lnum start) (cnum start) (lnum stop) (cnum stop) -%} - -%token LIT -%token ZERO -%token P CNF EOF - -%start file -%type file - -%% - -/* DIMACS syntax */ - -prelude: - | P CNF LIT LIT { () } - -clauses: - | { [] } - | clause clauses { $1 :: $2 } - -file: - | prelude clauses EOF { $2 } - -clause: - | ZERO { [] } - | LIT clause { $1 :: $2 } diff --git a/src/sat/src/main/dune b/src/sat/src/main/dune deleted file mode 100644 index 073ec30f..00000000 --- a/src/sat/src/main/dune +++ /dev/null @@ -1,14 +0,0 @@ - -; main binary -(executable - (name main) - (public_name msat) - (package msat-bin) - (libraries containers camlzip msat msat.sat msat.backend) - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -color always - -unbox-closures -unbox-closures-factor 20) - ) - -(ocamlyacc (modules Dimacs_parse)) -(ocamllex (modules Dimacs_lex)) diff --git a/src/sat/src/main/main.ml b/src/sat/src/main/main.ml deleted file mode 100644 index ddc1d856..00000000 --- a/src/sat/src/main/main.ml +++ /dev/null @@ -1,186 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -exception Incorrect_model -exception Out_of_time -exception Out_of_space - -let file = ref "" -let p_cnf = ref false -let p_check = ref false -let p_dot_proof = ref "" -let p_proof_print = ref false -let time_limit = ref 300. -let size_limit = ref 1000_000_000. -let no_proof = ref false - -module S = Msat_sat - -module Process() = struct - module D = Msat_backend.Dot.Make(S)(Msat_backend.Dot.Default(S)) - - let hyps = ref [] - - let st = S.create ~store_proof:(not !no_proof) ~size:`Big () - - let check_model sat = - let check_clause c = - let l = List.map (function a -> - Log.debugf 99 - (fun k -> k "Checking value of %a" S.Formula.pp a); - sat.Msat.eval a) c in - List.exists (fun x -> x) l - in - let l = List.map check_clause !hyps in - List.for_all (fun x -> x) l - - let prove ~assumptions () = - let res = S.solve ~assumptions st in - let t = Sys.time () in - begin match res with - | S.Sat state -> - if !p_check then - if not (check_model state) then - raise Incorrect_model; - let t' = Sys.time () -. t in - Format.printf "Sat (%f/%f)@." t t' - | S.Unsat state -> - if !p_check then ( - let p = state.Msat.get_proof () in - S.Proof.check_empty_conclusion p; - S.Proof.check p; - if !p_dot_proof <> "" then ( - let oc = open_out !p_dot_proof in - let fmt = Format.formatter_of_out_channel oc in - Format.fprintf fmt "%a@?" D.pp p; - flush oc; close_out_noerr oc; - ) - ); - let t' = Sys.time () -. t in - Format.printf "Unsat (%f/%f)@." t t' - end - - let conv_c c = List.rev_map S.Int_lit.make c - - let add_clauses cs = - S.assume st (CCList.map conv_c cs) () -end[@@inline] - -let parse_file f = - let module L = Lexing in - CCIO.with_in f - (fun ic -> - let buf = - if CCString.suffix ~suf:".gz" f - then ( - let gic = Gzip.open_in_chan ic in - L.from_function (fun bytes len -> Gzip.input gic bytes 0 len) - ) else L.from_channel ic - in - buf.L.lex_curr_p <- {buf.L.lex_curr_p with L.pos_fname=f;}; - Dimacs_parse.file Dimacs_lex.token buf) - -let error_msg opt arg l = - Format.fprintf Format.str_formatter "'%s' is not a valid argument for '%s', valid arguments are : %a" - arg opt (fun fmt -> List.iter (fun (s, _) -> Format.fprintf fmt "%s, " s)) l; - Format.flush_str_formatter () - -(* Arguments parsing *) -let int_arg r arg = - let l = String.length arg in - let multiplier m = - let arg1 = String.sub arg 0 (l-1) in - r := m *. (float_of_string arg1) - in - if l = 0 then raise (Arg.Bad "bad numeric argument") - else - try - match arg.[l-1] with - | 'k' -> multiplier 1e3 - | 'M' -> multiplier 1e6 - | 'G' -> multiplier 1e9 - | 'T' -> multiplier 1e12 - | 's' -> multiplier 1. - | 'm' -> multiplier 60. - | 'h' -> multiplier 3600. - | 'd' -> multiplier 86400. - | '0'..'9' -> r := float_of_string arg - | _ -> raise (Arg.Bad "bad numeric argument") - with Failure _ -> raise (Arg.Bad "bad numeric argument") - -let setup_gc_stat () = - at_exit (fun () -> - Gc.print_stat stdout; - ) - -let input_file = fun s -> file := s - -let usage = "Usage : main [options] " -let argspec = Arg.align [ - "-bt", Arg.Unit (fun () -> Printexc.record_backtrace true), - " Enable stack traces"; - "-cnf", Arg.Set p_cnf, - " Prints the cnf used."; - "-check", Arg.Set p_check, - " Build, check and print the proof (if output is set), if unsat"; - "-dot", Arg.Set_string p_dot_proof, - " If provided, print the dot proof in the given file"; - "-gc", Arg.Unit setup_gc_stat, - " Outputs statistics about the GC"; - "-size", Arg.String (int_arg size_limit), - "[kMGT] Sets the size limit for the sat solver"; - "-time", Arg.String (int_arg time_limit), - "[smhd] Sets the time limit for the sat solver"; - "-v", Arg.Int (fun i -> Log.set_debug i), - " Sets the debug verbose level"; - "-no-proof", Arg.Set no_proof, " disable proof logging"; - ] - -(* Limits alarm *) -let check () = - let t = Sys.time () in - let heap_size = (Gc.quick_stat ()).Gc.heap_words in - let s = float heap_size *. float Sys.word_size /. 8. in - if t > !time_limit then - raise Out_of_time - else if s > !size_limit then - raise Out_of_space - -let main () = - (* Administrative duties *) - Arg.parse argspec input_file usage; - if !file = "" then ( - Arg.usage argspec usage; - exit 2 - ); - let al = Gc.create_alarm check in - - let module P = Process() in - - (* Interesting stuff happening *) - let clauses = parse_file !file in - P.add_clauses clauses; - P.prove ~assumptions:[] (); - Gc.delete_alarm al; - () - -let () = - try - main () - with - | Out_of_time -> - Format.printf "Timeout@."; - exit 2 - | Out_of_space -> - Format.printf "Spaceout@."; - exit 3 - | Incorrect_model -> - Format.printf "Internal error : incorrect *sat* model@."; - exit 4 - | S.Proof.Resolution_error msg -> - Format.printf "Internal error: incorrect *unsat* proof:\n%s@." msg; - exit 5 - diff --git a/src/sat/src/sat/Int_lit.ml b/src/sat/src/sat/Int_lit.ml deleted file mode 100644 index 9f01aaeb..00000000 --- a/src/sat/src/sat/Int_lit.ml +++ /dev/null @@ -1,66 +0,0 @@ - -exception Bad_atom -(** Exception raised if an atom cannot be created *) - -type t = int -(** Atoms are represented as integers. [-i] begin the negation of [i]. - Additionally, since we nee dot be able to create fresh atoms, we - use even integers for user-created atoms, and odd integers for the - fresh atoms. *) - -let max_lit = max_int - -(* Counters *) -let max_index = ref 0 -let max_fresh = ref (-1) - -(** Internal function for creating atoms. - Updates the internal counters *) -let _make i = - if i <> 0 && (abs i) < max_lit then begin - max_index := max !max_index (abs i); - i - end else - raise Bad_atom - -let to_int i = i - -(** *) -let neg a = - a - -let norm a = - abs a, if a < 0 then - Solver_intf.Negated - else - Solver_intf.Same_sign - -let abs = abs - -let sign i = i > 0 - -let apply_sign b i = if b then i else neg i - -let set_sign b i = if b then abs i else neg (abs i) - -let hash (a:int) = a land max_int -let equal (a:int) b = a=b -let compare (a:int) b = compare a b - -let make i = _make (2 * i) - -let fresh () = - incr max_fresh; - _make (2 * !max_fresh + 1) - -(* -let iter: (t -> unit) -> unit = fun f -> - for j = 1 to !max_index do - f j - done -*) - -let pp fmt a = - Format.fprintf fmt "%s%s%d" - (if a < 0 then "~" else "") - (if a mod 2 = 0 then "v" else "f") - ((abs a) / 2) diff --git a/src/sat/src/sat/Int_lit.mli b/src/sat/src/sat/Int_lit.mli deleted file mode 100644 index 6845d161..00000000 --- a/src/sat/src/sat/Int_lit.mli +++ /dev/null @@ -1,33 +0,0 @@ - -(** {1 The module defining formulas} *) - -(** SAT Formulas - - This modules implements formuals adequate for use in a pure SAT Solver. - Atomic formuals are represented using integers, that should allow - near optimal efficiency (both in terms of space and time). -*) - -include Solver_intf.FORMULA -(** This modules implements the requirements for implementing an SAT Solver. *) - -val make : int -> t -(** Make a proposition from an integer. *) - -val to_int : t -> int - -val fresh : unit -> t -(** Make a fresh atom *) - -val compare : t -> t -> int -(** Compare atoms *) - -val sign : t -> bool -(** Is the given atom positive ? *) - -val apply_sign : bool -> t -> t -(** [apply_sign b] is the identity if [b] is [true], and the negation - function if [b] is [false]. *) - -val set_sign : bool -> t -> t -(** Return the atom with the sign set. *) diff --git a/src/sat/src/sat/Msat_sat.ml b/src/sat/src/sat/Msat_sat.ml deleted file mode 100644 index 91cc70c6..00000000 --- a/src/sat/src/sat/Msat_sat.ml +++ /dev/null @@ -1,11 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -*) - -module Int_lit = Int_lit -include Msat.Make_pure_sat(struct - module Formula = Int_lit - type proof = unit - end) - diff --git a/src/sat/src/sat/Msat_sat.mli b/src/sat/src/sat/Msat_sat.mli deleted file mode 100644 index 0517e913..00000000 --- a/src/sat/src/sat/Msat_sat.mli +++ /dev/null @@ -1,19 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -*) - -(** Sat solver - - This modules instanciates a pure sat solver using integers to represent - atomic propositions. -*) - -module Int_lit = Int_lit - -include Msat.S - with type Formula.t = Int_lit.t - and type theory = unit - and type lemma = unit -(** A functor that can generate as many solvers as needed. *) - diff --git a/src/sat/src/sat/dune b/src/sat/src/sat/dune deleted file mode 100644 index 624b2a57..00000000 --- a/src/sat/src/sat/dune +++ /dev/null @@ -1,11 +0,0 @@ - -(library - (name msat_sat) - (public_name msat.sat) - (synopsis "purely boolean interface to Msat") - (libraries msat) - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -color always - -unbox-closures -unbox-closures-factor 20) - ) - diff --git a/src/sat/src/sudoku/dune b/src/sat/src/sudoku/dune deleted file mode 100644 index 000c407f..00000000 --- a/src/sat/src/sudoku/dune +++ /dev/null @@ -1,9 +0,0 @@ - -(executable - (name sudoku_solve) - (modes native) - (libraries msat msat.backtrack iter containers) - (flags :standard -warn-error -a -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) -) diff --git a/src/sat/src/sudoku/sudoku_solve.ml b/src/sat/src/sudoku/sudoku_solve.ml deleted file mode 100644 index a75ace03..00000000 --- a/src/sat/src/sudoku/sudoku_solve.ml +++ /dev/null @@ -1,331 +0,0 @@ - -(** {1 simple sudoku solver} *) - -module Fmt = CCFormat -module Log = Msat.Log -module Vec = Msat.Vec - -let errorf msg = CCFormat.kasprintf failwith msg - -module Cell : sig - type t = private int - val equal : t -> t -> bool - val neq : t -> t -> bool - val hash : t -> int - val empty : t - val is_empty : t -> bool - val is_full : t -> bool - val make : int -> t - val pp : t Fmt.printer -end = struct - type t = int - let empty = 0 - let[@inline] make i = assert (i >= 0 && i <= 9); i - let[@inline] is_empty x = x = 0 - let[@inline] is_full x = x > 0 - let hash = CCHash.int - let[@inline] equal (a:t) b = a=b - let[@inline] neq (a:t) b = a<>b - let pp out i = if i=0 then Fmt.char out '.' else Fmt.int out i -end - -module Grid : sig - type t - - val get : t -> int -> int -> Cell.t - val set : t -> int -> int -> Cell.t -> t - - (** A set of related cells *) - type set = (int*int*Cell.t) Iter.t - - val rows : t -> set Iter.t - val cols : t -> set Iter.t - val squares : t -> set Iter.t - - val all_cells : t -> (int*int*Cell.t) Iter.t - - val parse : string -> t - val is_full : t -> bool - val is_valid : t -> bool - val matches : pat:t -> t -> bool - val pp : t Fmt.printer -end = struct - type t = Cell.t array - - let[@inline] get (s:t) i j = s.(i*9 + j) - - let[@inline] set (s:t) i j n = - let s' = Array.copy s in - s'.(i*9 + j) <- n; - s' - - (** A set of related cells *) - type set = (int*int*Cell.t) Iter.t - - open Iter.Infix - - let all_cells (g:t) = - 0 -- 8 >>= fun i -> - 0 -- 8 >|= fun j -> (i,j,get g i j) - - let rows (g:t) = - 0 -- 8 >|= fun i -> - ( 0 -- 8 >|= fun j -> (i,j,get g i j)) - - let cols g = - 0 -- 8 >|= fun j -> - ( 0 -- 8 >|= fun i -> (i,j,get g i j)) - - let squares g = - 0 -- 2 >>= fun sq_i -> - 0 -- 2 >|= fun sq_j -> - ( 0 -- 2 >>= fun off_i -> - 0 -- 2 >|= fun off_j -> - let i = 3*sq_i + off_i in - let j = 3*sq_j + off_j in - (i,j,get g i j)) - - let is_full g = Array.for_all Cell.is_full g - - let is_valid g = - let all_distinct (s:set) = - (s >|= fun (_,_,c) -> c) - |> Iter.diagonal - |> Iter.for_all (fun (c1,c2) -> Cell.neq c1 c2) - in - Iter.for_all all_distinct @@ rows g && - Iter.for_all all_distinct @@ cols g && - Iter.for_all all_distinct @@ squares g - - let matches ~pat:g1 g2 : bool = - all_cells g1 - |> Iter.filter (fun (_,_,c) -> Cell.is_full c) - |> Iter.for_all (fun (x,y,c) -> Cell.equal c @@ get g2 x y) - - let pp out g = - Fmt.fprintf out "@["; - Array.iteri - (fun i n -> - Cell.pp out n; - if i mod 9 = 8 then Fmt.fprintf out "@,") - g; - Fmt.fprintf out "@]" - - let parse (s:string) : t = - if String.length s < 81 then ( - errorf "line is too short, expected 81 chars, not %d" (String.length s); - ); - let a = Array.make 81 Cell.empty in - for i = 0 to 80 do - let c = String.get s i in - let n = if c = '.' then 0 else Char.code c - Char.code '0' in - if n < 0 || n > 9 then errorf "invalid char %c" c; - a.(i) <- Cell.make n - done; - a -end - -module B_ref = Msat_backtrack.Ref - -module Solver : sig - type t - val create : Grid.t -> t - val solve : t -> Grid.t option -end = struct - open Msat.Solver_intf - - (* formulas *) - module F = struct - type t = bool*int*int*Cell.t - let equal (sign1,x1,y1,c1)(sign2,x2,y2,c2) = - sign1=sign2 && x1=x2 && y1=y2 && Cell.equal c1 c2 - let hash (sign,x,y,c) = CCHash.(combine4 (bool sign)(int x)(int y)(Cell.hash c)) - let pp out (sign,x,y,c) = - Fmt.fprintf out "[@[(%d,%d) %s %a@]]" x y (if sign then "=" else "!=") Cell.pp c - let neg (sign,x,y,c) = (not sign,x,y,c) - let norm ((sign,_,_,_) as f) = - if sign then f, Same_sign else neg f, Negated - - let make sign x y (c:Cell.t) : t = (sign,x,y,c) - end - - module Theory = struct - type proof = unit - module Formula = F - type t = { - grid: Grid.t B_ref.t; - } - - let create g : t = {grid=B_ref.create g} - let[@inline] grid self : Grid.t = B_ref.get self.grid - let[@inline] set_grid self g : unit = B_ref.set self.grid g - - let push_level self = B_ref.push_level self.grid - let pop_levels self n = B_ref.pop_levels self.grid n - - let pp_c_ = Fmt.(list ~sep:(return "@ ∨ ")) F.pp - let[@inline] logs_conflict kind c : unit = - Log.debugf 4 (fun k->k "(@[conflict.%s@ %a@])" kind pp_c_ c) - - (* check that all cells are full *) - let check_full_ (self:t) acts : unit = - Grid.all_cells (grid self) - (fun (x,y,c) -> - if Cell.is_empty c then ( - let c = - CCList.init 9 - (fun c -> F.make true x y (Cell.make (c+1))) - in - Log.debugf 4 (fun k->k "(@[add-clause@ %a@])" pp_c_ c); - acts.acts_add_clause ~keep:true c (); - )) - - (* check constraints *) - let check_ (self:t) acts : unit = - Log.debugf 4 (fun k->k "(@[sudoku.check@ @[:g %a@]@])" Grid.pp (B_ref.get self.grid)); - let[@inline] all_diff kind f = - let pairs = - f (grid self) - |> Iter.flat_map - (fun set -> - set - |> Iter.filter (fun (_,_,c) -> Cell.is_full c) - |> Iter.diagonal) - in - pairs - (fun ((x1,y1,c1),(x2,y2,c2)) -> - if Cell.equal c1 c2 then ( - assert (x1<>x2 || y1<>y2); - let c = [F.make false x1 y1 c1; F.make false x2 y2 c2] in - logs_conflict ("all-diff." ^ kind) c; - acts.acts_raise_conflict c () - )) - in - all_diff "rows" Grid.rows; - all_diff "cols" Grid.cols; - all_diff "squares" Grid.squares; - () - - let trail_ (acts:_ Msat.acts) = - acts.acts_iter_assumptions - |> Iter.map - (function - | Assign _ -> assert false - | Lit f -> f) - - (* update current grid with the given slice *) - let add_slice (self:t) acts : unit = - trail_ acts - (function - | false,_,_,_ -> () - | true,x,y,c -> - assert (Cell.is_full c); - let grid = grid self in - let c' = Grid.get grid x y in - if Cell.is_empty c' then ( - set_grid self (Grid.set grid x y c); - ) else if Cell.neq c c' then ( - (* conflict: at most one value *) - let c = [F.make false x y c; F.make false x y c'] in - logs_conflict "at-most-one" c; - acts.acts_raise_conflict c () - ) - ) - - let partial_check (self:t) acts : unit = - Log.debugf 4 - (fun k->k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" - (Fmt.list F.pp) (trail_ acts |> Iter.to_list)); - add_slice self acts; - check_ self acts - - let final_check (self:t) acts : unit = - Log.debugf 4 (fun k->k "(@[sudoku.final-check@])"); - check_full_ self acts; - check_ self acts - - end - - module S = Msat.Make_cdcl_t(Theory) - - type t = { - grid0: Grid.t; - solver: S.t; - } - - let solve (self:t) : _ option = - let assumptions = - Grid.all_cells self.grid0 - |> Iter.filter (fun (_,_,c) -> Cell.is_full c) - |> Iter.map (fun (x,y,c) -> F.make true x y c) - |> Iter.map (S.make_atom self.solver) - |> Iter.to_rev_list - in - Log.debugf 2 - (fun k->k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list S.Atom.pp) assumptions); - let r = - match S.solve self.solver ~assumptions with - | S.Sat _ -> Some (Theory.grid (S.theory self.solver)) - | S.Unsat _ -> None - in - (* TODO: print some stats *) - r - - let create g : t = - { solver=S.create ~store_proof:false (Theory.create g); grid0=g } -end - -let solve_grid (g:Grid.t) : Grid.t option = - let s = Solver.create g in - Solver.solve s - -let solve_file file = - Format.printf "solve grids in file %S@." file; - let start = Sys.time() in - let grids = - CCIO.with_in file CCIO.read_lines_l - |> CCList.filter_map - (fun s -> - let s = String.trim s in - if s="" then None - else match Grid.parse s with - | g -> Some g - | exception e -> - errorf "cannot parse sudoku %S: %s@." s (Printexc.to_string e)) - in - Format.printf "parsed %d grids (in %.3fs)@." (List.length grids) (Sys.time()-.start); - List.iter - (fun g -> - Format.printf "@[@,#########################@,@[<2>solve grid:@ %a@]@]@." Grid.pp g; - let start=Sys.time() in - match solve_grid g with - | None -> Format.printf "no solution (in %.3fs)@." (Sys.time()-.start) - | Some g' when not @@ Grid.is_full g' -> - errorf "grid %a@ is not full" Grid.pp g' - | Some g' when not @@ Grid.is_valid g' -> - errorf "grid %a@ is not valid" Grid.pp g' - | Some g' when not @@ Grid.matches ~pat:g g' -> - errorf "grid %a@ @[<2>does not match original@ %a@]" Grid.pp g' Grid.pp g - | Some g' -> - Format.printf "@[@[<2>solution (in %.3fs):@ %a@]@,###################@]@." - (Sys.time()-.start) Grid.pp g') - grids; - Format.printf "@.solved %d grids (in %.3fs)@." (List.length grids) (Sys.time()-.start); - () - -let () = - Fmt.set_color_default true; - let files = ref [] in - let debug = ref 0 in - let opts = [ - "--debug", Arg.Set_int debug, " debug"; - "-d", Arg.Set_int debug, " debug"; - ] |> Arg.align in - Arg.parse opts (fun f -> files := f :: !files) "sudoku_solve [options] "; - Msat.Log.set_debug !debug; - try - List.iter (fun f -> solve_file f) !files; - with - | Failure msg | Invalid_argument msg -> - Format.printf "@{Error@}:@.%s@." msg; - exit 1 diff --git a/src/sat/src/tseitin/Msat_tseitin.ml b/src/sat/src/tseitin/Msat_tseitin.ml deleted file mode 100644 index 3f162c8d..00000000 --- a/src/sat/src/tseitin/Msat_tseitin.ml +++ /dev/null @@ -1,326 +0,0 @@ -(**************************************************************************) -(* *) -(* Alt-Ergo Zero *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - -module type Arg = Tseitin_intf.Arg - -module type S = Tseitin_intf.S - -module Make (F : Tseitin_intf.Arg) = struct - - exception Empty_Or - type combinator = And | Or | Imp | Not - - type atom = F.t - type t = - | True - | Lit of atom - | Comb of combinator * t list - - let rec pp fmt phi = - match phi with - | True -> Format.fprintf fmt "true" - | Lit a -> F.pp fmt a - | Comb (Not, [f]) -> - Format.fprintf fmt "not (%a)" pp f - | Comb (And, l) -> Format.fprintf fmt "(%a)" (pp_list "and") l - | Comb (Or, l) -> Format.fprintf fmt "(%a)" (pp_list "or") l - | Comb (Imp, [f1; f2]) -> - Format.fprintf fmt "(%a => %a)" pp f1 pp f2 - | _ -> assert false - and pp_list sep fmt = function - | [] -> () - | [f] -> pp fmt f - | f::l -> Format.fprintf fmt "%a %s %a" pp f sep (pp_list sep) l - - let make comb l = Comb (comb, l) - - let make_atom p = Lit p - - let f_true = True - let f_false = Comb(Not, [True]) - - let rec flatten comb acc = function - | [] -> acc - | (Comb (c, l)) :: r when c = comb -> - flatten comb (List.rev_append l acc) r - | a :: r -> - flatten comb (a :: acc) r - - let rec opt_rev_map f acc = function - | [] -> acc - | a :: r -> begin match f a with - | None -> opt_rev_map f acc r - | Some b -> opt_rev_map f (b :: acc) r - end - - let remove_true l = - let aux = function - | True -> None - | f -> Some f - in - opt_rev_map aux [] l - - let remove_false l = - let aux = function - | Comb(Not, [True]) -> None - | f -> Some f - in - opt_rev_map aux [] l - - - let make_not f = make Not [f] - - let make_and l = - let l' = remove_true (flatten And [] l) in - if List.exists ((=) f_false) l' then - f_false - else - make And l' - - let make_or l = - let l' = remove_false (flatten Or [] l) in - if List.exists ((=) f_true) l' then - f_true - else match l' with - | [] -> raise Empty_Or - | [a] -> a - | _ -> Comb (Or, l') - - let make_imply f1 f2 = make Imp [f1; f2] - let make_equiv f1 f2 = make_and [ make_imply f1 f2; make_imply f2 f1] - let make_xor f1 f2 = make_or [ make_and [ make_not f1; f2 ]; - make_and [ f1; make_not f2 ] ] - - (* simplify formula *) - let (%%) f g x = f (g x) - - let rec sform f k = match f with - | True | Comb (Not, [True]) -> k f - | Comb (Not, [Lit a]) -> k (Lit (F.neg a)) - | Comb (Not, [Comb (Not, [f])]) -> sform f k - | Comb (Not, [Comb (Or, l)]) -> sform_list_not [] l (k %% make_and) - | Comb (Not, [Comb (And, l)]) -> sform_list_not [] l (k %% make_or) - | Comb (And, l) -> sform_list [] l (k %% make_and) - | Comb (Or, l) -> sform_list [] l (k %% make_or) - | Comb (Imp, [f1; f2]) -> - sform (make_not f1) (fun f1' -> sform f2 (fun f2' -> k (make_or [f1'; f2']))) - | Comb (Not, [Comb (Imp, [f1; f2])]) -> - sform f1 (fun f1' -> sform (make_not f2) (fun f2' -> k (make_and [f1';f2']))) - | Comb ((Imp | Not), _) -> assert false - | Lit _ -> k f - and sform_list acc l k = match l with - | [] -> k acc - | f :: tail -> - sform f (fun f' -> sform_list (f'::acc) tail k) - and sform_list_not acc l k = match l with - | [] -> k acc - | f :: tail -> - sform (make_not f) (fun f' -> sform_list_not (f'::acc) tail k) - - let ( @@ ) l1 l2 = List.rev_append l1 l2 - (* let ( @ ) = `Use_rev_append_instead (* prevent use of non-tailrec append *) *) - - (* - let distrib l_and l_or = - let l = - if l_or = [] then l_and - else - List.rev_map - (fun x -> - match x with - | Lit _ -> Comb (Or, x::l_or) - | Comb (Or, l) -> Comb (Or, l @@ l_or) - | _ -> assert false - ) l_and - in - Comb (And, l) - - let rec flatten_or = function - | [] -> [] - | Comb (Or, l)::r -> l @@ (flatten_or r) - | Lit a :: r -> (Lit a)::(flatten_or r) - | _ -> assert false - - let rec flatten_and = function - | [] -> [] - | Comb (And, l)::r -> l @@ (flatten_and r) - | a :: r -> a::(flatten_and r) - - - let rec cnf f = - match f with - | Comb (Or, l) -> - begin - let l = List.rev_map cnf l in - let l_and, l_or = - List.partition (function Comb(And,_) -> true | _ -> false) l in - match l_and with - | [ Comb(And, l_conj) ] -> - let u = flatten_or l_or in - distrib l_conj u - - | Comb(And, l_conj) :: r -> - let u = flatten_or l_or in - cnf (Comb(Or, (distrib l_conj u)::r)) - - | _ -> - begin - match flatten_or l_or with - | [] -> assert false - | [r] -> r - | v -> Comb (Or, v) - end - end - | Comb (And, l) -> - Comb (And, List.rev_map cnf l) - | f -> f - - let rec mk_cnf = function - | Comb (And, l) -> - List.fold_left (fun acc f -> (mk_cnf f) @@ acc) [] l - - | Comb (Or, [f1;f2]) -> - let ll1 = mk_cnf f1 in - let ll2 = mk_cnf f2 in - List.fold_left - (fun acc l1 -> (List.rev_map (fun l2 -> l1 @@ l2)ll2) @@ acc) [] ll1 - - | Comb (Or, f1 :: l) -> - let ll1 = mk_cnf f1 in - let ll2 = mk_cnf (Comb (Or, l)) in - List.fold_left - (fun acc l1 -> (List.rev_map (fun l2 -> l1 @@ l2)ll2) @@ acc) [] ll1 - - | Lit a -> [[a]] - | Comb (Not, [Lit a]) -> [[F.neg a]] - | _ -> assert false - - - let rec unfold mono f = - match f with - | Lit a -> a::mono - | Comb (Not, [Lit a]) -> - (F.neg a)::mono - | Comb (Or, l) -> - List.fold_left unfold mono l - | _ -> assert false - - let rec init monos f = - match f with - | Comb (And, l) -> - List.fold_left init monos l - | f -> (unfold [] f)::monos - - let make_cnf f = - let sfnc = cnf (sform f) in - init [] sfnc - *) - - let mk_proxy = F.fresh - - let acc_or = ref [] - let acc_and = ref [] - - (* build a clause by flattening (if sub-formulas have the - same combinator) and proxy-ing sub-formulas that have the - opposite operator. *) - let rec cnf f = match f with - | Lit a -> None, [a] - | Comb (Not, [Lit a]) -> None, [F.neg a] - - | Comb (And, l) -> - List.fold_left - (fun (_, acc) f -> - match cnf f with - | _, [] -> assert false - | _cmb, [a] -> Some And, a :: acc - | Some And, l -> - Some And, l @@ acc - (* let proxy = mk_proxy () in *) - (* acc_and := (proxy, l) :: !acc_and; *) - (* proxy :: acc *) - | Some Or, l -> - let proxy = mk_proxy () in - acc_or := (proxy, l) :: !acc_or; - Some And, proxy :: acc - | None, l -> Some And, l @@ acc - | _ -> assert false - ) (None, []) l - - | Comb (Or, l) -> - List.fold_left - (fun (_, acc) f -> - match cnf f with - | _, [] -> assert false - | _cmb, [a] -> Some Or, a :: acc - | Some Or, l -> - Some Or, l @@ acc - (* let proxy = mk_proxy () in *) - (* acc_or := (proxy, l) :: !acc_or; *) - (* proxy :: acc *) - | Some And, l -> - let proxy = mk_proxy () in - acc_and := (proxy, l) :: !acc_and; - Some Or, proxy :: acc - | None, l -> Some Or, l @@ acc - | _ -> assert false - ) (None, []) l - | _ -> assert false - - let cnf f = - let acc = match f with - | True -> [] - | Comb(Not, [True]) -> [[]] - | Comb (And, l) -> List.rev_map (fun f -> snd(cnf f)) l - | _ -> [snd (cnf f)] - in - let proxies = ref [] in - (* encore clauses that make proxies in !acc_and equivalent to - their clause *) - let acc = - List.fold_left - (fun acc (p,l) -> - proxies := p :: !proxies; - let np = F.neg p in - (* build clause [cl = l1 & l2 & ... & ln => p] where [l = [l1;l2;..]] - also add clauses [p => l1], [p => l2], etc. *) - let cl, acc = - List.fold_left - (fun (cl,acc) a -> (F.neg a :: cl), [np; a] :: acc) - ([p],acc) l in - cl :: acc - ) acc !acc_and - in - (* encore clauses that make proxies in !acc_or equivalent to - their clause *) - let acc = - List.fold_left - (fun acc (p,l) -> - proxies := p :: !proxies; - (* add clause [p => l1 | l2 | ... | ln], and add clauses - [l1 => p], [l2 => p], etc. *) - let acc = List.fold_left (fun acc a -> [p; F.neg a]::acc) - acc l in - (F.neg p :: l) :: acc - ) acc !acc_or - in - acc - - let make_cnf f = - acc_or := []; - acc_and := []; - cnf (sform f (fun f' -> f')) - - (* Naive CNF XXX remove??? - let make_cnf f = mk_cnf (sform f) - *) -end diff --git a/src/sat/src/tseitin/Msat_tseitin.mli b/src/sat/src/tseitin/Msat_tseitin.mli deleted file mode 100644 index 498667c5..00000000 --- a/src/sat/src/tseitin/Msat_tseitin.mli +++ /dev/null @@ -1,22 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Tseitin CNF conversion - - This modules implements Tseitin's Conjunctive Normal Form conversion, i.e. - the ability to transform an arbitrary boolean formula into an equi-satisfiable - CNF, that can then be fed to a SAT/SMT/McSat solver. -*) - -module type Arg = Tseitin_intf.Arg -(** The implementation of formulas required to implement Tseitin's CNF conversion. *) - -module type S = Tseitin_intf.S -(** The exposed interface of Tseitin's CNF conversion. *) - -module Make : functor (F : Arg) -> S with type atom = F.t -(** This functor provides an implementation of Tseitin's CNF conversion. *) - diff --git a/src/sat/src/tseitin/Tseitin_intf.ml b/src/sat/src/tseitin/Tseitin_intf.ml deleted file mode 100644 index 99805c35..00000000 --- a/src/sat/src/tseitin/Tseitin_intf.ml +++ /dev/null @@ -1,85 +0,0 @@ -(**************************************************************************) -(* *) -(* Alt-Ergo Zero *) -(* *) -(* Sylvain Conchon and Alain Mebsout *) -(* Universite Paris-Sud 11 *) -(* *) -(* Copyright 2011. This file is distributed under the terms of the *) -(* Apache Software License version 2.0 *) -(* *) -(**************************************************************************) - -(** Interfaces for Tseitin's CNF conversion *) - -module type Arg = sig - (** Formulas - - This defines what is needed of formulas in order to implement - Tseitin's CNF conversion. - *) - - type t - (** Type of atomic formulas. *) - - val neg : t -> t - (** Negation of atomic formulas. *) - - val fresh : unit -> t - (** Generate fresh formulas (that are different from any other). *) - - val pp : Format.formatter -> t -> unit - (** Print the given formula. *) - -end - -module type S = sig - (** CNF conversion - - This modules converts arbitrary boolean formulas into CNF. - *) - - type atom - (** The type of atomic formulas. *) - - type t - (** The type of arbitrary boolean formulas. Arbitrary boolean formulas - can be built using functions in this module, and then converted - to a CNF, which is a list of clauses that only use atomic formulas. *) - - val f_true : t - (** The [true] formula, i.e a formula that is always satisfied. *) - - val f_false : t - (** The [false] formula, i.e a formula that cannot be satisfied. *) - - val make_atom : atom -> t - (** [make_atom p] builds the boolean formula equivalent to the atomic formula [p]. *) - - val make_not : t -> t - (** Creates the negation of a boolean formula. *) - - val make_and : t list -> t - (** Creates the conjunction of a list of formulas. An empty conjunction is always satisfied. *) - - val make_or : t list -> t - (** Creates the disjunction of a list of formulas. An empty disjunction is never satisfied. *) - - val make_xor : t -> t -> t - (** [make_xor p q] creates the boolean formula "[p] xor [q]". *) - - val make_imply : t -> t -> t - (** [make_imply p q] creates the boolean formula "[p] implies [q]". *) - - val make_equiv : t -> t -> t - (** [make_equiv p q] creates the boolena formula "[p] is equivalent to [q]". *) - - val make_cnf : t -> atom list list - (** [make_cnf f] returns a conjunctive normal form of [f] under the form: a - list (which is a conjunction) of lists (which are disjunctions) of - atomic formulas. *) - - val pp : Format.formatter -> t -> unit - (** [print fmt f] prints the formula on the formatter [fmt].*) - -end diff --git a/src/sat/src/tseitin/dune b/src/sat/src/tseitin/dune deleted file mode 100644 index 0a35225d..00000000 --- a/src/sat/src/tseitin/dune +++ /dev/null @@ -1,11 +0,0 @@ - -(library - (name msat_tseitin) - (public_name msat.tseitin) - (synopsis "Tseitin transformation for msat") - (libraries msat) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) - From 564dcec2528c56d91a9af2b9f6f5ac4a115269b7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 01:40:55 -0400 Subject: [PATCH 175/182] cleanup msat, rename it sidekick.sat --- src/backend/Coq.ml | 193 ------------------------ src/backend/Coq.mli | 46 ------ src/backend/Dedukti.ml | 62 -------- src/backend/Dedukti.mli | 32 ---- src/backend/Dot.ml | 6 +- src/backend/Dot.mli | 6 +- src/backend/dune | 12 +- src/backtrack/Backtrackable_ref.ml | 29 ---- src/backtrack/Backtrackable_ref.mli | 30 ---- src/backtrack/Msat_backtrack.ml | 2 - src/backtrack/dune | 11 -- src/base/Base_types.ml | 4 +- src/main/main.ml | 1 - src/msat-solver/Sidekick_msat_solver.ml | 52 +++---- src/msat-solver/dune | 2 +- src/sat/Heap_intf.ml | 9 +- src/sat/{Msat.ml => Sidekick_sat.ml} | 0 src/sat/dune | 10 +- src/util/Backtrack_stack.ml | 2 - src/{sat => util}/Log.ml | 5 - src/{sat => util}/Log.mli | 14 +- src/util/Sidekick_util.ml | 5 +- src/{sat => util}/Vec.ml | 0 src/{sat => util}/Vec.mli | 7 + 24 files changed, 66 insertions(+), 474 deletions(-) delete mode 100644 src/backend/Coq.ml delete mode 100644 src/backend/Coq.mli delete mode 100644 src/backend/Dedukti.ml delete mode 100644 src/backend/Dedukti.mli delete mode 100644 src/backtrack/Backtrackable_ref.ml delete mode 100644 src/backtrack/Backtrackable_ref.mli delete mode 100644 src/backtrack/Msat_backtrack.ml delete mode 100644 src/backtrack/dune rename src/sat/{Msat.ml => Sidekick_sat.ml} (100%) rename src/{sat => util}/Log.ml (83%) rename src/{sat => util}/Log.mli (60%) rename src/{sat => util}/Vec.ml (100%) rename src/{sat => util}/Vec.mli (91%) diff --git a/src/backend/Coq.ml b/src/backend/Coq.ml deleted file mode 100644 index ffb736fd..00000000 --- a/src/backend/Coq.ml +++ /dev/null @@ -1,193 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2015 Guillaume Bury -*) - -module type S = Backend_intf.S - -module type Arg = sig - - type hyp - type lemma - type assumption - - val prove_hyp : Format.formatter -> string -> hyp -> unit - val prove_lemma : Format.formatter -> string -> lemma -> unit - val prove_assumption : Format.formatter -> string -> assumption -> unit - -end - -module Make(S : Msat.S)(A : Arg with type hyp := S.clause - and type lemma := S.clause - and type assumption := S.clause) = struct - - module Atom = S.Atom - module Clause = S.Clause - module M = Map.Make(S.Atom) - module C_tbl = S.Clause.Tbl - module P = S.Proof - - let name = Clause.name - - let clause_map c = - let rec aux acc a i = - if i >= Array.length a then acc - else begin - let name = Format.sprintf "A%d" i in - aux (M.add a.(i) name acc) a (i + 1) - end - in - aux M.empty (Clause.atoms c) 0 - - let clause_iter m format fmt clause = - let aux atom = Format.fprintf fmt format (M.find atom m) in - Array.iter aux (Clause.atoms clause) - - let elim_duplicate fmt goal hyp _ = - (** Printing info comment in coq *) - Format.fprintf fmt - "(* Eliminating doublons. Goal : %s ; Hyp : %s *)@\n" - (name goal) (name hyp); - (** Prove the goal: intro the atoms, then use them with the hyp *) - let m = clause_map goal in - Format.fprintf fmt "pose proof @[(fun %a=>@ %s%a) as %s@].@\n" - (clause_iter m "%s@ ") goal (name hyp) - (clause_iter m "@ %s") hyp (name goal) - - let resolution_aux m a h1 h2 fmt () = - Format.fprintf fmt "%s%a" (name h1) - (fun fmt -> Array.iter (fun b -> - if b == a then begin - Format.fprintf fmt "@ (fun p =>@ %s%a)" - (name h2) (fun fmt -> (Array.iter (fun c -> - if Atom.equal c (Atom.neg a) then - Format.fprintf fmt "@ (fun np => np p)" - else - Format.fprintf fmt "@ %s" (M.find c m))) - ) (Clause.atoms h2) - end else - Format.fprintf fmt "@ %s" (M.find b m) - )) (Clause.atoms h1) - - let resolution fmt goal hyp1 hyp2 atom = - let a = Atom.abs atom in - let h1, h2 = - if Array.exists (Atom.equal a) (Clause.atoms hyp1) then hyp1, hyp2 - else ( - assert (Array.exists (Atom.equal a) (Clause.atoms hyp2)); - hyp2, hyp1 - ) - in - (** Print some debug info *) - Format.fprintf fmt - "(* Clausal resolution. Goal : %s ; Hyps : %s, %s *)@\n" - (name goal) (name h1) (name h2); - (** Prove the goal: intro the axioms, then perform resolution *) - if Array.length (Clause.atoms goal) = 0 then ( - let m = M.empty in - Format.fprintf fmt "exact @[(%a)@].@\n" (resolution_aux m a h1 h2) (); - false - ) else ( - let m = clause_map goal in - Format.fprintf fmt "pose proof @[(fun %a=>@ %a)@ as %s.@]@\n" - (clause_iter m "%s@ ") goal (resolution_aux m a h1 h2) () (name goal); - true - ) - - (* Count uses of hypotheses *) - let incr_use h c = - let i = try C_tbl.find h c with Not_found -> 0 in - C_tbl.add h c (i + 1) - - let decr_use h c = - let i = C_tbl.find h c - 1 in - assert (i >= 0); - let () = C_tbl.add h c i in - i <= 0 - - let clear fmt c = - Format.fprintf fmt "clear %s." (name c) - - let rec clean_aux fmt = function - | [] -> () - | [x] -> - Format.fprintf fmt "%a@\n" clear x - | x :: ((_ :: _) as r) -> - Format.fprintf fmt "%a@ %a" clear x clean_aux r - - let clean h fmt l = - match List.filter (decr_use h) l with - | [] -> () - | l' -> - Format.fprintf fmt "(* Clearing unused clauses *)@\n%a" clean_aux l' - - let prove_node t fmt node = - let clause = node.P.conclusion in - match node.P.step with - | P.Hypothesis _ -> - A.prove_hyp fmt (name clause) clause - | P.Assumption -> - A.prove_assumption fmt (name clause) clause - | P.Lemma _ -> - A.prove_lemma fmt (name clause) clause - | P.Duplicate (p, l) -> - let c = P.conclusion p in - let () = elim_duplicate fmt clause c l in - clean t fmt [c] - | P.Hyper_res hr -> - let (p1, p2, a) = P.res_of_hyper_res hr in - let c1 = P.conclusion p1 in - let c2 = P.conclusion p2 in - if resolution fmt clause c1 c2 a then clean t fmt [c1; c2] - - let count_uses p = - let h = C_tbl.create 128 in - let aux () node = - List.iter (fun p' -> incr_use h P.(conclusion p')) (P.parents node.P.step) - in - let () = P.fold aux () p in - h - - (* Here the main idea is to always try and have exactly - one goal to prove, i.e False. So each *) - let pp fmt p = - let h = count_uses p in - let aux () node = - Format.fprintf fmt "%a" (prove_node h) node - in - Format.fprintf fmt "(* Coq proof generated by mSAT*)@\n"; - P.fold aux () p -end - - -module Simple(S : Msat.S) - (A : Arg with type hyp = S.formula list - and type lemma := S.lemma - and type assumption := S.formula) = - Make(S)(struct - module P = S.Proof - - (* Some helpers *) - let lit = S.Atom.formula - - let get_assumption c = - match S.Clause.atoms_l c with - | [ x ] -> x - | _ -> assert false - - let get_lemma c = - match P.expand (P.prove c) with - | {P.step=P.Lemma p; _} -> p - | _ -> assert false - - let prove_hyp fmt name c = - A.prove_hyp fmt name (List.map lit (S.Clause.atoms_l c)) - - let prove_lemma fmt name c = - A.prove_lemma fmt name (get_lemma c) - - let prove_assumption fmt name c = - A.prove_assumption fmt name (lit (get_assumption c)) - - end) - diff --git a/src/backend/Coq.mli b/src/backend/Coq.mli deleted file mode 100644 index 3d34f549..00000000 --- a/src/backend/Coq.mli +++ /dev/null @@ -1,46 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2015 Guillaume Bury -*) - -(** Coq Backend - - This module provides an easy way to produce coq scripts - corresponding to the resolution proofs output by the - sat solver. *) - -module type S = Backend_intf.S -(** Interface for exporting proofs. *) - -module type Arg = sig - (** Term printing for Coq *) - - type hyp - type lemma - type assumption - (** The types of hypotheses, lemmas, and assumptions *) - - val prove_hyp : Format.formatter -> string -> hyp -> unit - val prove_lemma : Format.formatter -> string -> lemma -> unit - val prove_assumption : Format.formatter -> string -> assumption -> unit - (** Proving function for hypotheses, lemmas and assumptions. - [prove_x fmt name x] should prove [x], and be such that after - executing it, [x] is among the coq hypotheses under the name [name]. - The hypothesis should be the encoding of the given clause, i.e - for a clause [a \/ not b \/ c], the proved hypothesis should be: - [ ~ a -> ~ ~ b -> ~ c -> False ], keeping the same order as the - one in the atoms array of the clause. *) - -end - -module Make(S : Msat.S)(A : Arg with type hyp := S.clause - and type lemma := S.clause - and type assumption := S.clause) : S with type t := S.proof -(** Base functor to output Coq proofs *) - - -module Simple(S : Msat.S)(A : Arg with type hyp = S.formula list - and type lemma := S.lemma - and type assumption := S.formula) : S with type t := S.proof -(** Simple functor to output Coq proofs *) - diff --git a/src/backend/Dedukti.ml b/src/backend/Dedukti.ml deleted file mode 100644 index 7f8526eb..00000000 --- a/src/backend/Dedukti.ml +++ /dev/null @@ -1,62 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2015 Guillaume Bury -*) - -module type S = Backend_intf.S - -module type Arg = sig - - type proof - type lemma - type formula - - val pp : Format.formatter -> formula -> unit - val prove : Format.formatter -> lemma -> unit - val context : Format.formatter -> proof -> unit -end - -module Make(S : Msat.S)(A : Arg with type formula := S.formula - and type lemma := S.lemma - and type proof := S.proof) = struct - module P = S.Proof - - let pp_nl fmt = Format.fprintf fmt "@\n" - let fprintf fmt format = Format.kfprintf pp_nl fmt format - - let _clause_name = S.Clause.name - - let _pp_clause fmt c = - let rec aux fmt = function - | [] -> () - | a :: r -> - let f, pos = - if S.Atom.sign a then - S.Atom.formula a, true - else - S.Atom.formula (S.Atom.neg a), false - in - fprintf fmt "%s _b %a ->@ %a" - (if pos then "_pos" else "_neg") A.pp f aux r - in - fprintf fmt "_b : Prop ->@ %a ->@ _proof _b" aux (S.Clause.atoms_l c) - - let context fmt p = - fprintf fmt "(; Embedding ;)"; - fprintf fmt "Prop : Type."; - fprintf fmt "_proof : Prop -> Type."; - fprintf fmt "(; Notations for clauses ;)"; - fprintf fmt "_pos : Prop -> Prop -> Type."; - fprintf fmt "_neg : Prop -> Prop -> Type."; - fprintf fmt "[b: Prop, p: Prop] _pos b p --> _proof p -> _proof b."; - fprintf fmt "[b: Prop, p: Prop] _neg b p --> _pos b p -> _proof b."; - A.context fmt p - - let pp fmt p = - fprintf fmt "#NAME Proof."; - fprintf fmt "(; Dedukti file automatically generated by mSAT ;)"; - context fmt p; - () - -end - diff --git a/src/backend/Dedukti.mli b/src/backend/Dedukti.mli deleted file mode 100644 index ef7cc88e..00000000 --- a/src/backend/Dedukti.mli +++ /dev/null @@ -1,32 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** Deduki backend for proofs - - Work in progress... -*) - -module type S = Backend_intf.S - -module type Arg = sig - - type lemma - type proof - type formula - - val pp : Format.formatter -> formula -> unit - val prove : Format.formatter -> lemma -> unit - val context : Format.formatter -> proof -> unit -end - -module Make : - functor(S : Msat.S) -> - functor(A : Arg - with type formula := S.formula - and type lemma := S.lemma - and type proof := S.proof) -> - S with type t := S.proof -(** Functor to generate a backend to output proofs for the dedukti type checker. *) diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 9097cc45..632f39a7 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -28,7 +28,7 @@ module type Arg = sig end -module Default(S : Msat.S) = struct +module Default(S : Sidekick_sat.S) = struct module Atom = S.Atom module Clause = S.Clause @@ -49,7 +49,7 @@ module Default(S : Msat.S) = struct end (** Functor to provide dot printing *) -module Make(S : Msat.S)(A : Arg with type atom := S.atom +module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) = struct @@ -151,7 +151,7 @@ module Make(S : Msat.S)(A : Arg with type atom := S.atom end -module Simple(S : Msat.S) +module Simple(S : Sidekick_sat.S) (A : Arg with type atom := S.formula and type hyp = S.formula list and type lemma := S.lemma diff --git a/src/backend/Dot.mli b/src/backend/Dot.mli index 70adffa1..13ccc703 100644 --- a/src/backend/Dot.mli +++ b/src/backend/Dot.mli @@ -47,20 +47,20 @@ module type Arg = sig end -module Default(S : Msat.S) : Arg with type atom := S.atom +module Default(S : Sidekick_sat.S) : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause (** Provides a reasonnable default to instantiate the [Make] functor, assuming the original printing functions are compatible with DOT html labels. *) -module Make(S : Msat.S)(A : Arg with type atom := S.atom +module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause and type assumption := S.clause) : S with type t := S.proof (** Functor for making a module to export proofs to the DOT format. *) -module Simple(S : Msat.S)(A : Arg with type atom := S.formula +module Simple(S : Sidekick_sat.S)(A : Arg with type atom := S.formula and type hyp = S.formula list and type lemma := S.lemma and type assumption = S.formula) : S with type t := S.proof diff --git a/src/backend/dune b/src/backend/dune index 7febb3bc..316c6d7a 100644 --- a/src/backend/dune +++ b/src/backend/dune @@ -1,10 +1,6 @@ (library - (name msat_backend) - (public_name msat.backend) - (synopsis "proof backends for msat") - (libraries msat) - (flags :standard -w +a-4-42-44-48-50-58-32-60@8 -warn-error -27 -color always -safe-string) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) + (name sidekick_backend) + (public_name sidekick.backend) + (synopsis "Proof backends for sidekick") + (libraries sidekick.sat)) diff --git a/src/backtrack/Backtrackable_ref.ml b/src/backtrack/Backtrackable_ref.ml deleted file mode 100644 index bc91cfd5..00000000 --- a/src/backtrack/Backtrackable_ref.ml +++ /dev/null @@ -1,29 +0,0 @@ - -type 'a t = { - mutable cur: 'a; - stack: 'a Vec.t; - copy: ('a -> 'a) option; -} - -let create ?copy x: _ t = - {cur=x; stack=Vec.create(); copy} - -let[@inline] get self = self.cur -let[@inline] set self x = self.cur <- x -let[@inline] update self f = self.cur <- f self.cur - -let[@inline] n_levels self = Vec.size self.stack - -let[@inline] push_level self : unit = - let x = self.cur in - let x = match self.copy with None -> x | Some f -> f x in - Vec.push self.stack x - -let pop_levels self n : unit = - assert (n>=0); - if n > Vec.size self.stack then invalid_arg "Backtrackable_ref.pop_levels"; - let i = Vec.size self.stack-n in - let x = Vec.get self.stack i in - self.cur <- x; - Vec.shrink self.stack i; - () diff --git a/src/backtrack/Backtrackable_ref.mli b/src/backtrack/Backtrackable_ref.mli deleted file mode 100644 index a1755115..00000000 --- a/src/backtrack/Backtrackable_ref.mli +++ /dev/null @@ -1,30 +0,0 @@ - -(** {1 Backtrackable ref} *) - -type 'a t - -val create : ?copy:('a -> 'a) -> 'a -> 'a t -(** Create a backtrackable reference holding the given value initially. - @param copy if provided, will be used to copy the value when [push_level] - is called. *) - -val set : 'a t -> 'a -> unit -(** Set the reference's current content *) - -val get : 'a t -> 'a -(** Get the reference's current content *) - -val update : 'a t -> ('a -> 'a) -> unit -(** Update the reference's current content *) - -val push_level : _ t -> unit -(** Push a backtracking level, copying the current value on top of some - stack. The [copy] function will be used if it was provided in {!create}. *) - -val n_levels : _ t -> int -(** Number of saved values *) - -val pop_levels : _ t -> int -> unit -(** Pop [n] levels, restoring to the value the reference was storing [n] calls - to [push_level] earlier. - @raise Invalid_argument if [n] is bigger than [n_levels]. *) diff --git a/src/backtrack/Msat_backtrack.ml b/src/backtrack/Msat_backtrack.ml deleted file mode 100644 index 14857855..00000000 --- a/src/backtrack/Msat_backtrack.ml +++ /dev/null @@ -1,2 +0,0 @@ - -module Ref = Backtrackable_ref diff --git a/src/backtrack/dune b/src/backtrack/dune deleted file mode 100644 index 48740ab9..00000000 --- a/src/backtrack/dune +++ /dev/null @@ -1,11 +0,0 @@ - -(library - (name msat_backtrack) - (public_name msat.backtrack) - (libraries msat) - (synopsis "backtrackable data structures for msat") - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 - -color always -safe-string -open Msat) - (ocamlopt_flags :standard -O3 -bin-annot - -unbox-closures -unbox-closures-factor 20) - ) diff --git a/src/base/Base_types.ml b/src/base/Base_types.ml index 97fc2562..c788cf97 100644 --- a/src/base/Base_types.ml +++ b/src/base/Base_types.ml @@ -1,7 +1,7 @@ (** Basic type definitions for Sidekick_base *) -module Vec = Msat.Vec -module Log = Msat.Log +module Vec = Sidekick_util.Vec +module Log = Sidekick_util.Log module Fmt = CCFormat module CC_view = Sidekick_core.CC_view diff --git a/src/main/main.ml b/src/main/main.ml index 8b61bb49..6651aca3 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -11,7 +11,6 @@ module Fmt = CCFormat module Term = Sidekick_base.Term module Solver = Sidekick_smtlib.Solver module Process = Sidekick_smtlib.Process -module Vec = Msat.Vec type 'a or_error = ('a, string) E.t diff --git a/src/msat-solver/Sidekick_msat_solver.ml b/src/msat-solver/Sidekick_msat_solver.ml index 139d2694..e9881926 100644 --- a/src/msat-solver/Sidekick_msat_solver.ml +++ b/src/msat-solver/Sidekick_msat_solver.ml @@ -1,12 +1,10 @@ -(** {1 Implementation of a Solver using Msat} *) +(** Core of the SMT solver using Sidekick_sat -(** {{: https://github.com/Gbury/mSAT/} Msat} is a modular SAT solver in + Sidekick_sat (in src/sat/) is a modular SAT solver in pure OCaml. - This builds a {!Sidekick_core.SOLVER} on top of it. *) - -module Log = Msat.Log -(** A logging module *) + This builds a {!Sidekick_core.SOLVER} on top of it. +*) (** Argument to pass to the functor {!Make} in order to create a new Msat-based SMT solver. *) @@ -76,7 +74,7 @@ module Make(A : ARG) type lit = Lit_.t (* actions from msat *) - type msat_acts = (Msat.void, lit, Msat.void, P.t) Msat.acts + type msat_acts = (Sidekick_sat.void, lit, Sidekick_sat.void, P.t) Sidekick_sat.acts (* the full argument to the congruence closure *) module CC_actions = struct @@ -91,10 +89,10 @@ module Make(A : ARG) module Lit = Lit type t = msat_acts let[@inline] raise_conflict a lits pr = - a.Msat.acts_raise_conflict lits pr + a.Sidekick_sat.acts_raise_conflict lits pr let[@inline] propagate a lit ~reason = - let reason = Msat.Consequence reason in - a.Msat.acts_propagate lit reason + let reason = Sidekick_sat.Consequence reason in + a.Sidekick_sat.acts_propagate lit reason end end @@ -218,7 +216,7 @@ module Make(A : ARG) include Lit let norm lit = let lit', sign = norm_sign lit in - lit', if sign then Msat.Same_sign else Msat.Negated + lit', if sign then Sidekick_sat.Same_sign else Sidekick_sat.Negated end module Eq_class = CC.N module Expl = CC.Expl @@ -244,22 +242,22 @@ module Make(A : ARG) let push_decision (_self:t) (acts:actions) (lit:lit) : unit = let sign = Lit.sign lit in - acts.Msat.acts_add_decision_lit (Lit.abs lit) sign + acts.Sidekick_sat.acts_add_decision_lit (Lit.abs lit) sign let[@inline] raise_conflict self acts c proof : 'a = Stat.incr self.count_conflict; - acts.Msat.acts_raise_conflict c proof + acts.Sidekick_sat.acts_raise_conflict c proof let[@inline] propagate self acts p ~reason : unit = Stat.incr self.count_propagate; - acts.Msat.acts_propagate p (Msat.Consequence reason) + acts.Sidekick_sat.acts_propagate p (Sidekick_sat.Consequence reason) let[@inline] propagate_l self acts p cs proof : unit = propagate self acts p ~reason:(fun()->cs,proof) let add_sat_clause_ self acts ~keep lits (proof:P.t) : unit = Stat.incr self.count_axiom; - acts.Msat.acts_add_clause ~keep lits proof + acts.Sidekick_sat.acts_add_clause ~keep lits proof let preprocess_term_ (self:t) ~add_clause (t:term) : term * proof = let mk_lit t = Lit.atom self.tst t in (* no further simplification *) @@ -377,7 +375,7 @@ module Make(A : ARG) let[@inline] add_clause_permanent self acts lits (proof:P.t) : unit = add_sat_clause_ self acts ~keep:true lits proof - let add_lit _self acts lit : unit = acts.Msat.acts_mk_lit lit + let add_lit _self acts lit : unit = acts.Sidekick_sat.acts_mk_lit lit let add_lit_t self acts ?sign t = let lit = mk_lit self acts ?sign t in @@ -429,7 +427,7 @@ module Make(A : ARG) (* handle a literal assumed by the SAT solver *) let assert_lits_ ~final (self:t) (acts:actions) (lits:Lit.t Iter.t) : unit = - Msat.Log.debugf 2 + Sidekick_sat.Log.debugf 2 (fun k->k "(@[@{msat-solver.assume_lits@}%s[lvl=%d]@ %a@])" (if final then "[final]" else "") self.level (Util.pp_iter ~sep:"; " Lit.pp) lits); (* transmit to CC *) @@ -458,26 +456,26 @@ module Make(A : ARG) let[@inline] iter_atoms_ acts : _ Iter.t = fun f -> - acts.Msat.acts_iter_assumptions + acts.Sidekick_sat.acts_iter_assumptions (function - | Msat.Lit a -> f a - | Msat.Assign _ -> assert false) + | Sidekick_sat.Lit a -> f a + | Sidekick_sat.Assign _ -> assert false) (* propagation from the bool solver *) let check_ ~final (self:t) (acts: msat_acts) = let pb = if final then Profile.begin_ "solver.final-check" else Profile.null_probe in let iter = iter_atoms_ acts in - Msat.Log.debugf 5 (fun k->k "(msat-solver.assume :len %d)" (Iter.length iter)); + Sidekick_sat.Log.debugf 5 (fun k->k "(msat-solver.assume :len %d)" (Iter.length iter)); self.on_progress(); assert_lits_ ~final self acts iter; Profile.exit pb (* propagation from the bool solver *) - let[@inline] partial_check (self:t) (acts:_ Msat.acts) : unit = + let[@inline] partial_check (self:t) (acts:_ Sidekick_sat.acts) : unit = check_ ~final:false self acts (* perform final check of the model *) - let[@inline] final_check (self:t) (acts:_ Msat.acts) : unit = + let[@inline] final_check (self:t) (acts:_ Sidekick_sat.acts) : unit = check_ ~final:true self acts let create ~stat (tst:Term.store) (ty_st:Ty.store) () : t = @@ -510,7 +508,7 @@ module Make(A : ARG) module Lit = Solver_internal.Lit (** the parametrized SAT Solver *) - module Sat_solver = Msat.Make_cdcl_t(Solver_internal) + module Sat_solver = Sidekick_sat.Make_cdcl_t(Solver_internal) module Atom = Sat_solver.Atom @@ -528,7 +526,7 @@ module Make(A : ARG) let pp_dot = let module Dot = - Msat_backend.Dot.Make(Sat_solver)(Msat_backend.Dot.Default(Sat_solver)) in + Sidekick_backend.Dot.Make(Sat_solver)(Sidekick_backend.Dot.Default(Sat_solver)) in let pp out self = Dot.pp out self.msat in Some pp @@ -925,9 +923,9 @@ module Make(A : ARG) let pr = us.get_proof () in if check then Sat_solver.Proof.check pr; Some (Pre_proof.make pr (List.rev self.si.t_defs)) - with Msat.Solver_intf.No_proof -> None + with Sidekick_sat.Solver_intf.No_proof -> None ) in - let unsat_core = lazy (us.Msat.unsat_assumptions ()) in + let unsat_core = lazy (us.Sidekick_sat.unsat_assumptions ()) in do_on_exit (); Unsat {proof; unsat_core} diff --git a/src/msat-solver/dune b/src/msat-solver/dune index 8e6100cd..78d55808 100644 --- a/src/msat-solver/dune +++ b/src/msat-solver/dune @@ -2,5 +2,5 @@ (name Sidekick_msat_solver) (public_name sidekick.msat-solver) (libraries containers iter sidekick.core sidekick.util - sidekick.cc sidekick.sat) + sidekick.cc sidekick.sat sidekick.backend) (flags :standard -open Sidekick_util)) diff --git a/src/sat/Heap_intf.ml b/src/sat/Heap_intf.ml index bee623e6..e7d4aee7 100644 --- a/src/sat/Heap_intf.ml +++ b/src/sat/Heap_intf.ml @@ -1,8 +1,13 @@ module type RANKED = sig type t - val idx: t -> int (** Index in heap. return -1 if never set *) - val set_idx : t -> int -> unit (** Update index in heap *) + + val idx: t -> int + (** Index in heap. return -1 if never set *) + + val set_idx : t -> int -> unit + (** Update index in heap *) + val cmp : t -> t -> bool end diff --git a/src/sat/Msat.ml b/src/sat/Sidekick_sat.ml similarity index 100% rename from src/sat/Msat.ml rename to src/sat/Sidekick_sat.ml diff --git a/src/sat/dune b/src/sat/dune index 85a23d9a..25a4c2cf 100644 --- a/src/sat/dune +++ b/src/sat/dune @@ -1,10 +1,10 @@ (library - (name msat) - (public_name msat) - (libraries iter) - (synopsis "core data structures and algorithms for msat") - (flags :standard -warn-error -3 -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string) + (name sidekick_sat) + (public_name sidekick.sat) + (libraries iter sidekick.util) + (synopsis "Pure OCaml SAT solver implementation for sidekick") + (flags :standard -open Sidekick_util) (ocamlopt_flags :standard -O3 -bin-annot -unbox-closures -unbox-closures-factor 20) ) diff --git a/src/util/Backtrack_stack.ml b/src/util/Backtrack_stack.ml index 890be02f..81b78f37 100644 --- a/src/util/Backtrack_stack.ml +++ b/src/util/Backtrack_stack.ml @@ -1,6 +1,4 @@ -module Vec = Msat.Vec - type 'a t = { vec: 'a Vec.t; lvls: int Vec.t; diff --git a/src/sat/Log.ml b/src/util/Log.ml similarity index 83% rename from src/sat/Log.ml rename to src/util/Log.ml index f60603ea..b93d4a7f 100644 --- a/src/sat/Log.ml +++ b/src/util/Log.ml @@ -1,8 +1,3 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) (** {1 Logging functions, real version} *) diff --git a/src/sat/Log.mli b/src/util/Log.mli similarity index 60% rename from src/sat/Log.mli rename to src/util/Log.mli index 8923f8e3..f6b7f435 100644 --- a/src/sat/Log.mli +++ b/src/util/Log.mli @@ -1,15 +1,13 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) -(** {1 Logging function, for debugging} *) +(** Logging function, for debugging *) val enabled : bool -val set_debug : int -> unit (** Set debug level *) -val get_debug : unit -> int (** Current debug level *) +val set_debug : int -> unit +(** Set debug level *) + +val get_debug : unit -> int +(** Current debug level *) val debugf : int -> diff --git a/src/util/Sidekick_util.ml b/src/util/Sidekick_util.ml index 01ae1f46..09b56058 100644 --- a/src/util/Sidekick_util.ml +++ b/src/util/Sidekick_util.ml @@ -1,9 +1,10 @@ (* re-exports *) module Fmt = CCFormat -module Vec = Msat.Vec -module Log = Msat.Log + module Util = Util +module Vec = Vec +module Log = Log module Backtrack_stack = Backtrack_stack module Backtrackable_tbl = Backtrackable_tbl module Error = Error diff --git a/src/sat/Vec.ml b/src/util/Vec.ml similarity index 100% rename from src/sat/Vec.ml rename to src/util/Vec.ml diff --git a/src/sat/Vec.mli b/src/util/Vec.mli similarity index 91% rename from src/sat/Vec.mli rename to src/util/Vec.mli index d51a2b69..b4994de4 100644 --- a/src/sat/Vec.mli +++ b/src/util/Vec.mli @@ -1,4 +1,11 @@ +(** Vectors + + A resizable array, workhorse of imperative programming :-). + This implementation originated in alt-ergo-zero but has been basically rewritten + from scratch several times since. +*) + type 'a t (** Abstract type of vectors of 'a *) From 4cb8887639c6e7f0edfd3ca96e44e925ee8d122e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 02:01:19 -0400 Subject: [PATCH 176/182] wip: remove all traces of mcsat from src/sat --- src/backend/Dot.mli | 4 +- src/msat-solver/Sidekick_msat_solver.ml | 22 +- src/sat/Internal.ml | 2275 ----------------------- src/sat/Sidekick_sat.ml | 31 +- src/sat/Solver.ml | 1932 ++++++++++++++++++- src/sat/Solver.mli | 34 - src/sat/Solver_intf.ml | 114 +- src/sat/msat.mld | 92 - 8 files changed, 1953 insertions(+), 2551 deletions(-) delete mode 100644 src/sat/Internal.ml delete mode 100644 src/sat/Solver.mli delete mode 100644 src/sat/msat.mld diff --git a/src/backend/Dot.mli b/src/backend/Dot.mli index 13ccc703..eecdebaf 100644 --- a/src/backend/Dot.mli +++ b/src/backend/Dot.mli @@ -57,13 +57,13 @@ module Default(S : Sidekick_sat.S) : Arg with type atom := S.atom module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom and type hyp := S.clause and type lemma := S.clause - and type assumption := S.clause) : S with type t := S.proof + and type assumption := S.clause) : S with type t := S.Proof.t (** Functor for making a module to export proofs to the DOT format. *) module Simple(S : Sidekick_sat.S)(A : Arg with type atom := S.formula and type hyp = S.formula list and type lemma := S.lemma - and type assumption = S.formula) : S with type t := S.proof + and type assumption = S.formula) : S with type t := S.Proof.t (** Functor for making a module to export proofs to the DOT format. The substitution of the hyp type is non-destructive due to a restriction of destructive substitutions on earlier versions of ocaml. *) diff --git a/src/msat-solver/Sidekick_msat_solver.ml b/src/msat-solver/Sidekick_msat_solver.ml index e9881926..aa35c35a 100644 --- a/src/msat-solver/Sidekick_msat_solver.ml +++ b/src/msat-solver/Sidekick_msat_solver.ml @@ -74,7 +74,7 @@ module Make(A : ARG) type lit = Lit_.t (* actions from msat *) - type msat_acts = (Sidekick_sat.void, lit, Sidekick_sat.void, P.t) Sidekick_sat.acts + type msat_acts = (lit, P.t) Sidekick_sat.acts (* the full argument to the congruence closure *) module CC_actions = struct @@ -427,7 +427,7 @@ module Make(A : ARG) (* handle a literal assumed by the SAT solver *) let assert_lits_ ~final (self:t) (acts:actions) (lits:Lit.t Iter.t) : unit = - Sidekick_sat.Log.debugf 2 + Log.debugf 2 (fun k->k "(@[@{msat-solver.assume_lits@}%s[lvl=%d]@ %a@])" (if final then "[final]" else "") self.level (Util.pp_iter ~sep:"; " Lit.pp) lits); (* transmit to CC *) @@ -455,17 +455,13 @@ module Make(A : ARG) () let[@inline] iter_atoms_ acts : _ Iter.t = - fun f -> - acts.Sidekick_sat.acts_iter_assumptions - (function - | Sidekick_sat.Lit a -> f a - | Sidekick_sat.Assign _ -> assert false) + fun f -> acts.Sidekick_sat.acts_iter_assumptions f (* propagation from the bool solver *) let check_ ~final (self:t) (acts: msat_acts) = let pb = if final then Profile.begin_ "solver.final-check" else Profile.null_probe in let iter = iter_atoms_ acts in - Sidekick_sat.Log.debugf 5 (fun k->k "(msat-solver.assume :len %d)" (Iter.length iter)); + Log.debugf 5 (fun k->k "(msat-solver.assume :len %d)" (Iter.length iter)); self.on_progress(); assert_lits_ ~final self acts iter; Profile.exit pb @@ -517,7 +513,7 @@ module Make(A : ARG) module SC = Sat_solver.Clause type t = { - msat: Sat_solver.proof; + msat: Sat_solver.Proof.t; tdefs: (term*term) list; (* term definitions *) p: P.t lazy_t; } @@ -539,7 +535,7 @@ module Make(A : ARG) clause [c] under given assumptions (each assm is a lit), and return [-a1 \/ … \/ -an \/ c], discharging assumptions *) - let conv_proof (msat:Sat_solver.proof) (t_defs:_ list) : P.t = + let conv_proof (msat:Sat_solver.Proof.t) (t_defs:_ list) : P.t = let assms = ref [] in let steps = ref [] in @@ -547,7 +543,7 @@ module Make(A : ARG) let n_tbl_: string SC.Tbl.t = SC.Tbl.create 32 in (* node.concl -> unique idx *) (* name of an already processed proof node *) - let find_proof_name (p:Sat_solver.proof) : string = + let find_proof_name (p:Sat_solver.Proof.t) : string = try SC.Tbl.find n_tbl_ (SP.conclusion p) with Not_found -> Error.errorf @@ -631,7 +627,7 @@ module Make(A : ARG) let t_defs = CCList.map (fun (c,rhs) -> P.deft c rhs) t_defs in P.composite_l ~assms (CCList.append t_defs (List.rev !steps)) - let make (msat: Sat_solver.proof) (tdefs: _ list) : t = + let make (msat: Sat_solver.Proof.t) (tdefs: _ list) : t = { msat; tdefs; p=lazy (conv_proof msat tdefs) } let check self = SP.check self.msat @@ -912,7 +908,7 @@ module Make(A : ARG) match r with | Sat_solver.Sat st -> Log.debug 1 "sidekick.msat-solver: SAT"; - let _lits f = st.iter_trail f (fun _ -> ()) in + let _lits f = st.iter_trail f in (* TODO: theory combination *) let m = mk_model self _lits in do_on_exit (); diff --git a/src/sat/Internal.ml b/src/sat/Internal.ml deleted file mode 100644 index 7e704403..00000000 --- a/src/sat/Internal.ml +++ /dev/null @@ -1,2275 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -module type PLUGIN = sig - val mcsat : bool - (** Is this a mcsat plugin? *) - - val has_theory : bool - (** Is this a CDCL(T) plugin or mcsat plugin? - i.e does it have theories *) - - include Solver_intf.PLUGIN_MCSAT -end - -let invalid_argf fmt = - Format.kasprintf (fun msg -> invalid_arg ("msat: " ^ msg)) fmt - -module Make(Plugin : PLUGIN) -= struct - module Term = Plugin.Term - module Formula = Plugin.Formula - module Value = Plugin.Value - - type term = Term.t - type formula = Formula.t - type theory = Plugin.t - type lemma = Plugin.proof - type value = Value.t - - (* MCSAT literal *) - type lit = { - lid : int; - term : term; - mutable l_level : int; - mutable l_idx: int; - mutable l_weight : float; - mutable assigned : value option; - } - - type var = { - vid : int; - pa : atom; - na : atom; - mutable v_fields : int; - mutable v_level : int; - mutable v_idx: int; (** position in heap *) - mutable v_weight : float; (** Weight (for the heap), tracking activity *) - mutable v_assignable: lit list option; - mutable reason : reason option; - } - - and atom = { - aid : int; - var : var; - neg : atom; - lit : formula; - mutable is_true : bool; - watched : clause Vec.t; - } - - and clause = { - cid: int; - atoms : atom array; - mutable cpremise : premise; - mutable activity : float; - mutable flags: int; (* bitfield *) - } - - and reason = - | Decision - | Bcp of clause - | Bcp_lazy of clause lazy_t - | Semantic - - (* TODO: remove, replace with user-provided proof trackng device? - for pure SAT, [reason] is sufficient *) - and premise = - | Hyp of lemma - | Local - | Lemma of lemma - | History of clause list - | Empty_premise - - type elt = - | E_lit of lit - | E_var of var - - type trail_elt = - | Lit of lit - | Atom of atom - - (* Constructors *) - module MF = Hashtbl.Make(Formula) - module MT = Hashtbl.Make(Term) - - type st = { - t_map: lit MT.t; - f_map: var MF.t; - vars: elt Vec.t; - mutable cpt_mk_var: int; - mutable cpt_mk_clause: int; - } - - let create_st ?(size=`Big) () : st = - let size_map = match size with - | `Tiny -> 8 - | `Small -> 16 - | `Big -> 4096 - in - { f_map = MF.create size_map; - t_map = MT.create size_map; - vars = Vec.create(); - cpt_mk_var = 0; - cpt_mk_clause = 0; - } - - let nb_elt st = Vec.size st.vars - let get_elt st i = Vec.get st.vars i - let iter_elt st f = Vec.iter f st.vars - - let name_of_clause c = match c.cpremise with - | Hyp _ -> "H" ^ string_of_int c.cid - | Lemma _ -> "T" ^ string_of_int c.cid - | Local -> "L" ^ string_of_int c.cid - | History _ -> "C" ^ string_of_int c.cid - | Empty_premise -> string_of_int c.cid - - module Lit = struct - type t = lit - let[@inline] term l = l.term - let[@inline] level l = l.l_level - let[@inline] assigned l = l.assigned - let[@inline] weight l = l.l_weight - - let make (st:st) (t:term) : t = - try MT.find st.t_map t - with Not_found -> - let res = { - lid = st.cpt_mk_var; - term = t; - l_weight = 1.; - l_idx= -1; - l_level = -1; - assigned = None; - } in - st.cpt_mk_var <- st.cpt_mk_var + 1; - MT.add st.t_map t res; - Vec.push st.vars (E_lit res); - res - - let debug_assign fmt v = - match v.assigned with - | None -> - Format.fprintf fmt "" - | Some t -> - Format.fprintf fmt "@[@@%d->@ %a@]" v.l_level Value.pp t - - let pp out v = Term.pp out v.term - let debug out v = - Format.fprintf out "%d[%a][lit:@[%a@]]" - (v.lid+1) debug_assign v Term.pp v.term - end - - (* some boolean flags for variables, used as masks *) - let seen_var = 0b1 - let seen_pos = 0b10 - let seen_neg = 0b100 - let default_pol_true = 0b1000 - - module Var = struct - type t = var - let[@inline] level v = v.v_level - let[@inline] pos v = v.pa - let[@inline] neg v = v.na - let[@inline] reason v = v.reason - let[@inline] assignable v = v.v_assignable - let[@inline] weight v = v.v_weight - let[@inline] mark v = v.v_fields <- v.v_fields lor seen_var - let[@inline] unmark v = v.v_fields <- v.v_fields land (lnot seen_var) - let[@inline] marked v = (v.v_fields land seen_var) <> 0 - let[@inline] set_default_pol_true v = v.v_fields <- v.v_fields lor default_pol_true - let[@inline] set_default_pol_false v = v.v_fields <- v.v_fields land (lnot default_pol_true) - let[@inline] default_pol v = (v.v_fields land default_pol_true) <> 0 - - let make ?(default_pol=true) (st:st) (t:formula) : var * Solver_intf.negated = - let lit, negated = Formula.norm t in - try - MF.find st.f_map lit, negated - with Not_found -> - let cpt_double = st.cpt_mk_var lsl 1 in - let rec var = - { vid = st.cpt_mk_var; - pa = pa; - na = na; - v_fields = 0; - v_level = -1; - v_idx= -1; - v_weight = 0.; - v_assignable = None; - reason = None; - } - and pa = - { var = var; - lit = lit; - watched = Vec.create(); - neg = na; - is_true = false; - aid = cpt_double (* aid = vid*2 *) } - and na = - { var = var; - lit = Formula.neg lit; - watched = Vec.create(); - neg = pa; - is_true = false; - aid = cpt_double + 1 (* aid = vid*2+1 *) } in - MF.add st.f_map lit var; - st.cpt_mk_var <- st.cpt_mk_var + 1; - if default_pol then set_default_pol_true var; - Vec.push st.vars (E_var var); - var, negated - - (* Marking helpers *) - let[@inline] clear v = - v.v_fields <- 0 - - let[@inline] seen_both v = - (seen_pos land v.v_fields <> 0) && - (seen_neg land v.v_fields <> 0) - end - - module Atom = struct - type t = atom - let[@inline] level a = a.var.v_level - let[@inline] var a = a.var - let[@inline] neg a = a.neg - let[@inline] abs a = a.var.pa - let[@inline] formula a = a.lit - let[@inline] equal a b = a == b - let[@inline] sign a = a == abs a - let[@inline] hash a = Hashtbl.hash a.aid - let[@inline] compare a b = compare a.aid b.aid - let[@inline] reason a = Var.reason a.var - let[@inline] id a = a.aid - let[@inline] is_true a = a.is_true - let[@inline] is_false a = a.neg.is_true - let has_value a = is_true a || is_false a - - let[@inline] seen a = - if sign a - then (seen_pos land a.var.v_fields <> 0) - else (seen_neg land a.var.v_fields <> 0) - - let[@inline] mark a = - let pos = equal a (abs a) in - if pos then ( - a.var.v_fields <- seen_pos lor a.var.v_fields - ) else ( - a.var.v_fields <- seen_neg lor a.var.v_fields - ) - - let[@inline] make ?default_pol st lit = - let var, negated = Var.make ?default_pol st lit in - match negated with - | Solver_intf.Negated -> var.na - | Solver_intf.Same_sign -> var.pa - - let pp fmt a = Formula.pp fmt a.lit - - let pp_a fmt v = - if Array.length v = 0 then ( - Format.fprintf fmt "∅" - ) else ( - pp fmt v.(0); - if (Array.length v) > 1 then begin - for i = 1 to (Array.length v) - 1 do - Format.fprintf fmt " ∨ %a" pp v.(i) - done - end - ) - - (* Complete debug printing *) - let pp_sign a = if a == a.var.pa then "+" else "-" - - let debug_reason fmt = function - | n, _ when n < 0 -> - Format.fprintf fmt "%%" - | n, None -> - Format.fprintf fmt "%d" n - | n, Some Decision -> - Format.fprintf fmt "@@%d" n - | n, Some Bcp c -> - Format.fprintf fmt "->%d/%s" n (name_of_clause c) - | n, Some (Bcp_lazy _) -> - Format.fprintf fmt "->%d/" n - | n, Some Semantic -> - Format.fprintf fmt "::%d" n - - let pp_level fmt a = - debug_reason fmt (a.var.v_level, a.var.reason) - - let debug_value fmt a = - if a.is_true then - Format.fprintf fmt "T%a" pp_level a - else if a.neg.is_true then - Format.fprintf fmt "F%a" pp_level a - else - Format.fprintf fmt "" - - let debug out a = - Format.fprintf out "%s%d[%a][atom:@[%a@]]" - (pp_sign a) (a.var.vid+1) debug_value a Formula.pp a.lit - - let debug_a out vec = - Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec - let debug_l out l = - List.iter (fun a -> Format.fprintf out "%a@ " debug a) l - - module Set = Set.Make(struct type t=atom let compare=compare end) - end - - (* Elements *) - module Elt = struct - type t = elt - let[@inline] of_lit l = E_lit l - let[@inline] of_var v = E_var v - - let[@inline] id = function - | E_lit l -> l.lid | E_var v -> v.vid - let[@inline] level = function - | E_lit l -> l.l_level | E_var v -> v.v_level - let[@inline] idx = function - | E_lit l -> l.l_idx | E_var v -> v.v_idx - let[@inline] weight = function - | E_lit l -> l.l_weight | E_var v -> v.v_weight - - let[@inline] set_level e lvl = match e with - | E_lit l -> l.l_level <- lvl | E_var v -> v.v_level <- lvl - let[@inline] set_idx e i = match e with - | E_lit l -> l.l_idx <- i | E_var v -> v.v_idx <- i - let[@inline] set_weight e w = match e with - | E_lit l -> l.l_weight <- w | E_var v -> v.v_weight <- w - end - - module Trail_elt = struct - type t = trail_elt - let[@inline] of_lit l = Lit l - let[@inline] of_atom a = Atom a - - let debug fmt = function - | Lit l -> Lit.debug fmt l - | Atom a -> Atom.debug fmt a - end - - module Clause = struct - type t = clause - - let make_a = - let n = ref 0 in - fun ~flags atoms premise -> - let cid = !n in - incr n; - { cid; - atoms = atoms; - flags; - activity = 0.; - cpremise = premise} - - let make ~flags l premise = make_a ~flags (Array.of_list l) premise - - let empty = make [] (History []) - let name = name_of_clause - let[@inline] equal c1 c2 = c1.cid = c2.cid - let[@inline] hash c = Hashtbl.hash c.cid - let[@inline] atoms c = c.atoms - let[@inline] atoms_seq c = Iter.of_array c.atoms - let[@inline] atoms_l c = Array.to_list c.atoms - - let flag_attached = 0b1 - let flag_visited = 0b10 - let flag_removable = 0b100 - let flag_dead = 0b1000 - - let[@inline] make_removable l premise = make ~flags:flag_removable l premise - let[@inline] make_removable_a l premise = make_a ~flags:flag_removable l premise - let[@inline] make_permanent l premise = make ~flags:0 l premise - - let[@inline] visited c = (c.flags land flag_visited) <> 0 - let[@inline] set_visited c b = - if b then c.flags <- c.flags lor flag_visited - else c.flags <- c.flags land lnot flag_visited - - let[@inline] attached c = (c.flags land flag_attached) <> 0 - let[@inline] set_attached c b = - if b then c.flags <- c.flags lor flag_attached - else c.flags <- c.flags land lnot flag_attached - - let[@inline] removable c = (c.flags land flag_removable) <> 0 - let[@inline] set_removable c b = - if b then c.flags <- c.flags lor flag_removable - else c.flags <- c.flags land lnot flag_removable - - let[@inline] dead c = (c.flags land flag_dead) <> 0 - let[@inline] set_dead c = c.flags <- c.flags lor flag_dead - - let[@inline] activity c = c.activity - let[@inline] set_activity c w = c.activity <- w - - module Tbl = Hashtbl.Make(struct - type t = clause - let hash = hash - let equal = equal - end) - - let pp fmt c = - Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms - - let debug_premise out = function - | Hyp _ -> Format.fprintf out "hyp" - | Lemma _ -> Format.fprintf out "th_lemma" - | Local -> Format.fprintf out "local" - | History v -> - List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v - | Empty_premise -> Format.fprintf out "" - - let debug out ({atoms=arr; cpremise=cp;_}as c) = - Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" - (name c) Atom.debug_a arr debug_premise cp - end - - module Proof = struct - exception Resolution_error of string - - type atom = Atom.t - type clause = Clause.t - type formula = Formula.t - type lemma = Plugin.proof - - let error_res_f msg = Format.kasprintf (fun s -> raise (Resolution_error s)) msg - - let[@inline] clear_var_of_ (a:atom) = Var.clear a.var - - (* Compute resolution of 2 clauses. - returns [pivots, resulting_atoms] *) - let resolve (c1:clause) (c2:clause) : atom list * atom list = - (* invariants: only atoms in [c2] are marked, and the pivot is - cleared when traversing [c1] *) - Array.iter Atom.mark c2.atoms; - let pivots = ref [] in - let l = - Array.fold_left - (fun l a -> - if Atom.seen a then l - else if Atom.seen a.neg then ( - pivots := a.var.pa :: !pivots; - clear_var_of_ a; - l - ) else a::l) - [] c1.atoms - in - let l = - Array.fold_left (fun l a -> if Atom.seen a then a::l else l) l c2.atoms - in - Array.iter clear_var_of_ c2.atoms; - !pivots, l - - (* [find_dups c] returns a list of duplicate atoms, and the deduplicated list *) - let find_dups (c:clause) : atom list * atom list = - let res = - Array.fold_left - (fun (dups,l) a -> - if Atom.seen a then ( - a::dups, l - ) else ( - Atom.mark a; - dups, a::l - )) - ([], []) c.atoms - in - Array.iter clear_var_of_ c.atoms; - res - - (* do [c1] and [c2] have the same lits, modulo reordering and duplicates? *) - let same_lits (c1:atom Iter.t) (c2:atom Iter.t): bool = - let subset a b = - Iter.iter Atom.mark b; - let res = Iter.for_all Atom.seen a in - Iter.iter clear_var_of_ b; - res - in - subset c1 c2 && subset c2 c1 - - let prove conclusion = - match conclusion.cpremise with - | History [] -> assert false - | Empty_premise -> raise Solver_intf.No_proof - | _ -> conclusion - - let rec set_atom_proof a = - let aux acc b = - if Atom.equal a.neg b then acc - else set_atom_proof b :: acc - in - assert (a.var.v_level >= 0); - match (a.var.reason) with - | Some (Bcp c | Bcp_lazy (lazy c)) -> - Log.debugf 5 (fun k->k "(@[proof.analyze.clause@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c); - if Array.length c.atoms = 1 then ( - Log.debugf 5 (fun k -> k "(@[proof.analyze.old-reason@ %a@])" Atom.debug a); - c - ) else ( - assert (a.neg.is_true); - let r = History (c :: (Array.fold_left aux [] c.atoms)) in - let c' = Clause.make_permanent [a.neg] r in - a.var.reason <- Some (Bcp c'); - Log.debugf 5 - (fun k -> k "(@[proof.analyze.new-reason@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c'); - c' - ) - | _ -> - error_res_f "cannot prove atom %a" Atom.debug a - - let prove_unsat conflict = - if Array.length conflict.atoms = 0 then ( - conflict - ) else ( - Log.debugf 1 (fun k -> k "(@[sat.prove-unsat@ :from %a@])" Clause.debug conflict); - let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.atoms in - let res = Clause.make_permanent [] (History (conflict :: l)) in - Log.debugf 1 (fun k -> k "(@[sat.proof-found@ %a@])" Clause.debug res); - res - ) - - let prove_atom a = - if a.is_true && a.var.v_level = 0 then - Some (set_atom_proof a) - else - None - - type t = clause - and proof_node = { - conclusion : clause; - step : step; - } - and step = - | Hypothesis of lemma - | Assumption - | Lemma of lemma - | Duplicate of t * atom list - | Hyper_res of hyper_res_step - - and hyper_res_step = { - hr_init: t; - hr_steps: (atom * t) list; (* list of pivot+clause to resolve against [init] *) - } - - let[@inline] conclusion (p:t) : clause = p - - type res_step = { - rs_res: atom list; - rs_c1: clause; - rs_c2: clause; - rs_pivot: atom; - } - - (* find pivots for resolving [l] with [init], and also return - the atoms of the conclusion *) - let find_pivots (init:clause) (l:clause list) : _ * (atom * t) list = - Log.debugf 15 - (fun k->k "(@[proof.find-pivots@ :init %a@ :l %a@])" - Clause.debug init (Format.pp_print_list Clause.debug) l); - Array.iter Atom.mark init.atoms; - let steps = - List.map - (fun c -> - let pivot = - match - Iter.of_array c.atoms - |> Iter.filter (fun a -> Atom.seen (Atom.neg a)) - |> Iter.to_list - with - | [a] -> a - | [] -> - error_res_f "(@[proof.expand.pivot_missing@ %a@])" Clause.debug c - | pivots -> - error_res_f "(@[proof.expand.multiple_pivots@ %a@ :pivots %a@])" - Clause.debug c Atom.debug_l pivots - in - Array.iter Atom.mark c.atoms; (* add atoms to result *) - clear_var_of_ pivot; - Atom.abs pivot, c) - l - in - (* cleanup *) - let res = ref [] in - let cleanup_a_ a = - if Atom.seen a then ( - res := a :: !res; - clear_var_of_ a - ) - in - Array.iter cleanup_a_ init.atoms; - List.iter (fun c -> Array.iter cleanup_a_ c.atoms) l; - !res, steps - - let expand conclusion = - Log.debugf 5 (fun k -> k "(@[sat.proof.expand@ @[%a@]@])" Clause.debug conclusion); - match conclusion.cpremise with - | Lemma l -> - { conclusion; step = Lemma l; } - | Local -> - { conclusion; step = Assumption; } - | Hyp l -> - { conclusion; step = Hypothesis l; } - | History [] -> - error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion - | History [c] -> - let duplicates, res = find_dups c in - assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); - { conclusion; step = Duplicate (c, duplicates) } - | History (c :: r) -> - let res, steps = find_pivots c r in - assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); - { conclusion; step = Hyper_res {hr_init=c; hr_steps=steps}; } - | Empty_premise -> raise Solver_intf.No_proof - - let rec res_of_hyper_res (hr: hyper_res_step) : _ * _ * atom = - let {hr_init=c1; hr_steps=l} = hr in - match l with - | [] -> assert false - | [a, c2] -> c1, c2, a (* done *) - | (a,c2) :: steps' -> - (* resolve [c1] with [c2], then resolve that against [steps] *) - let pivots, l = resolve c1 c2 in - assert (match pivots with [a'] -> Atom.equal a a' | _ -> false); - let c_1_2 = Clause.make_removable l (History [c1; c2]) in - res_of_hyper_res {hr_init=c_1_2; hr_steps=steps'} - - (* Proof nodes manipulation *) - let is_leaf = function - | Hypothesis _ - | Assumption - | Lemma _ -> true - | Duplicate _ - | Hyper_res _ -> false - - let parents = function - | Hypothesis _ - | Assumption - | Lemma _ -> [] - | Duplicate (p, _) -> [p] - | Hyper_res {hr_init; hr_steps} -> hr_init :: List.map snd hr_steps - - let expl = function - | Hypothesis _ -> "hypothesis" - | Assumption -> "assumption" - | Lemma _ -> "lemma" - | Duplicate _ -> "duplicate" - | Hyper_res _ -> "hyper-resolution" - - (* Compute unsat-core by accumulating the leaves *) - let unsat_core proof = - let rec aux res acc = function - | [] -> res, acc - | c :: r -> - if not @@ Clause.visited c then ( - Clause.set_visited c true; - match c.cpremise with - | Empty_premise -> raise Solver_intf.No_proof - | Hyp _ | Lemma _ | Local -> aux (c :: res) acc r - | History h -> - let l = List.fold_left (fun acc c -> - if not @@ Clause.visited c then c :: acc else acc) r h in - aux res (c :: acc) l - ) else ( - aux res acc r - ) - in - let res, tmp = aux [] [] [proof] in - List.iter (fun c -> Clause.set_visited c false) res; - List.iter (fun c -> Clause.set_visited c false) tmp; - res - - module Tbl = Clause.Tbl - - type task = - | Enter of t - | Leaving of t - - let spop s = try Some (Stack.pop s) with Stack.Empty -> None - - let rec fold_aux s h f acc = - match spop s with - | None -> acc - | Some (Leaving c) -> - Tbl.add h c true; - fold_aux s h f (f acc (expand c)) - | Some (Enter c) -> - if not (Tbl.mem h c) then begin - Stack.push (Leaving c) s; - let node = expand c in - begin match node.step with - | Duplicate (p1, _) -> - Stack.push (Enter p1) s - | Hyper_res {hr_init=p1; hr_steps=l} -> - List.iter (fun (_,p2) -> Stack.push (Enter p2) s) l; - Stack.push (Enter p1) s; - | Hypothesis _ | Assumption | Lemma _ -> () - end - end; - fold_aux s h f acc - - let fold f acc p = - let h = Tbl.create 42 in - let s = Stack.create () in - Stack.push (Enter p) s; - fold_aux s h f acc - - let check_empty_conclusion (p:t) = - if Array.length p.atoms > 0 then ( - error_res_f "@[<2>Proof.check: non empty conclusion for clause@ %a@]" Clause.debug p; - ) - - let check (p:t) = fold (fun () _ -> ()) () p - end - type proof = Proof.t - - module H = (Heap.Make [@specialise]) (struct - type t = Elt.t - let[@inline] cmp i j = Elt.weight j < Elt.weight i (* comparison by weight *) - let idx = Elt.idx - let set_idx = Elt.set_idx - end) - - (* cause of "unsat", possibly conditional to local assumptions *) - type unsat_cause = - | US_local of { - first: atom; (* assumption which was found to be proved false *) - core: atom list; (* the set of assumptions *) - } - | US_false of clause (* true unsat *) - - exception E_sat - exception E_unsat of unsat_cause - exception UndecidedLit - exception Restart - exception Conflict of clause - - (* Log levels *) - let error = 1 - let warn = 3 - let info = 5 - let debug = 50 - - let var_decay : float = 1. /. 0.95 - (* inverse of the activity factor for variables. Default 1/0.95 *) - - let clause_decay : float = 1. /. 0.999 - (* inverse of the activity factor for clauses. Default 1/0.999 *) - - let restart_inc : float = 1.5 - (* multiplicative factor for restart limit, default 1.5 *) - - let learntsize_inc : float = 1.1 - (* multiplicative factor for [learntsize_factor] at each restart, default 1.1 *) - - (* Singleton type containing the current state *) - type t = { - st : st; - th: theory; - - store_proof: bool; (* do we store proofs? *) - - (* Clauses are simplified for eficiency purposes. In the following - vectors, the comments actually refer to the original non-simplified - clause. *) - - clauses_hyps : clause Vec.t; - (* clauses added by the user *) - clauses_learnt : clause Vec.t; - (* learnt clauses (tautologies true at any time, whatever the user level) *) - - clauses_to_add : clause Vec.t; - (* Clauses either assumed or pushed by the theory, waiting to be added. *) - - mutable unsat_at_0: clause option; - (* conflict at level 0, if any *) - - mutable next_decisions : atom list; - (* When the last conflict was a semantic one (mcsat), - this stores the next decision to make; - if some theory wants atoms to be decided on (for theory combination), - store them here. *) - - trail : trail_elt Vec.t; - (* decision stack + propagated elements (atoms or assignments). *) - - elt_levels : int Vec.t; - (* decision levels in [trail] *) - - mutable assumptions: atom Vec.t; - (* current assumptions *) - - mutable th_head : int; - (* Start offset in the queue {!trail} of - unit facts not yet seen by the theory. *) - mutable elt_head : int; - (* Start offset in the queue {!trail} of - unit facts to propagate, within the trail *) - - (* invariant: - - during propagation, th_head <= elt_head - - then, once elt_head reaches length trail, Th.assume is - called so that th_head can catch up with elt_head - - this is repeated until a fixpoint is reached; - - before a decision (and after the fixpoint), - th_head = elt_head = length trail - *) - - order : H.t; - (* Heap ordered by variable activity *) - - to_clear: var Vec.t; - (* variables to unmark *) - - mutable var_incr : float; - (* increment for variables' activity *) - - mutable clause_incr : float; - (* increment for clauses' activity *) - - mutable on_conflict : (atom array -> unit) option; - mutable on_decision : (atom -> unit) option; - mutable on_new_atom: (atom -> unit) option; - } - type solver = t - - (* intial restart limit *) - let restart_first = 100 - - (* initial limit for the number of learnt clauses, 1/3 of initial - number of clauses by default *) - let learntsize_factor = 1. /. 3. - - let _nop_on_conflict (_:atom array) = () - - (* Starting environment. *) - let create_ ~st ~store_proof (th:theory) : t = { - st; th; - unsat_at_0=None; - next_decisions = []; - - clauses_hyps = Vec.create(); - clauses_learnt = Vec.create(); - - clauses_to_add = Vec.create (); - to_clear=Vec.create(); - - th_head = 0; - elt_head = 0; - - trail = Vec.create (); - elt_levels = Vec.create(); - assumptions= Vec.create(); - - order = H.create(); - - var_incr = 1.; - clause_incr = 1.; - store_proof; - on_conflict = None; - on_decision= None; - on_new_atom = None; - } - - let create - ?on_conflict ?on_decision ?on_new_atom - ?(store_proof=true) ?(size=`Big) (th:theory) : t = - let st = create_st ~size () in - let st = create_ ~st ~store_proof th in - st.on_new_atom <- on_new_atom; - st.on_decision <- on_decision; - st.on_conflict <- on_conflict; - st - - let[@inline] st t = t.st - let[@inline] nb_clauses st = Vec.size st.clauses_hyps - let[@inline] decision_level st = Vec.size st.elt_levels - - (* Do we have a level-0 empty clause? *) - let[@inline] check_unsat_ st = - match st.unsat_at_0 with - | Some c -> raise (E_unsat (US_false c)) - | None -> () - - (* Iteration over subterms. - When incrementing activity, we want to be able to iterate over - all subterms of a formula. However, the function provided by the theory - may be costly (if it walks a tree-like structure, and does some processing - to ignore some subterms for instance), so we want to 'cache' the list - of subterms of each formula, so we have a field [v_assignable] - directly in variables to do so. *) - let iter_sub f v = - if Plugin.mcsat then ( - match v.v_assignable with - | Some l -> List.iter f l - | None -> assert false - ) - - let mk_atom_mcsat_ st a = - match a.var.v_assignable with - | Some _ -> () - | None -> - let l = ref [] in - Plugin.iter_assignable st.th - (fun t -> l := Lit.make st.st t :: !l) - a.var.pa.lit; - a.var.v_assignable <- Some !l; - () - - (* When we have a new literal, - we need to first create the list of its subterms. *) - let mk_atom ?default_pol st (f:formula) : atom = - let res = Atom.make ?default_pol st.st f in - if Plugin.mcsat then ( - mk_atom_mcsat_ st res; - ); - res - - (* Variable and literal activity. - Activity is used to decide on which variable to decide when propagation - is done. Uses a heap (implemented in Iheap), to keep track of variable activity. - To be more general, the heap only stores the variable/literal id (i.e an int). - When we add a variable (which wraps a formula), we also need to add all - its subterms. - *) - let rec insert_elt_order st (elt:elt) : unit = - H.insert st.order elt; - if Plugin.mcsat then ( - match elt with - | E_lit _ -> () - | E_var v -> insert_subterms_order st v - ) - - and insert_var_order st (v:var) : unit = - insert_elt_order st (E_var v) - - and insert_subterms_order st (v:var) : unit = - iter_sub (fun t -> insert_elt_order st (Elt.of_lit t)) v - - (* Add new litterals/atoms on which to decide on, even if there is no - clause that constrains it. - We could maybe check if they have already has been decided before - inserting them into the heap, if it appears that it helps performance. *) - let make_term st t = - let l = Lit.make st.st t in - if l.l_level < 0 then ( - insert_elt_order st (E_lit l); - ) - - let make_atom st (p:formula) : atom = - let a = mk_atom st p in - if a.var.v_level < 0 then ( - insert_elt_order st (E_var a.var); - (match st.on_new_atom with Some f -> f a | None -> ()); - ) else ( - assert (a.is_true || a.neg.is_true); - ); - a - - (* Rather than iterate over all the heap when we want to decrease all the - variables/literals activity, we instead increase the value by which - we increase the activity of 'interesting' var/lits. *) - let[@inline] var_decay_activity st = - st.var_incr <- st.var_incr *. var_decay - - let[@inline] clause_decay_activity st = - st.clause_incr <- st.clause_incr *. clause_decay - - (* increase activity of [v] *) - let var_bump_activity_aux st v = - v.v_weight <- v.v_weight +. st.var_incr; - if v.v_weight > 1e100 then ( - for i = 0 to nb_elt st.st - 1 do - Elt.set_weight (get_elt st.st i) ((Elt.weight (get_elt st.st i)) *. 1e-100) - done; - st.var_incr <- st.var_incr *. 1e-100; - ); - let elt = Elt.of_var v in - if H.in_heap elt then ( - H.decrease st.order elt - ) - - (* increase activity of literal [l] *) - let lit_bump_activity_aux (st:t) (l:lit): unit = - l.l_weight <- l.l_weight +. st.var_incr; - if l.l_weight > 1e100 then ( - iter_elt st.st (fun e -> Elt.set_weight e (Elt.weight e *. 1e-100)); - st.var_incr <- st.var_incr *. 1e-100; - ); - let elt = Elt.of_lit l in - if H.in_heap elt then ( - H.decrease st.order elt - ) - - (* increase activity of var [v] *) - let var_bump_activity st (v:var): unit = - var_bump_activity_aux st v; - iter_sub (lit_bump_activity_aux st) v - - (* increase activity of clause [c] *) - let clause_bump_activity st (c:clause) : unit = - c.activity <- c.activity +. st.clause_incr; - if c.activity > 1e20 then ( - Vec.iter (fun c -> c.activity <- c.activity *. 1e-20) st.clauses_learnt; - st.clause_incr <- st.clause_incr *. 1e-20 - ) - - (* Simplification of clauses. - - When adding new clauses, it is desirable to 'simplify' them, i.e - minimize the amount of literals in it, because it greatly reduces - the search space for new watched literals during propagation. - Additionally, we have to partition the lits, to ensure the watched - literals (which are the first two lits of the clause) are appropriate. - Indeed, it is better to watch true literals, and then unassigned literals. - Watching false literals should be a last resort, and come with constraints - (see {!add_clause}). - *) - exception Trivial - - (* [arr_to_list a i] converts [a.(i), ... a.(length a-1)] into a list *) - let arr_to_list arr i : _ list = - if i >= Array.length arr then [] - else Array.to_list (Array.sub arr i (Array.length arr - i)) - - (* Eliminates atom duplicates in clauses *) - let eliminate_duplicates clause : clause = - let trivial = ref false in - let duplicates = ref [] in - let res = ref [] in - Array.iter (fun a -> - if Atom.seen a then duplicates := a :: !duplicates - else ( - Atom.mark a; - res := a :: !res - )) - clause.atoms; - List.iter - (fun a -> - if Var.seen_both a.var then trivial := true; - Var.clear a.var) - !res; - if !trivial then ( - raise Trivial - ) else if !duplicates = [] then ( - clause - ) else ( - Clause.make ~flags:clause.flags !res (History [clause]) - ) - - (* Partition literals for new clauses, into: - - true literals (maybe makes the clause trivial if the lit is proved true at level 0) - - unassigned literals, yet to be decided - - false literals (not suitable to watch, those at level 0 can be removed from the clause) - - Clauses that propagated false lits are remembered to reconstruct resolution proofs. - *) - let partition atoms : atom list * clause list = - let rec partition_aux trues unassigned falses history i = - if i >= Array.length atoms then ( - trues @ unassigned @ falses, history - ) else ( - let a = atoms.(i) in - if a.is_true then ( - let l = a.var.v_level in - if l = 0 then - raise Trivial (* A var true at level 0 gives a trivially true clause *) - else - (a :: trues) @ unassigned @ falses @ - (arr_to_list atoms (i + 1)), history - ) else if a.neg.is_true then ( - let l = a.var.v_level in - if l = 0 then ( - match a.var.reason with - | Some (Bcp cl | Bcp_lazy (lazy cl)) -> - partition_aux trues unassigned falses (cl :: history) (i + 1) - (* A var false at level 0 can be eliminated from the clause, - but we need to kepp in mind that we used another clause to simplify it. *) - | Some Semantic -> - partition_aux trues unassigned falses history (i + 1) - (* Semantic propagations at level 0 are, well not easy to deal with, - this shouldn't really happen actually (because semantic propagations - at level 0 should come with a proof). *) - (* TODO: get a proof of the propagation. *) - | None | Some Decision -> assert false - (* The var must have a reason, and it cannot be a decision/assumption, - since its level is 0. *) - ) else ( - partition_aux trues unassigned (a::falses) history (i + 1) - ) - ) else ( - partition_aux trues (a::unassigned) falses history (i + 1) - ) - ) - in - partition_aux [] [] [] [] 0 - - - (* Making a decision. - Before actually creatig a new decision level, we check that - all propagations have been done and propagated to the theory, - i.e that the theoriy state indeed takes into account the whole - stack of literals - i.e we have indeed reached a propagation fixpoint before making - a new decision *) - let new_decision_level st = - assert (st.th_head = Vec.size st.trail); - assert (st.elt_head = Vec.size st.trail); - Vec.push st.elt_levels (Vec.size st.trail); - Plugin.push_level st.th; - () - - (* Attach/Detach a clause. - - A clause is attached (to its watching lits) when it is first added, - either because it is assumed or learnt. - - *) - let attach_clause c = - assert (not @@ Clause.attached c); - Log.debugf debug (fun k -> k "(@[sat.attach-clause@ %a@])" Clause.debug c); - Vec.push c.atoms.(0).neg.watched c; - Vec.push c.atoms.(1).neg.watched c; - Clause.set_attached c true; - () - - (* Backtracking. - Used to backtrack, i.e cancel down to [lvl] excluded, - i.e we want to go back to the state the solver was in - when decision level [lvl] was created. *) - let cancel_until st lvl = - assert (lvl >= 0); - (* Nothing to do if we try to backtrack to a non-existent level. *) - if decision_level st <= lvl then ( - Log.debugf debug (fun k -> k "(@[sat.cancel-until.nop@ :already-at-level <= %d@])" lvl) - ) else ( - Log.debugf info (fun k -> k "(@[sat.cancel-until %d@])" lvl); - (* We set the head of the solver and theory queue to what it was. *) - let head = ref (Vec.get st.elt_levels lvl) in - st.elt_head <- !head; - st.th_head <- !head; - (* Now we need to cleanup the vars that are not valid anymore - (i.e to the right of elt_head in the queue. *) - for c = st.elt_head to Vec.size st.trail - 1 do - match (Vec.get st.trail c) with - (* A literal is unassigned, we nedd to add it back to - the heap of potentially assignable literals, unless it has - a level lower than [lvl], in which case we just move it back. *) - | Lit l -> - if l.l_level <= lvl then ( - Vec.set st.trail !head (Trail_elt.of_lit l); - head := !head + 1 - ) else ( - l.assigned <- None; - l.l_level <- -1; - insert_elt_order st (Elt.of_lit l) - ) - (* A variable is not true/false anymore, one of two things can happen: *) - | Atom a -> - if a.var.v_level <= lvl then ( - (* It is a late propagation, which has a level - lower than where we backtrack, so we just move it to the head - of the queue, to be propagated again. *) - Vec.set st.trail !head (Trail_elt.of_atom a); - head := !head + 1 - ) else ( - (* it is a result of bolean propagation, or a semantic propagation - with a level higher than the level to which we backtrack, - in that case, we simply unset its value and reinsert it into the heap. *) - a.is_true <- false; - a.neg.is_true <- false; - a.var.v_level <- -1; - a.var.reason <- None; - insert_elt_order st (Elt.of_var a.var) - ) - done; - (* Recover the right theory state. *) - let n = decision_level st - lvl in - assert (n>0); - (* Resize the vectors according to their new size. *) - Vec.shrink st.trail !head; - Vec.shrink st.elt_levels lvl; - Plugin.pop_levels st.th n; - st.next_decisions <- []; - ); - () - - let pp_unsat_cause out = function - | US_local {first=_; core} -> - Format.fprintf out "(@[unsat-cause@ :false-assumptions %a@])" - (Format.pp_print_list Atom.pp) core - | US_false c -> - Format.fprintf out "(@[unsat-cause@ :false %a@])" Clause.debug c - - (* Unsatisfiability is signaled through an exception, since it can happen - in multiple places (adding new clauses, or solving for instance). *) - let report_unsat st (us:unsat_cause) : _ = - Log.debugf info (fun k -> k "(@[sat.unsat-conflict@ %a@])" pp_unsat_cause us); - let us = match us with - | US_false c -> - let c = if st.store_proof then Proof.prove_unsat c else c in - st.unsat_at_0 <- Some c; - US_false c - | _ -> us - in - raise (E_unsat us) - - (* Simplification of boolean propagation reasons. - When doing boolean propagation *at level 0*, it can happen - that the clause cl, which propagates a formula, also contains - other formulas, but has been simplified. in which case, we - need to rebuild a clause with correct history, in order to - be able to build a correct proof at the end of proof search. *) - let simpl_reason : reason -> reason = function - | (Bcp cl | Bcp_lazy (lazy cl)) as r -> - let l, history = partition cl.atoms in - begin match l with - | [_] -> - if history = [] then ( - (* no simplification has been done, so [cl] is actually a clause with only - [a], so it is a valid reason for propagating [a]. *) - r - ) else ( - (* Clauses in [history] have been used to simplify [cl] into a clause [tmp_cl] - with only one formula (which is [a]). So we explicitly create that clause - and set it as the cause for the propagation of [a], that way we can - rebuild the whole resolution tree when we want to prove [a]. *) - let c' = Clause.make ~flags:cl.flags l (History (cl :: history)) in - Log.debugf debug - (fun k -> k "(@[sat.simplified-reason@ %a@ %a@])" Clause.debug cl Clause.debug c'); - Bcp c' - ) - | _ -> - Log.debugf error - (fun k -> - k "(@[sat.simplify-reason.failed@ :at %a@ %a@]" - (Vec.pp ~sep:"" Atom.debug) (Vec.of_list l) - Clause.debug cl); - assert false - end - | (Decision | Semantic) as r -> r - - (* Boolean propagation. - Wrapper function for adding a new propagated formula. *) - let enqueue_bool st a ~level:lvl reason : unit = - if a.neg.is_true then ( - Log.debugf error - (fun k->k "(@[sat.error.trying to enqueue a false literal %a@])" Atom.debug a); - assert false - ); - assert (not a.is_true && a.var.v_level < 0 && - a.var.reason = None && lvl >= 0); - let reason = - if lvl > 0 then reason - else simpl_reason reason - in - a.is_true <- true; - a.var.v_level <- lvl; - a.var.reason <- Some reason; - Vec.push st.trail (Trail_elt.of_atom a); - Log.debugf debug - (fun k->k "(@[sat.enqueue[%d]@ %a@])" (Vec.size st.trail) Atom.debug a); - () - - let enqueue_semantic st a terms = - if not a.is_true then ( - let l = List.map (Lit.make st.st) terms in - let lvl = List.fold_left (fun acc {l_level; _} -> - assert (l_level > 0); max acc l_level) 0 l in - enqueue_bool st a ~level:lvl Semantic - ) - - (* MCsat semantic assignment *) - let enqueue_assign st (l:lit) (value:value) lvl = - match l.assigned with - | Some _ -> - Log.debugf error - (fun k -> k "(@[sat.error: Trying to assign an already assigned literal:@ %a@])" Lit.debug l); - assert false - | None -> - assert (l.l_level < 0); - l.assigned <- Some value; - l.l_level <- lvl; - Vec.push st.trail (Trail_elt.of_lit l); - Log.debugf debug - (fun k -> k "(@[sat.enqueue-semantic[%d]@ %a@])" (Vec.size st.trail) Lit.debug l); - () - - (* swap elements of array *) - let[@inline] swap_arr a i j = - if i<>j then ( - let tmp = a.(i) in - a.(i) <- a.(j); - a.(j) <- tmp; - ) - - (* move atoms assigned at high levels first *) - let put_high_level_atoms_first (arr:atom array) : unit = - Array.iteri - (fun i a -> - if i>0 && Atom.level a > Atom.level arr.(0) then ( - (* move first to second, [i]-th to first, second to [i] *) - if i=1 then ( - swap_arr arr 0 1; - ) else ( - let tmp = arr.(1) in - arr.(1) <- arr.(0); - arr.(0) <- arr.(i); - arr.(i) <- tmp; - ); - ) else if i>1 && Atom.level a > Atom.level arr.(1) then ( - swap_arr arr 1 i; - )) - arr - - (* evaluate an atom for MCsat, if it's not assigned - by boolean propagation/decision *) - let th_eval st a : bool option = - if a.is_true || a.neg.is_true then None - else match Plugin.eval st.th a.lit with - | Solver_intf.Unknown -> None - | Solver_intf.Valued (b, l) -> - if l = [] then ( - invalid_argf "semantic propagation at level 0 currently forbidden: %a" Atom.pp a; - ); - let atom = if b then a else a.neg in - enqueue_semantic st atom l; - Some b - - (* find which level to backtrack to, given a conflict clause - and a boolean stating whether it is - a UIP ("Unique Implication Point") - precond: the atom list is sorted by decreasing decision level *) - let backtrack_lvl _st (arr: atom array) : int * bool = - if Array.length arr <= 1 then ( - 0, true - ) else ( - let a = arr.(0) in - let b = arr.(1) in - assert(a.var.v_level > 0); - if a.var.v_level > b.var.v_level then ( - (* backtrack below [a], so we can propagate [not a] *) - b.var.v_level, true - ) else ( - assert (a.var.v_level = b.var.v_level); - assert (a.var.v_level >= 0); - max (a.var.v_level - 1) 0, false - ) - ) - - (* result of conflict analysis, containing the learnt clause and some - additional info. - - invariant: cr_history's order matters, as its head is later used - during pop operations to determine the origin of a clause/conflict - (boolean conflict i.e hypothesis, or theory lemma) *) - type conflict_res = { - cr_backtrack_lvl : int; (* level to backtrack to *) - cr_learnt: atom array; (* lemma learnt from conflict *) - cr_history: clause list; (* justification *) - cr_is_uip: bool; (* conflict is UIP? *) - } - - let[@inline] get_atom st i = - match Vec.get st.trail i with - | Atom x -> x - | Lit _ -> assert false - - (* conflict analysis for SAT - Same idea as the mcsat analyze function (without semantic propagations), - except we look the the Last UIP (TODO: check ?), and do it in an imperative - and efficient manner. *) - let analyze st c_clause : conflict_res = - let pathC = ref 0 in - let learnt = ref [] in - let cond = ref true in - let blevel = ref 0 in - let to_unmark = st.to_clear in (* for cleanup *) - let c = ref (Some c_clause) in - let tr_ind = ref (Vec.size st.trail - 1) in - let history = ref [] in - assert (decision_level st > 0); - Vec.clear to_unmark; - let conflict_level = - if Plugin.mcsat || Plugin.has_theory - then Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms - else decision_level st - in - Log.debugf debug - (fun k -> k "(@[sat.analyze-conflict@ :c-level %d@ :clause %a@])" conflict_level Clause.debug c_clause); - while !cond do - begin match !c with - | None -> - Log.debug debug "(@[sat.analyze-conflict: skipping resolution for semantic propagation@])" - | Some clause -> - Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); - if Clause.removable clause then ( - clause_bump_activity st clause; - ); - history := clause :: !history; - (* visit the current predecessors *) - for j = 0 to Array.length clause.atoms - 1 do - let q = clause.atoms.(j) in - assert (q.is_true || q.neg.is_true && q.var.v_level >= 0); (* unsure? *) - if q.var.v_level <= 0 then ( - assert (q.neg.is_true); - match q.var.reason with - | Some (Bcp cl | Bcp_lazy (lazy cl)) -> history := cl :: !history - | Some (Decision | Semantic) | None -> assert false - ); - if not (Var.marked q.var) then ( - Var.mark q.var; - Vec.push to_unmark q.var; - if q.var.v_level > 0 then ( - var_bump_activity st q.var; - if q.var.v_level >= conflict_level then ( - incr pathC; - ) else ( - learnt := q :: !learnt; - blevel := max !blevel q.var.v_level - ) - ) - ) - done - end; - - (* look for the next node to expand *) - while - let a = Vec.get st.trail !tr_ind in - Log.debugf debug - (fun k -> k "(@[sat.analyze-conflict.at-trail-elt@ %a@])" Trail_elt.debug a); - match a with - | Atom q -> - (not (Var.marked q.var)) || - (q.var.v_level < conflict_level) - | Lit _ -> true - do - decr tr_ind; - done; - let p = get_atom st !tr_ind in - decr pathC; - decr tr_ind; - match !pathC, p.var.reason with - | 0, _ -> - cond := false; - learnt := p.neg :: List.rev !learnt - | n, Some Semantic -> - assert (n > 0); - learnt := p.neg :: !learnt; - c := None - | n, Some (Bcp cl | Bcp_lazy (lazy cl)) -> - assert (n > 0); - assert (p.var.v_level >= conflict_level); - c := Some cl - | _, (None | Some Decision) -> assert false - done; - Vec.iter Var.clear to_unmark; - Vec.clear to_unmark; - (* put high-level literals first, so that: - - they make adequate watch lits - - the first literal is the UIP, if any *) - let a = Array.of_list !learnt in - Array.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) a; - (* put_high_level_atoms_first a; *) - let level, is_uip = backtrack_lvl st a in - { cr_backtrack_lvl = level; - cr_learnt = a; - cr_history = List.rev !history; - cr_is_uip = is_uip; - } - - (* add the learnt clause to the clause database, propagate, etc. *) - let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = - let proof = if st.store_proof then History cr.cr_history else Empty_premise in - begin match cr.cr_learnt with - | [| |] -> assert false - | [|fuip|] -> - assert (cr.cr_backtrack_lvl = 0 && decision_level st = 0); - if fuip.neg.is_true then ( - (* incompatible at level 0 *) - report_unsat st (US_false confl) - ) else ( - let uclause = Clause.make_removable_a cr.cr_learnt proof in - (* no need to attach [uclause], it is true at level 0 *) - enqueue_bool st fuip ~level:0 (Bcp uclause) - ) - | _ -> - let fuip = cr.cr_learnt.(0) in - let lclause = Clause.make_removable_a cr.cr_learnt proof in - if Array.length lclause.atoms > 2 then ( - Vec.push st.clauses_learnt lclause; (* potentially gc'able *) - ); - attach_clause lclause; - clause_bump_activity st lclause; - if cr.cr_is_uip then ( - enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) - ) else ( - assert Plugin.mcsat; - assert (st.next_decisions = []); - st.next_decisions <- [fuip.neg]; - ) - end; - var_decay_activity st; - clause_decay_activity st - - (* process a conflict: - - learn clause - - backtrack - - report unsat if conflict at level 0 - *) - let add_boolean_conflict st (confl:clause): unit = - Log.debugf info (fun k -> k "(@[sat.add-bool-conflict@ %a@])" Clause.debug confl); - st.next_decisions <- []; - assert (decision_level st >= 0); - if decision_level st = 0 || - Array.for_all (fun a -> a.var.v_level <= 0) confl.atoms then ( - (* Top-level conflict *) - report_unsat st (US_false confl); - ); - let cr = analyze st confl in - cancel_until st (max cr.cr_backtrack_lvl 0); - record_learnt_clause st confl cr - - (* Get the correct vector to insert a clause in. *) - let[@inline] add_clause_to_vec st c = - if Clause.removable c then ( - Vec.push st.clauses_learnt c - ) else ( - Vec.push st.clauses_hyps c - ) - - (* Add a new clause, simplifying, propagating, and backtracking if - the clause is false in the current trail *) - let add_clause_ st (init:clause) : unit = - Log.debugf debug (fun k -> k "(@[sat.add-clause@ @[%a@]@])" Clause.debug init); - (* Insertion of new lits is done before simplification. Indeed, else a lit in a - trivial clause could end up being not decided on, which is a bug. *) - Array.iter (fun x -> insert_elt_order st (Elt.of_var x.var)) init.atoms; - try - let c = eliminate_duplicates init in - assert (c.flags = init.flags); - Log.debugf debug (fun k -> k "(@[sat.dups-removed@ %a@])" Clause.debug c); - let atoms, history = partition c.atoms in - let clause = - if history = [] then ( - (* just update order of atoms *) - List.iteri (fun i a -> c.atoms.(i) <- a) atoms; - c - ) else ( - let proof = if st.store_proof then History (c::history) else Empty_premise in - Clause.make ~flags:c.flags atoms proof - ) - in - assert (clause.flags = init.flags); - Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); - match atoms with - | [] -> - report_unsat st @@ US_false clause - | [a] -> - cancel_until st 0; - if a.neg.is_true then ( - (* cannot recover from this *) - report_unsat st @@ US_false clause - ) else if a.is_true then ( - () (* atom is already true, nothing to do *) - ) else ( - Log.debugf debug - (fun k->k "(@[sat.add-clause.unit-clause@ :propagating %a@])" Atom.debug a); - add_clause_to_vec st clause; - enqueue_bool st a ~level:0 (Bcp clause) - ) - | a::b::_ -> - add_clause_to_vec st clause; - if a.neg.is_true then ( - (* Atoms need to be sorted in decreasing order of decision level, - or we might watch the wrong literals. *) - put_high_level_atoms_first clause.atoms; - attach_clause clause; - add_boolean_conflict st clause - ) else ( - attach_clause clause; - if b.neg.is_true && not a.is_true && not a.neg.is_true then ( - let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in - cancel_until st lvl; - enqueue_bool st a ~level:lvl (Bcp clause) - ) - ) - with Trivial -> - Log.debugf info - (fun k->k "(@[sat.add-clause@ :ignore-trivial @[%a@]@])" Clause.debug init) - - let[@inline never] flush_clauses_ st = - while not @@ Vec.is_empty st.clauses_to_add do - let c = Vec.pop st.clauses_to_add in - add_clause_ st c - done - - let[@inline] flush_clauses st = - if not @@ Vec.is_empty st.clauses_to_add then flush_clauses_ st - - type watch_res = - | Watch_kept - | Watch_removed - - (* boolean propagation. - [a] is the false atom, one of [c]'s two watch literals - [i] is the index of [c] in [a.watched] - @return whether [c] was removed from [a.watched] - *) - let propagate_in_clause st (a:atom) (c:clause) (i:int): watch_res = - let atoms = c.atoms in - let first = atoms.(0) in - if first == a.neg then ( - (* false lit must be at index 1 *) - atoms.(0) <- atoms.(1); - atoms.(1) <- first - ) else ( - assert (a.neg == atoms.(1)) - ); - let first = atoms.(0) in - if first.is_true - then Watch_kept (* true clause, keep it in watched *) - else ( - try (* look for another watch lit *) - for k = 2 to Array.length atoms - 1 do - let ak = atoms.(k) in - if not (ak.neg.is_true) then ( - (* watch lit found: update and exit *) - atoms.(1) <- ak; - atoms.(k) <- a.neg; - (* remove [c] from [a.watched], add it to [ak.neg.watched] *) - Vec.push ak.neg.watched c; - assert (Vec.get a.watched i == c); - Vec.fast_remove a.watched i; - raise_notrace Exit - ) - done; - (* no watch lit found *) - if first.neg.is_true then ( - (* clause is false *) - st.elt_head <- Vec.size st.trail; - raise_notrace (Conflict c) - ) else ( - match th_eval st first with - | None -> (* clause is unit, keep the same watches, but propagate *) - enqueue_bool st first ~level:(decision_level st) (Bcp c) - | Some true -> () - | Some false -> - st.elt_head <- Vec.size st.trail; - raise_notrace (Conflict c) - ); - Watch_kept - with Exit -> - Watch_removed - ) - - (* propagate atom [a], which was just decided. This checks every - clause watching [a] to see if the clause is false, unit, or has - other possible watches - @param res the optional conflict clause that the propagation might trigger *) - let propagate_atom st a : unit = - let watched = a.watched in - let rec aux i = - if i >= Vec.size watched then () - else ( - let c = Vec.get watched i in - assert (Clause.attached c); - let j = - if Clause.dead c then ( - Vec.fast_remove watched i; - i - ) else ( - match propagate_in_clause st a c i with - | Watch_kept -> i+1 - | Watch_removed -> i (* clause at this index changed *) - ) - in - aux j - ) - in - aux 0 - - (* Propagation (boolean and theory) *) - let create_atom ?default_pol st f = - let a = mk_atom ?default_pol st f in - ignore (th_eval st a); - a - - exception Th_conflict of Clause.t - - let slice_get st i = - match Vec.get st.trail i with - | Atom a -> - Solver_intf.Lit a.lit - | Lit {term; assigned = Some v; _} -> - Solver_intf.Assign (term, v) - | Lit _ -> assert false - - let acts_add_clause st ?(keep=false) (l:formula list) (lemma:lemma): unit = - let atoms = List.rev_map (create_atom st) l in - let flags = if keep then 0 else Clause.flag_removable in - let c = Clause.make ~flags atoms (Lemma lemma) in - Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); - Vec.push st.clauses_to_add c - - let acts_add_decision_lit (st:t) (f:formula) (sign:bool) : unit = - let a = create_atom st f in - let a = if sign then a else Atom.neg a in - if not (Atom.has_value a) then ( - Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); - st.next_decisions <- a :: st.next_decisions - ) - - let acts_raise st (l:formula list) proof : 'a = - let atoms = List.rev_map (create_atom st) l in - (* conflicts can be removed *) - let c = Clause.make_removable atoms (Lemma proof) in - Log.debugf 5 (fun k->k "(@[@{sat.th.raise-conflict@}@ %a@])" Clause.debug c); - raise_notrace (Th_conflict c) - - let check_consequence_lits_false_ l : unit = - match List.find Atom.is_true l with - | a -> - invalid_argf - "slice.acts_propagate:@ Consequence should contain only true literals, but %a isn't" - Atom.debug (Atom.neg a) - | exception Not_found -> () - - let acts_propagate (st:t) f = function - | Solver_intf.Eval l -> - let a = mk_atom st f in - enqueue_semantic st a l - | Solver_intf.Consequence mk_expl -> - let p = mk_atom st f in - if Atom.is_true p then () - else if Atom.is_false p then ( - let lits, proof = mk_expl() in - let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in - check_consequence_lits_false_ l; - let c = Clause.make_removable (p :: l) (Lemma proof) in - raise_notrace (Th_conflict c) - ) else ( - insert_var_order st p.var; - let c = lazy ( - let lits, proof = mk_expl () in - let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in - (* note: we can check that invariant here in the [lazy] block, - as conflict analysis will run in an environment where - the literals should be true anyway, since it's an extension of the - current trail - (otherwise the propagated lit would have been backtracked and - discarded already.) *) - check_consequence_lits_false_ l; - Clause.make_removable (p :: l) (Lemma proof) - ) in - let level = decision_level st in - enqueue_bool st p ~level (Bcp_lazy c) - ) - - let[@specialise] acts_iter st ~full head f : unit = - for i = (if full then 0 else head) to Vec.size st.trail-1 do - let e = match Vec.get st.trail i with - | Atom a -> - Solver_intf.Lit a.lit - | Lit {term; assigned = Some v; _} -> - Solver_intf.Assign (term, v) - | Lit _ -> assert false - in - f e - done - - let eval_atom_ a = - if Atom.is_true a then Solver_intf.L_true - else if Atom.is_false a then Solver_intf.L_false - else Solver_intf.L_undefined - - let[@inline] acts_eval_lit st (f:formula) : Solver_intf.lbool = - let a = create_atom st f in - eval_atom_ a - - let[@inline] acts_mk_lit st ?default_pol f : unit = - ignore (create_atom ?default_pol st f : atom) - - let[@inline] acts_mk_term st t : unit = make_term st t - - let[@inline] current_slice st : _ Solver_intf.acts = { - Solver_intf. - acts_iter_assumptions=acts_iter st ~full:false st.th_head; - acts_eval_lit= acts_eval_lit st; - acts_mk_lit=acts_mk_lit st; - acts_mk_term=acts_mk_term st; - acts_add_clause = acts_add_clause st; - acts_propagate = acts_propagate st; - acts_raise_conflict=acts_raise st; - acts_add_decision_lit=acts_add_decision_lit st; - } - - (* full slice, for [if_sat] final check *) - let[@inline] full_slice st : _ Solver_intf.acts = { - Solver_intf. - acts_iter_assumptions=acts_iter st ~full:true st.th_head; - acts_eval_lit= acts_eval_lit st; - acts_mk_lit=acts_mk_lit st; - acts_mk_term=acts_mk_term st; - acts_add_clause = acts_add_clause st; - acts_propagate = acts_propagate st; - acts_raise_conflict=acts_raise st; - acts_add_decision_lit=acts_add_decision_lit st; - } - - (* Assert that the conflict is indeeed a conflict *) - let check_is_conflict_ (c:Clause.t) : unit = - if not @@ Array.for_all (Atom.is_false) c.atoms then ( - invalid_argf "conflict should be false: %a" Clause.debug c - ) - - (* some boolean literals were decided/propagated within Msat. Now we - need to inform the theory of those assumptions, so it can do its job. - @return the conflict clause, if the theory detects unsatisfiability *) - let rec theory_propagate st : clause option = - assert (st.elt_head = Vec.size st.trail); - assert (st.th_head <= st.elt_head); - if st.th_head = st.elt_head then ( - None (* fixpoint/no propagation *) - ) else ( - let slice = current_slice st in - st.th_head <- st.elt_head; (* catch up *) - match Plugin.partial_check st.th slice with - | () -> - flush_clauses st; - propagate st - | exception Th_conflict c -> - check_is_conflict_ c; - Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; - Some c - ) - - (* fixpoint between boolean propagation and theory propagation - @return a conflict clause, if any *) - and propagate (st:t) : clause option = - (* First, treat the stack of lemmas added by the theory, if any *) - flush_clauses st; - (* Now, check that the situation is sane *) - assert (st.elt_head <= Vec.size st.trail); - if st.elt_head = Vec.size st.trail then ( - theory_propagate st - ) else ( - match - while st.elt_head < Vec.size st.trail do - begin match Vec.get st.trail st.elt_head with - | Lit _ -> () - | Atom a -> propagate_atom st a - end; - st.elt_head <- st.elt_head + 1; - done; - with - | () -> theory_propagate st - | exception Conflict c -> Some c - ) - - (* compute unsat core from assumption [a] *) - let analyze_final (self:t) (a:atom) : atom list = - Log.debugf 5 (fun k->k "(@[sat.analyze-final@ :lit %a@])" Atom.debug a); - assert (Atom.is_false a); - let core = ref [a] in - let idx = ref (Vec.size self.trail - 1) in - Var.mark a.var; - let seen = ref [a.var] in - while !idx >= 0 do - begin match Vec.get self.trail !idx with - | Lit _ -> () (* semantic decision, ignore *) - | Atom a' -> - if Var.marked a'.var then ( - match Atom.reason a' with - | Some Semantic -> () - | Some Decision -> core := a' :: !core - | Some (Bcp c | Bcp_lazy (lazy c)) -> - Array.iter - (fun a -> - let v = a.var in - if not @@ Var.marked v then ( - seen := v :: !seen; - Var.mark v; - )) - c.atoms - | None -> () - ); - end; - decr idx - done; - List.iter Var.unmark !seen; - Log.debugf 5 (fun k->k "(@[sat.analyze-final.done@ :core %a@])" (Format.pp_print_list Atom.debug) !core); - !core - - (* remove some learnt clauses. *) - let reduce_db (st:t) (n_of_learnts: int) : unit = - let v = st.clauses_learnt in - Log.debugf 3 (fun k->k "(@[sat.gc.start :keep %d :out-of %d@])" n_of_learnts (Vec.size v)); - assert (Vec.size v > n_of_learnts); - (* sort by decreasing activity *) - Vec.sort v (fun c1 c2 -> compare c2.activity c1.activity); - let n_collected = ref 0 in - while Vec.size v > n_of_learnts do - let c = Vec.pop v in - assert (Clause.removable c); - Clause.set_dead c; - assert (Clause.dead c); - incr n_collected; - done; - Log.debugf 3 (fun k->k "(@[sat.gc.done :collected %d@])" !n_collected); - () - - (* Decide on a new literal, and enqueue it into the trail *) - let rec pick_branch_aux st atom : unit = - let v = atom.var in - if v.v_level >= 0 then ( - assert (v.pa.is_true || v.na.is_true); - pick_branch_lit st - ) else if Plugin.mcsat then ( - match Plugin.eval st.th atom.lit with - | Solver_intf.Unknown -> - new_decision_level st; - let current_level = decision_level st in - enqueue_bool st atom ~level:current_level Decision; - (match st.on_decision with Some f -> f atom | None -> ()); - | Solver_intf.Valued (b, l) -> - let a = if b then atom else atom.neg in - enqueue_semantic st a l - ) else ( - new_decision_level st; - let current_level = decision_level st in - enqueue_bool st atom ~level:current_level Decision; - (match st.on_decision with Some f -> f atom | None -> ()); - ) - - and pick_branch_lit st = - match st.next_decisions with - | atom :: tl -> - st.next_decisions <- tl; - pick_branch_aux st atom - | [] when decision_level st < Vec.size st.assumptions -> - (* use an assumption *) - let a = Vec.get st.assumptions (decision_level st) in - if Atom.is_true a then ( - new_decision_level st; (* pseudo decision level, [a] is already true *) - pick_branch_lit st - ) else if Atom.is_false a then ( - (* root conflict, find unsat core *) - let core = analyze_final st a in - raise (E_unsat (US_local {first=a; core})) - ) else ( - pick_branch_aux st a - ) - | [] -> - begin match H.remove_min st.order with - | E_lit l -> - if Lit.level l >= 0 then ( - pick_branch_lit st - ) else ( - let value = Plugin.assign st.th l.term in - new_decision_level st; - let current_level = decision_level st in - enqueue_assign st l value current_level - ) - | E_var v -> - pick_branch_aux st (if Var.default_pol v then v.pa else v.na) - | exception Not_found -> raise_notrace E_sat - end - - (* do some amount of search, until the number of conflicts or clause learnt - reaches the given parameters *) - let search (st:t) n_of_conflicts n_of_learnts : unit = - Log.debugf 3 - (fun k->k "(@[sat.search@ :n-conflicts %d@ :n-learnt %d@])" n_of_conflicts n_of_learnts); - let conflictC = ref 0 in - while true do - match propagate st with - | Some confl -> (* Conflict *) - incr conflictC; - (* When the theory has raised Unsat, add_boolean_conflict - might 'forget' the initial conflict clause, and only add the - analyzed backtrack clause. So in those case, we use add_clause - to make sure the initial conflict clause is also added. *) - if Clause.attached confl then ( - add_boolean_conflict st confl - ) else ( - add_clause_ st confl - ); - (match st.on_conflict with Some f -> f confl.atoms | None -> ()); - - | None -> (* No Conflict *) - assert (st.elt_head = Vec.size st.trail); - assert (st.elt_head = st.th_head); - if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( - Log.debug info "(sat.restarting)"; - cancel_until st 0; - raise_notrace Restart - ); - (* if decision_level() = 0 then simplify (); *) - - if n_of_learnts > 0 && - Vec.size st.clauses_learnt - Vec.size st.trail > n_of_learnts then ( - reduce_db st n_of_learnts; - ); - - pick_branch_lit st - done - - let eval_level (_st:t) (a:atom) = - let lvl = a.var.v_level in - if Atom.is_true a then ( - assert (lvl >= 0); - true, lvl - ) else if Atom.is_false a then ( - false, lvl - ) else ( - raise UndecidedLit - ) - - let[@inline] eval st lit = fst @@ eval_level st lit - - let[@inline] unsat_conflict st = st.unsat_at_0 - - let model (st:t) : (term * value) list = - let opt = function Some a -> a | None -> assert false in - Vec.fold - (fun acc e -> match e with - | Lit v -> (v.term, opt v.assigned) :: acc - | Atom _ -> acc) - [] st.trail - - (* fixpoint of propagation and decisions until a model is found, or a - conflict is reached *) - let solve_ (st:t) : unit = - Log.debugf 5 (fun k->k "(@[sat.solve :assms %d@])" (Vec.size st.assumptions)); - check_unsat_ st; - try - flush_clauses st; (* add initial clauses *) - let n_of_conflicts = ref (float_of_int restart_first) in - let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. learntsize_factor) in - while true do - begin try - search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) - with - | Restart -> - n_of_conflicts := !n_of_conflicts *. restart_inc; - n_of_learnts := !n_of_learnts *. learntsize_inc - | E_sat -> - assert (st.elt_head = Vec.size st.trail && - Vec.is_empty st.clauses_to_add && - st.next_decisions=[]); - begin match Plugin.final_check st.th (full_slice st) with - | () -> - if st.elt_head = Vec.size st.trail && - Vec.is_empty st.clauses_to_add && - st.next_decisions = [] - then ( - raise_notrace E_sat - ); - (* otherwise, keep on *) - flush_clauses st; - | exception Th_conflict c -> - check_is_conflict_ c; - Array.iter (fun a -> insert_elt_order st (Elt.of_var a.var)) c.atoms; - Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); - (match st.on_conflict with Some f -> f c.atoms | None -> ()); - Vec.push st.clauses_to_add c; - flush_clauses st; - end; - end - done - with E_sat -> () - - let assume st cnf lemma = - List.iter - (fun l -> - let atoms = List.rev_map (mk_atom st) l in - let c = Clause.make_permanent atoms (Hyp lemma) in - Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); - Vec.push st.clauses_to_add c) - cnf - - (* Check satisfiability *) - let check_clause c = - let res = Array.exists (fun a -> a.is_true) c.atoms in - if not res then ( - Log.debugf debug - (fun k -> k "(@[sat.check-clause@ :not-satisfied @[%a@]@])" Clause.debug c); - false - ) else - true - - let check_vec v = Vec.for_all check_clause v - let check st : bool = - Vec.is_empty st.clauses_to_add && - check_vec st.clauses_hyps && - check_vec st.clauses_learnt - - let[@inline] theory st = st.th - - (* Unsafe access to internal data *) - - let hyps env = env.clauses_hyps - - let history env = env.clauses_learnt - - let trail env = env.trail - - (* Result type *) - type res = - | Sat of (term,Formula.t,value) Solver_intf.sat_state - | Unsat of (atom,clause,Proof.t) Solver_intf.unsat_state - - let pp_all st lvl status = - Log.debugf lvl - (fun k -> k - "(@[sat.full-state :res %s - Full summary:@,@[Trail:@\n%a@]@,\ - @[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." - status - (Vec.pp ~sep:"" Trail_elt.debug) (trail st) - (Vec.pp ~sep:"" Clause.debug) (hyps st) - (Vec.pp ~sep:"" Clause.debug) (history st) - ) - - let mk_sat (st:t) : (Term.t, Formula.t, _) Solver_intf.sat_state = - pp_all st 99 "SAT"; - let t = trail st in - let iter_trail f f' = - Vec.iter (function - | Atom a -> f (Atom.formula a) - | Lit l -> f' l.term) - t - in - let[@inline] eval f = eval st (mk_atom st f) in - let[@inline] eval_level f = eval_level st (mk_atom st f) in - { Solver_intf. - eval; eval_level; iter_trail; - model = (fun () -> model st); - } - - let mk_unsat (st:t) (us: unsat_cause) : _ Solver_intf.unsat_state = - pp_all st 99 "UNSAT"; - let unsat_assumptions () = match us with - | US_local {first=_; core} -> core - | _ -> [] - in - let unsat_conflict = match us with - | US_false c -> fun() -> c - | US_local {core=[]; _} -> assert false - | US_local {first; core} -> - let c = lazy ( - let core = List.rev core in (* increasing trail order *) - assert (Atom.equal first @@ List.hd core); - let proof_of (a:atom) = match Atom.reason a with - | Some (Decision | Semantic) -> Clause.make_removable [a] Local - | Some (Bcp c | Bcp_lazy (lazy c)) -> c - | None -> assert false - in - let other_lits = List.filter (fun a -> not (Atom.equal a first)) core in - let hist = - Clause.make_permanent [first] Local :: - proof_of first :: - List.map proof_of other_lits in - Clause.make_permanent [] (History hist) - ) in - fun () -> Lazy.force c - in - let get_proof () = - let c = unsat_conflict () in - Proof.prove c - in - { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } - - let add_clause_a st c lemma : unit = - try - let c = Clause.make_a ~flags:0 c (Hyp lemma) in - add_clause_ st c - with - | E_unsat (US_false c) -> - st.unsat_at_0 <- Some c - - let add_clause st c lemma : unit = - try - let c = Clause.make_permanent c (Hyp lemma) in - add_clause_ st c - with - | E_unsat (US_false c) -> - st.unsat_at_0 <- Some c - - let solve ?(assumptions=[]) (st:t) : res = - cancel_until st 0; - Vec.clear st.assumptions; - List.iter (Vec.push st.assumptions) assumptions; - try - solve_ st; - Sat (mk_sat st) - with E_unsat us -> - Unsat (mk_unsat st us) - - let true_at_level0 st a = - try - let b, lev = eval_level st a in - b && lev = 0 - with UndecidedLit -> false - - let[@inline] eval_atom _st a : Solver_intf.lbool = eval_atom_ a - - let export (st:t) : clause Solver_intf.export = - let hyps = hyps st in - let history = history st in - {Solver_intf.hyps; history; } -end -[@@inline][@@specialise] - - module Void_ = struct - type t = Solver_intf.void - let equal _ _ = assert false - let hash _ = assert false - let pp _ _ = assert false - end - -module Make_cdcl_t(Plugin : Solver_intf.PLUGIN_CDCL_T) = - Make(struct - include Plugin - module Term = Void_ - module Value = Void_ - let eval _ _ = Solver_intf.Unknown - let assign _ t = t - let mcsat = false - let has_theory = true - let iter_assignable _ _ _ = () - end) -[@@inline][@@specialise] - -module Make_mcsat(Plugin : Solver_intf.PLUGIN_MCSAT) = - Make(struct - include Plugin - let mcsat = true - let has_theory = false - end) -[@@inline][@@specialise] - -module Make_pure_sat(Plugin : Solver_intf.PLUGIN_SAT) = - Make(struct - module Formula = Plugin.Formula - module Term = Void_ - module Value = Void_ - type t = unit - type proof = Plugin.proof - let push_level () = () - let pop_levels _ _ = () - let partial_check () _ = () - let final_check () _ = () - let eval () _ = Solver_intf.Unknown - let assign () t = t - let mcsat = false - let has_theory = false - let iter_assignable () _ _ = () - let mcsat = false -end) -[@@inline][@@specialise] - diff --git a/src/sat/Sidekick_sat.ml b/src/sat/Sidekick_sat.ml index fb049615..a618d8a7 100644 --- a/src/sat/Sidekick_sat.ml +++ b/src/sat/Sidekick_sat.ml @@ -5,21 +5,15 @@ module Solver_intf = Solver_intf module type S = Solver_intf.S module type FORMULA = Solver_intf.FORMULA -module type EXPR = Solver_intf.EXPR module type PLUGIN_CDCL_T = Solver_intf.PLUGIN_CDCL_T -module type PLUGIN_MCSAT = Solver_intf.PLUGIN_MCSAT module type PROOF = Solver_intf.PROOF -(** Empty type *) -type void = (unit,bool) Solver_intf.gadt_eq - type lbool = Solver_intf.lbool = L_true | L_false | L_undefined -type ('term, 'form, 'value) sat_state = ('term, 'form, 'value) Solver_intf.sat_state = { +type 'form sat_state = 'form Solver_intf.sat_state = { eval : 'form -> bool; eval_level : 'form -> bool * int; - iter_trail : ('form -> unit) -> ('term -> unit) -> unit; - model : unit -> ('term * 'value) list; + iter_trail : ('form -> unit) -> unit; } type ('atom,'clause, 'proof) unsat_state = ('atom,'clause, 'proof) Solver_intf.unsat_state = { @@ -32,22 +26,16 @@ type 'clause export = 'clause Solver_intf.export = { history : 'clause Vec.t; } -type ('term, 'formula, 'value) assumption = ('term, 'formula, 'value) Solver_intf.assumption = - | Lit of 'formula (** The given formula is asserted true by the solver *) - | Assign of 'term * 'value (** The term is assigned to the value *) - -type ('term, 'formula, 'proof) reason = ('term, 'formula, 'proof) Solver_intf.reason = - | Eval of 'term list +type ('formula, 'proof) reason = ('formula, 'proof) Solver_intf.reason = | Consequence of (unit -> 'formula list * 'proof) -type ('term, 'formula, 'value, 'proof) acts = ('term, 'formula, 'value, 'proof) Solver_intf.acts = { - acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; +type ('formula, 'proof) acts = ('formula, 'proof) Solver_intf.acts = { + acts_iter_assumptions: ('formula -> unit) -> unit; acts_eval_lit: 'formula -> lbool; acts_mk_lit: ?default_pol:bool -> 'formula -> unit; - acts_mk_term: 'term -> unit; acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; - acts_propagate : 'formula -> ('term, 'formula, 'proof) reason -> unit; + acts_propagate : 'formula -> ('formula, 'proof) reason -> unit; acts_add_decision_lit: 'formula -> bool -> unit; } @@ -66,11 +54,4 @@ let pp_lbool out = function exception No_proof = Solver_intf.No_proof -module Make_mcsat = Solver.Make_mcsat module Make_cdcl_t = Solver.Make_cdcl_t -module Make_pure_sat = Solver.Make_pure_sat - -(**/**) -module Vec = Vec -module Log = Log -(**/**) diff --git a/src/sat/Solver.ml b/src/sat/Solver.ml index 5863de2a..bb827f8f 100644 --- a/src/sat/Solver.ml +++ b/src/sat/Solver.ml @@ -1,12 +1,1928 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2016 Guillaume Bury -Copyright 2016 Simon Cruanes -*) + +module type PLUGIN = sig + val has_theory : bool + (** Is this a CDCL(T) plugin or mcsat plugin? + i.e does it have theories *) + + include Solver_intf.PLUGIN_CDCL_T +end module type S = Solver_intf.S +module type PLUGIN_CDCL_T = Solver_intf.PLUGIN_CDCL_T -module Make_cdcl_t = Internal.Make_cdcl_t -module Make_mcsat = Internal.Make_mcsat -module Make_pure_sat = Internal.Make_pure_sat +let invalid_argf fmt = + Format.kasprintf (fun msg -> invalid_arg ("msat: " ^ msg)) fmt + +module Make(Plugin : PLUGIN) += struct + module Formula = Plugin.Formula + + type formula = Formula.t + type theory = Plugin.t + type lemma = Plugin.proof + + type var = { + vid : int; + pa : atom; + na : atom; + mutable v_fields : int; + mutable v_level : int; + mutable v_idx: int; (** position in heap *) + mutable v_weight : float; (** Weight (for the heap), tracking activity *) + mutable reason : reason option; + } + + and atom = { + aid : int; + var : var; + neg : atom; + lit : formula; + mutable is_true : bool; + watched : clause Vec.t; + } + + and clause = { + cid: int; + atoms : atom array; + mutable cpremise : premise; + mutable activity : float; + mutable flags: int; (* bitfield *) + } + + and reason = + | Decision + | Bcp of clause + | Bcp_lazy of clause lazy_t + + (* TODO: remove, replace with user-provided proof trackng device? + for pure SAT, [reason] is sufficient *) + and premise = + | Hyp of lemma + | Local + | Lemma of lemma + | History of clause list + | Empty_premise + + (* Constructors *) + module MF = Hashtbl.Make(Formula) + + type st = { + f_map: var MF.t; + vars: var Vec.t; + mutable cpt_mk_var: int; + mutable cpt_mk_clause: int; + } + + let create_st ?(size=`Big) () : st = + let size_map = match size with + | `Tiny -> 8 + | `Small -> 16 + | `Big -> 4096 + in + { f_map = MF.create size_map; + vars = Vec.create(); + cpt_mk_var = 0; + cpt_mk_clause = 0; + } + + let nb_elt st = Vec.size st.vars + let get_elt st i = Vec.get st.vars i + let iter_elt st f = Vec.iter f st.vars + + let name_of_clause c = match c.cpremise with + | Hyp _ -> "H" ^ string_of_int c.cid + | Lemma _ -> "T" ^ string_of_int c.cid + | Local -> "L" ^ string_of_int c.cid + | History _ -> "C" ^ string_of_int c.cid + | Empty_premise -> string_of_int c.cid + + (* some boolean flags for variables, used as masks *) + let seen_var = 0b1 + let seen_pos = 0b10 + let seen_neg = 0b100 + let default_pol_true = 0b1000 + + module Var = struct + let[@inline] level v = v.v_level + let[@inline] pos v = v.pa + let[@inline] neg v = v.na + let[@inline] reason v = v.reason + let[@inline] weight v = v.v_weight + let[@inline] set_weight v w = v.v_weight <- w + let[@inline] mark v = v.v_fields <- v.v_fields lor seen_var + let[@inline] unmark v = v.v_fields <- v.v_fields land (lnot seen_var) + let[@inline] marked v = (v.v_fields land seen_var) <> 0 + let[@inline] set_default_pol_true v = v.v_fields <- v.v_fields lor default_pol_true + let[@inline] set_default_pol_false v = v.v_fields <- v.v_fields land (lnot default_pol_true) + let[@inline] default_pol v = (v.v_fields land default_pol_true) <> 0 + let[@inline] idx v = v.v_idx + let[@inline] set_idx v i = v.v_idx <- i + + let make ?(default_pol=true) (st:st) (t:formula) : var * Solver_intf.negated = + let lit, negated = Formula.norm t in + try + MF.find st.f_map lit, negated + with Not_found -> + let cpt_double = st.cpt_mk_var lsl 1 in + let rec var = + { vid = st.cpt_mk_var; + pa = pa; + na = na; + v_fields = 0; + v_level = -1; + v_idx= -1; + v_weight = 0.; + reason = None; + } + and pa = + { var = var; + lit = lit; + watched = Vec.create(); + neg = na; + is_true = false; + aid = cpt_double (* aid = vid*2 *) } + and na = + { var = var; + lit = Formula.neg lit; + watched = Vec.create(); + neg = pa; + is_true = false; + aid = cpt_double + 1 (* aid = vid*2+1 *) } in + MF.add st.f_map lit var; + st.cpt_mk_var <- st.cpt_mk_var + 1; + if default_pol then set_default_pol_true var; + Vec.push st.vars var; + var, negated + + (* Marking helpers *) + let[@inline] clear v = + v.v_fields <- 0 + + let[@inline] seen_both v = + (seen_pos land v.v_fields <> 0) && + (seen_neg land v.v_fields <> 0) + end + + module Atom = struct + type t = atom + let[@inline] level a = a.var.v_level + let[@inline] var a = a.var + let[@inline] neg a = a.neg + let[@inline] abs a = a.var.pa + let[@inline] formula a = a.lit + let[@inline] equal a b = a == b + let[@inline] sign a = a == abs a + let[@inline] hash a = Hashtbl.hash a.aid + let[@inline] compare a b = compare a.aid b.aid + let[@inline] reason a = Var.reason a.var + let[@inline] id a = a.aid + let[@inline] is_true a = a.is_true + let[@inline] is_false a = a.neg.is_true + let has_value a = is_true a || is_false a + + let[@inline] seen a = + if sign a + then (seen_pos land a.var.v_fields <> 0) + else (seen_neg land a.var.v_fields <> 0) + + let[@inline] mark a = + let pos = equal a (abs a) in + if pos then ( + a.var.v_fields <- seen_pos lor a.var.v_fields + ) else ( + a.var.v_fields <- seen_neg lor a.var.v_fields + ) + + let[@inline] make ?default_pol st lit = + let var, negated = Var.make ?default_pol st lit in + match negated with + | Solver_intf.Negated -> var.na + | Solver_intf.Same_sign -> var.pa + + let pp fmt a = Formula.pp fmt a.lit + + let pp_a fmt v = + if Array.length v = 0 then ( + Format.fprintf fmt "∅" + ) else ( + pp fmt v.(0); + if (Array.length v) > 1 then begin + for i = 1 to (Array.length v) - 1 do + Format.fprintf fmt " ∨ %a" pp v.(i) + done + end + ) + + (* Complete debug printing *) + let pp_sign a = if a == a.var.pa then "+" else "-" + + let debug_reason fmt = function + | n, _ when n < 0 -> + Format.fprintf fmt "%%" + | n, None -> + Format.fprintf fmt "%d" n + | n, Some Decision -> + Format.fprintf fmt "@@%d" n + | n, Some Bcp c -> + Format.fprintf fmt "->%d/%s" n (name_of_clause c) + | n, Some (Bcp_lazy _) -> + Format.fprintf fmt "->%d/" n + + let pp_level fmt a = + debug_reason fmt (a.var.v_level, a.var.reason) + + let debug_value fmt a = + if a.is_true then + Format.fprintf fmt "T%a" pp_level a + else if a.neg.is_true then + Format.fprintf fmt "F%a" pp_level a + else + Format.fprintf fmt "" + + let debug out a = + Format.fprintf out "%s%d[%a][atom:@[%a@]]" + (pp_sign a) (a.var.vid+1) debug_value a Formula.pp a.lit + + let debug_a out vec = + Array.iter (fun a -> Format.fprintf out "%a@ " debug a) vec + let debug_l out l = + List.iter (fun a -> Format.fprintf out "%a@ " debug a) l + + module Set = Set.Make(struct type t=atom let compare=compare end) + end + + module Clause = struct + type t = clause + + let make_a = + let n = ref 0 in + fun ~flags atoms premise -> + let cid = !n in + incr n; + { cid; + atoms = atoms; + flags; + activity = 0.; + cpremise = premise} + + let make ~flags l premise = make_a ~flags (Array.of_list l) premise + + let empty = make [] (History []) + let name = name_of_clause + let[@inline] equal c1 c2 = c1.cid = c2.cid + let[@inline] hash c = Hashtbl.hash c.cid + let[@inline] atoms c = c.atoms + let[@inline] atoms_seq c = Iter.of_array c.atoms + let[@inline] atoms_l c = Array.to_list c.atoms + + let flag_attached = 0b1 + let flag_visited = 0b10 + let flag_removable = 0b100 + let flag_dead = 0b1000 + + let[@inline] make_removable l premise = make ~flags:flag_removable l premise + let[@inline] make_removable_a l premise = make_a ~flags:flag_removable l premise + let[@inline] make_permanent l premise = make ~flags:0 l premise + + let[@inline] visited c = (c.flags land flag_visited) <> 0 + let[@inline] set_visited c b = + if b then c.flags <- c.flags lor flag_visited + else c.flags <- c.flags land lnot flag_visited + + let[@inline] attached c = (c.flags land flag_attached) <> 0 + let[@inline] set_attached c b = + if b then c.flags <- c.flags lor flag_attached + else c.flags <- c.flags land lnot flag_attached + + let[@inline] removable c = (c.flags land flag_removable) <> 0 + let[@inline] set_removable c b = + if b then c.flags <- c.flags lor flag_removable + else c.flags <- c.flags land lnot flag_removable + + let[@inline] dead c = (c.flags land flag_dead) <> 0 + let[@inline] set_dead c = c.flags <- c.flags lor flag_dead + + let[@inline] activity c = c.activity + let[@inline] set_activity c w = c.activity <- w + + module Tbl = Hashtbl.Make(struct + type t = clause + let hash = hash + let equal = equal + end) + + let pp fmt c = + Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms + + let debug_premise out = function + | Hyp _ -> Format.fprintf out "hyp" + | Lemma _ -> Format.fprintf out "th_lemma" + | Local -> Format.fprintf out "local" + | History v -> + List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v + | Empty_premise -> Format.fprintf out "" + + let debug out ({atoms=arr; cpremise=cp;_}as c) = + Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" + (name c) Atom.debug_a arr debug_premise cp + end + + module Proof = struct + exception Resolution_error of string + + type atom = Atom.t + type clause = Clause.t + type formula = Formula.t + type lemma = Plugin.proof + + let error_res_f msg = Format.kasprintf (fun s -> raise (Resolution_error s)) msg + + let[@inline] clear_var_of_ (a:atom) = Var.clear a.var + + (* Compute resolution of 2 clauses. + returns [pivots, resulting_atoms] *) + let resolve (c1:clause) (c2:clause) : atom list * atom list = + (* invariants: only atoms in [c2] are marked, and the pivot is + cleared when traversing [c1] *) + Array.iter Atom.mark c2.atoms; + let pivots = ref [] in + let l = + Array.fold_left + (fun l a -> + if Atom.seen a then l + else if Atom.seen a.neg then ( + pivots := a.var.pa :: !pivots; + clear_var_of_ a; + l + ) else a::l) + [] c1.atoms + in + let l = + Array.fold_left (fun l a -> if Atom.seen a then a::l else l) l c2.atoms + in + Array.iter clear_var_of_ c2.atoms; + !pivots, l + + (* [find_dups c] returns a list of duplicate atoms, and the deduplicated list *) + let find_dups (c:clause) : atom list * atom list = + let res = + Array.fold_left + (fun (dups,l) a -> + if Atom.seen a then ( + a::dups, l + ) else ( + Atom.mark a; + dups, a::l + )) + ([], []) c.atoms + in + Array.iter clear_var_of_ c.atoms; + res + + (* do [c1] and [c2] have the same lits, modulo reordering and duplicates? *) + let same_lits (c1:atom Iter.t) (c2:atom Iter.t): bool = + let subset a b = + Iter.iter Atom.mark b; + let res = Iter.for_all Atom.seen a in + Iter.iter clear_var_of_ b; + res + in + subset c1 c2 && subset c2 c1 + + let prove conclusion = + match conclusion.cpremise with + | History [] -> assert false + | Empty_premise -> raise Solver_intf.No_proof + | _ -> conclusion + + let rec set_atom_proof a = + let aux acc b = + if Atom.equal a.neg b then acc + else set_atom_proof b :: acc + in + assert (a.var.v_level >= 0); + match (a.var.reason) with + | Some (Bcp c | Bcp_lazy (lazy c)) -> + Log.debugf 5 (fun k->k "(@[proof.analyze.clause@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c); + if Array.length c.atoms = 1 then ( + Log.debugf 5 (fun k -> k "(@[proof.analyze.old-reason@ %a@])" Atom.debug a); + c + ) else ( + assert (a.neg.is_true); + let r = History (c :: (Array.fold_left aux [] c.atoms)) in + let c' = Clause.make_permanent [a.neg] r in + a.var.reason <- Some (Bcp c'); + Log.debugf 5 + (fun k -> k "(@[proof.analyze.new-reason@ :atom %a@ :c %a@])" Atom.debug a Clause.debug c'); + c' + ) + | _ -> + error_res_f "cannot prove atom %a" Atom.debug a + + let prove_unsat conflict = + if Array.length conflict.atoms = 0 then ( + conflict + ) else ( + Log.debugf 1 (fun k -> k "(@[sat.prove-unsat@ :from %a@])" Clause.debug conflict); + let l = Array.fold_left (fun acc a -> set_atom_proof a :: acc) [] conflict.atoms in + let res = Clause.make_permanent [] (History (conflict :: l)) in + Log.debugf 1 (fun k -> k "(@[sat.proof-found@ %a@])" Clause.debug res); + res + ) + + let prove_atom a = + if a.is_true && a.var.v_level = 0 then + Some (set_atom_proof a) + else + None + + type t = clause + and proof_node = { + conclusion : clause; + step : step; + } + and step = + | Hypothesis of lemma + | Assumption + | Lemma of lemma + | Duplicate of t * atom list + | Hyper_res of hyper_res_step + + and hyper_res_step = { + hr_init: t; + hr_steps: (atom * t) list; (* list of pivot+clause to resolve against [init] *) + } + + let[@inline] conclusion (p:t) : clause = p + + (* find pivots for resolving [l] with [init], and also return + the atoms of the conclusion *) + let find_pivots (init:clause) (l:clause list) : _ * (atom * t) list = + Log.debugf 15 + (fun k->k "(@[proof.find-pivots@ :init %a@ :l %a@])" + Clause.debug init (Format.pp_print_list Clause.debug) l); + Array.iter Atom.mark init.atoms; + let steps = + List.map + (fun c -> + let pivot = + match + Iter.of_array c.atoms + |> Iter.filter (fun a -> Atom.seen (Atom.neg a)) + |> Iter.to_list + with + | [a] -> a + | [] -> + error_res_f "(@[proof.expand.pivot_missing@ %a@])" Clause.debug c + | pivots -> + error_res_f "(@[proof.expand.multiple_pivots@ %a@ :pivots %a@])" + Clause.debug c Atom.debug_l pivots + in + Array.iter Atom.mark c.atoms; (* add atoms to result *) + clear_var_of_ pivot; + Atom.abs pivot, c) + l + in + (* cleanup *) + let res = ref [] in + let cleanup_a_ a = + if Atom.seen a then ( + res := a :: !res; + clear_var_of_ a + ) + in + Array.iter cleanup_a_ init.atoms; + List.iter (fun c -> Array.iter cleanup_a_ c.atoms) l; + !res, steps + + let expand conclusion = + Log.debugf 5 (fun k -> k "(@[sat.proof.expand@ @[%a@]@])" Clause.debug conclusion); + match conclusion.cpremise with + | Lemma l -> + { conclusion; step = Lemma l; } + | Local -> + { conclusion; step = Assumption; } + | Hyp l -> + { conclusion; step = Hypothesis l; } + | History [] -> + error_res_f "@[empty history for clause@ %a@]" Clause.debug conclusion + | History [c] -> + let duplicates, res = find_dups c in + assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); + { conclusion; step = Duplicate (c, duplicates) } + | History (c :: r) -> + let res, steps = find_pivots c r in + assert (same_lits (Iter.of_list res) (Clause.atoms_seq conclusion)); + { conclusion; step = Hyper_res {hr_init=c; hr_steps=steps}; } + | Empty_premise -> raise Solver_intf.No_proof + + let rec res_of_hyper_res (hr: hyper_res_step) : _ * _ * atom = + let {hr_init=c1; hr_steps=l} = hr in + match l with + | [] -> assert false + | [a, c2] -> c1, c2, a (* done *) + | (a,c2) :: steps' -> + (* resolve [c1] with [c2], then resolve that against [steps] *) + let pivots, l = resolve c1 c2 in + assert (match pivots with [a'] -> Atom.equal a a' | _ -> false); + let c_1_2 = Clause.make_removable l (History [c1; c2]) in + res_of_hyper_res {hr_init=c_1_2; hr_steps=steps'} + + (* Proof nodes manipulation *) + let is_leaf = function + | Hypothesis _ + | Assumption + | Lemma _ -> true + | Duplicate _ + | Hyper_res _ -> false + + let parents = function + | Hypothesis _ + | Assumption + | Lemma _ -> [] + | Duplicate (p, _) -> [p] + | Hyper_res {hr_init; hr_steps} -> hr_init :: List.map snd hr_steps + + let expl = function + | Hypothesis _ -> "hypothesis" + | Assumption -> "assumption" + | Lemma _ -> "lemma" + | Duplicate _ -> "duplicate" + | Hyper_res _ -> "hyper-resolution" + + (* Compute unsat-core by accumulating the leaves *) + let unsat_core proof = + let rec aux res acc = function + | [] -> res, acc + | c :: r -> + if not @@ Clause.visited c then ( + Clause.set_visited c true; + match c.cpremise with + | Empty_premise -> raise Solver_intf.No_proof + | Hyp _ | Lemma _ | Local -> aux (c :: res) acc r + | History h -> + let l = List.fold_left (fun acc c -> + if not @@ Clause.visited c then c :: acc else acc) r h in + aux res (c :: acc) l + ) else ( + aux res acc r + ) + in + let res, tmp = aux [] [] [proof] in + List.iter (fun c -> Clause.set_visited c false) res; + List.iter (fun c -> Clause.set_visited c false) tmp; + res + + module Tbl = Clause.Tbl + + type task = + | Enter of t + | Leaving of t + + let spop s = try Some (Stack.pop s) with Stack.Empty -> None + + let rec fold_aux s h f acc = + match spop s with + | None -> acc + | Some (Leaving c) -> + Tbl.add h c true; + fold_aux s h f (f acc (expand c)) + | Some (Enter c) -> + if not (Tbl.mem h c) then begin + Stack.push (Leaving c) s; + let node = expand c in + begin match node.step with + | Duplicate (p1, _) -> + Stack.push (Enter p1) s + | Hyper_res {hr_init=p1; hr_steps=l} -> + List.iter (fun (_,p2) -> Stack.push (Enter p2) s) l; + Stack.push (Enter p1) s; + | Hypothesis _ | Assumption | Lemma _ -> () + end + end; + fold_aux s h f acc + + let fold f acc p = + let h = Tbl.create 42 in + let s = Stack.create () in + Stack.push (Enter p) s; + fold_aux s h f acc + + let check_empty_conclusion (p:t) = + if Array.length p.atoms > 0 then ( + error_res_f "@[<2>Proof.check: non empty conclusion for clause@ %a@]" Clause.debug p; + ) + + let check (p:t) = fold (fun () _ -> ()) () p + end + + module H = (Heap.Make [@specialise]) (struct + type t = var + let[@inline] cmp i j = Var.weight j < Var.weight i (* comparison by weight *) + let idx = Var.idx + let set_idx = Var.set_idx + end) + + (* cause of "unsat", possibly conditional to local assumptions *) + type unsat_cause = + | US_local of { + first: atom; (* assumption which was found to be proved false *) + core: atom list; (* the set of assumptions *) + } + | US_false of clause (* true unsat *) + + exception E_sat + exception E_unsat of unsat_cause + exception UndecidedLit + exception Restart + exception Conflict of clause + + (* Log levels *) + let error = 1 + let warn = 3 + let info = 5 + let debug = 50 + + let var_decay : float = 1. /. 0.95 + (* inverse of the activity factor for variables. Default 1/0.95 *) + + let clause_decay : float = 1. /. 0.999 + (* inverse of the activity factor for clauses. Default 1/0.999 *) + + let restart_inc : float = 1.5 + (* multiplicative factor for restart limit, default 1.5 *) + + let learntsize_inc : float = 1.1 + (* multiplicative factor for [learntsize_factor] at each restart, default 1.1 *) + + (* Singleton type containing the current state *) + type t = { + st : st; + th: theory; + + store_proof: bool; (* do we store proofs? *) + + (* Clauses are simplified for eficiency purposes. In the following + vectors, the comments actually refer to the original non-simplified + clause. *) + + clauses_hyps : clause Vec.t; + (* clauses added by the user *) + clauses_learnt : clause Vec.t; + (* learnt clauses (tautologies true at any time, whatever the user level) *) + + clauses_to_add : clause Vec.t; + (* Clauses either assumed or pushed by the theory, waiting to be added. *) + + mutable unsat_at_0: clause option; + (* conflict at level 0, if any *) + + mutable next_decisions : atom list; + (* When the last conflict was a semantic one (mcsat), + this stores the next decision to make; + if some theory wants atoms to be decided on (for theory combination), + store them here. *) + + trail : atom Vec.t; + (* decision stack + propagated elements (atoms or assignments). *) + + var_levels : int Vec.t; + (* decision levels in [trail] *) + + mutable assumptions: atom Vec.t; + (* current assumptions *) + + mutable th_head : int; + (* Start offset in the queue {!trail} of + unit facts not yet seen by the theory. *) + mutable elt_head : int; + (* Start offset in the queue {!trail} of + unit facts to propagate, within the trail *) + + (* invariant: + - during propagation, th_head <= elt_head + - then, once elt_head reaches length trail, Th.assume is + called so that th_head can catch up with elt_head + - this is repeated until a fixpoint is reached; + - before a decision (and after the fixpoint), + th_head = elt_head = length trail + *) + + order : H.t; + (* Heap ordered by variable activity *) + + to_clear: var Vec.t; + (* variables to unmark *) + + mutable var_incr : float; + (* increment for variables' activity *) + + mutable clause_incr : float; + (* increment for clauses' activity *) + + mutable on_conflict : (atom array -> unit) option; + mutable on_decision : (atom -> unit) option; + mutable on_new_atom: (atom -> unit) option; + } + type solver = t + + (* intial restart limit *) + let restart_first = 100 + + (* initial limit for the number of learnt clauses, 1/3 of initial + number of clauses by default *) + let learntsize_factor = 1. /. 3. + + let _nop_on_conflict (_:atom array) = () + + (* Starting environment. *) + let create_ ~st ~store_proof (th:theory) : t = { + st; th; + unsat_at_0=None; + next_decisions = []; + + clauses_hyps = Vec.create(); + clauses_learnt = Vec.create(); + + clauses_to_add = Vec.create (); + to_clear=Vec.create(); + + th_head = 0; + elt_head = 0; + + trail = Vec.create (); + var_levels = Vec.create(); + assumptions= Vec.create(); + + order = H.create(); + + var_incr = 1.; + clause_incr = 1.; + store_proof; + on_conflict = None; + on_decision= None; + on_new_atom = None; + } + + let create + ?on_conflict ?on_decision ?on_new_atom + ?(store_proof=true) ?(size=`Big) (th:theory) : t = + let st = create_st ~size () in + let st = create_ ~st ~store_proof th in + st.on_new_atom <- on_new_atom; + st.on_decision <- on_decision; + st.on_conflict <- on_conflict; + st + + let[@inline] st t = t.st + let[@inline] nb_clauses st = Vec.size st.clauses_hyps + let[@inline] decision_level st = Vec.size st.var_levels + + (* Do we have a level-0 empty clause? *) + let[@inline] check_unsat_ st = + match st.unsat_at_0 with + | Some c -> raise (E_unsat (US_false c)) + | None -> () + + let mk_atom ?default_pol st (f:formula) : atom = + let res = Atom.make ?default_pol st.st f in + res + + (* Variable and literal activity. + Activity is used to decide on which variable to decide when propagation + is done. Uses a heap (implemented in Iheap), to keep track of variable activity. + To be more general, the heap only stores the variable/literal id (i.e an int). + *) + let insert_var_order st (v:var) : unit = + H.insert st.order v + + let make_atom st (p:formula) : atom = + let a = mk_atom st p in + if a.var.v_level < 0 then ( + insert_var_order st a.var; + (match st.on_new_atom with Some f -> f a | None -> ()); + ) else ( + assert (a.is_true || a.neg.is_true); + ); + a + + (* Rather than iterate over all the heap when we want to decrease all the + variables/literals activity, we instead increase the value by which + we increase the activity of 'interesting' var/lits. *) + let[@inline] var_decay_activity st = + st.var_incr <- st.var_incr *. var_decay + + let[@inline] clause_decay_activity st = + st.clause_incr <- st.clause_incr *. clause_decay + + (* increase activity of [v] *) + let var_bump_activity st v = + v.v_weight <- v.v_weight +. st.var_incr; + if v.v_weight > 1e100 then ( + for i = 0 to nb_elt st.st - 1 do + Var.set_weight (get_elt st.st i) ((Var.weight (get_elt st.st i)) *. 1e-100) + done; + st.var_incr <- st.var_incr *. 1e-100; + ); + if H.in_heap v then ( + H.decrease st.order v + ) + + (* increase activity of clause [c] *) + let clause_bump_activity st (c:clause) : unit = + c.activity <- c.activity +. st.clause_incr; + if c.activity > 1e20 then ( + Vec.iter (fun c -> c.activity <- c.activity *. 1e-20) st.clauses_learnt; + st.clause_incr <- st.clause_incr *. 1e-20 + ) + + (* Simplification of clauses. + + When adding new clauses, it is desirable to 'simplify' them, i.e + minimize the amount of literals in it, because it greatly reduces + the search space for new watched literals during propagation. + Additionally, we have to partition the lits, to ensure the watched + literals (which are the first two lits of the clause) are appropriate. + Indeed, it is better to watch true literals, and then unassigned literals. + Watching false literals should be a last resort, and come with constraints + (see {!add_clause}). + *) + exception Trivial + + (* [arr_to_list a i] converts [a.(i), ... a.(length a-1)] into a list *) + let arr_to_list arr i : _ list = + if i >= Array.length arr then [] + else Array.to_list (Array.sub arr i (Array.length arr - i)) + + (* Eliminates atom duplicates in clauses *) + let eliminate_duplicates clause : clause = + let trivial = ref false in + let duplicates = ref [] in + let res = ref [] in + Array.iter (fun a -> + if Atom.seen a then duplicates := a :: !duplicates + else ( + Atom.mark a; + res := a :: !res + )) + clause.atoms; + List.iter + (fun a -> + if Var.seen_both a.var then trivial := true; + Var.clear a.var) + !res; + if !trivial then ( + raise Trivial + ) else if !duplicates = [] then ( + clause + ) else ( + Clause.make ~flags:clause.flags !res (History [clause]) + ) + + (* Partition literals for new clauses, into: + - true literals (maybe makes the clause trivial if the lit is proved true at level 0) + - unassigned literals, yet to be decided + - false literals (not suitable to watch, those at level 0 can be removed from the clause) + + Clauses that propagated false lits are remembered to reconstruct resolution proofs. + *) + let partition atoms : atom list * clause list = + let rec partition_aux trues unassigned falses history i = + if i >= Array.length atoms then ( + trues @ unassigned @ falses, history + ) else ( + let a = atoms.(i) in + if a.is_true then ( + let l = a.var.v_level in + if l = 0 then + raise Trivial (* A var true at level 0 gives a trivially true clause *) + else + (a :: trues) @ unassigned @ falses @ + (arr_to_list atoms (i + 1)), history + ) else if a.neg.is_true then ( + let l = a.var.v_level in + if l = 0 then ( + match a.var.reason with + | Some (Bcp cl | Bcp_lazy (lazy cl)) -> + partition_aux trues unassigned falses (cl :: history) (i + 1) + (* A var false at level 0 can be eliminated from the clause, + but we need to kepp in mind that we used another clause to simplify it. *) + (* TODO: get a proof of the propagation. *) + | None | Some Decision -> assert false + (* The var must have a reason, and it cannot be a decision/assumption, + since its level is 0. *) + ) else ( + partition_aux trues unassigned (a::falses) history (i + 1) + ) + ) else ( + partition_aux trues (a::unassigned) falses history (i + 1) + ) + ) + in + partition_aux [] [] [] [] 0 + + + (* Making a decision. + Before actually creatig a new decision level, we check that + all propagations have been done and propagated to the theory, + i.e that the theoriy state indeed takes into account the whole + stack of literals + i.e we have indeed reached a propagation fixpoint before making + a new decision *) + let new_decision_level st = + assert (st.th_head = Vec.size st.trail); + assert (st.elt_head = Vec.size st.trail); + Vec.push st.var_levels (Vec.size st.trail); + Plugin.push_level st.th; + () + + (* Attach/Detach a clause. + + A clause is attached (to its watching lits) when it is first added, + either because it is assumed or learnt. + + *) + let attach_clause c = + assert (not @@ Clause.attached c); + Log.debugf debug (fun k -> k "(@[sat.attach-clause@ %a@])" Clause.debug c); + Vec.push c.atoms.(0).neg.watched c; + Vec.push c.atoms.(1).neg.watched c; + Clause.set_attached c true; + () + + (* Backtracking. + Used to backtrack, i.e cancel down to [lvl] excluded, + i.e we want to go back to the state the solver was in + when decision level [lvl] was created. *) + let cancel_until st lvl = + assert (lvl >= 0); + (* Nothing to do if we try to backtrack to a non-existent level. *) + if decision_level st <= lvl then ( + Log.debugf debug (fun k -> k "(@[sat.cancel-until.nop@ :already-at-level <= %d@])" lvl) + ) else ( + Log.debugf info (fun k -> k "(@[sat.cancel-until %d@])" lvl); + (* We set the head of the solver and theory queue to what it was. *) + let head = ref (Vec.get st.var_levels lvl) in + st.elt_head <- !head; + st.th_head <- !head; + (* Now we need to cleanup the vars that are not valid anymore + (i.e to the right of elt_head in the queue. *) + for c = st.elt_head to Vec.size st.trail - 1 do + let a = Vec.get st.trail c in + (* A literal is unassigned, we nedd to add it back to + the heap of potentially assignable literals, unless it has + a level lower than [lvl], in which case we just move it back. *) + (* A variable is not true/false anymore, one of two things can happen: *) + if a.var.v_level <= lvl then ( + (* It is a late propagation, which has a level + lower than where we backtrack, so we just move it to the head + of the queue, to be propagated again. *) + Vec.set st.trail !head a; + head := !head + 1 + ) else ( + (* it is a result of bolean propagation, or a semantic propagation + with a level higher than the level to which we backtrack, + in that case, we simply unset its value and reinsert it into the heap. *) + a.is_true <- false; + a.neg.is_true <- false; + a.var.v_level <- -1; + a.var.reason <- None; + insert_var_order st a.var + ) + done; + (* Recover the right theory state. *) + let n = decision_level st - lvl in + assert (n>0); + (* Resize the vectors according to their new size. *) + Vec.shrink st.trail !head; + Vec.shrink st.var_levels lvl; + Plugin.pop_levels st.th n; + st.next_decisions <- []; + ); + () + + let pp_unsat_cause out = function + | US_local {first=_; core} -> + Format.fprintf out "(@[unsat-cause@ :false-assumptions %a@])" + (Format.pp_print_list Atom.pp) core + | US_false c -> + Format.fprintf out "(@[unsat-cause@ :false %a@])" Clause.debug c + + (* Unsatisfiability is signaled through an exception, since it can happen + in multiple places (adding new clauses, or solving for instance). *) + let report_unsat st (us:unsat_cause) : _ = + Log.debugf info (fun k -> k "(@[sat.unsat-conflict@ %a@])" pp_unsat_cause us); + let us = match us with + | US_false c -> + let c = if st.store_proof then Proof.prove_unsat c else c in + st.unsat_at_0 <- Some c; + US_false c + | _ -> us + in + raise (E_unsat us) + + (* Simplification of boolean propagation reasons. + When doing boolean propagation *at level 0*, it can happen + that the clause cl, which propagates a formula, also contains + other formulas, but has been simplified. in which case, we + need to rebuild a clause with correct history, in order to + be able to build a correct proof at the end of proof search. *) + let simpl_reason : reason -> reason = function + | (Bcp cl | Bcp_lazy (lazy cl)) as r -> + let l, history = partition cl.atoms in + begin match l with + | [_] -> + if history = [] then ( + (* no simplification has been done, so [cl] is actually a clause with only + [a], so it is a valid reason for propagating [a]. *) + r + ) else ( + (* Clauses in [history] have been used to simplify [cl] into a clause [tmp_cl] + with only one formula (which is [a]). So we explicitly create that clause + and set it as the cause for the propagation of [a], that way we can + rebuild the whole resolution tree when we want to prove [a]. *) + let c' = Clause.make ~flags:cl.flags l (History (cl :: history)) in + Log.debugf debug + (fun k -> k "(@[sat.simplified-reason@ %a@ %a@])" Clause.debug cl Clause.debug c'); + Bcp c' + ) + | _ -> + Log.debugf error + (fun k -> + k "(@[sat.simplify-reason.failed@ :at %a@ %a@]" + (Vec.pp ~sep:"" Atom.debug) (Vec.of_list l) + Clause.debug cl); + assert false + end + | Decision as r -> r + + (* Boolean propagation. + Wrapper function for adding a new propagated formula. *) + let enqueue_bool st a ~level:lvl reason : unit = + if a.neg.is_true then ( + Log.debugf error + (fun k->k "(@[sat.error.trying to enqueue a false literal %a@])" Atom.debug a); + assert false + ); + assert (not a.is_true && a.var.v_level < 0 && + a.var.reason = None && lvl >= 0); + let reason = + if lvl > 0 then reason + else simpl_reason reason + in + a.is_true <- true; + a.var.v_level <- lvl; + a.var.reason <- Some reason; + Vec.push st.trail a; + Log.debugf debug + (fun k->k "(@[sat.enqueue[%d]@ %a@])" (Vec.size st.trail) Atom.debug a); + () + + (* swap elements of array *) + let[@inline] swap_arr a i j = + if i<>j then ( + let tmp = a.(i) in + a.(i) <- a.(j); + a.(j) <- tmp; + ) + + (* move atoms assigned at high levels first *) + let put_high_level_atoms_first (arr:atom array) : unit = + Array.iteri + (fun i a -> + if i>0 && Atom.level a > Atom.level arr.(0) then ( + (* move first to second, [i]-th to first, second to [i] *) + if i=1 then ( + swap_arr arr 0 1; + ) else ( + let tmp = arr.(1) in + arr.(1) <- arr.(0); + arr.(0) <- arr.(i); + arr.(i) <- tmp; + ); + ) else if i>1 && Atom.level a > Atom.level arr.(1) then ( + swap_arr arr 1 i; + )) + arr + + (* find which level to backtrack to, given a conflict clause + and a boolean stating whether it is + a UIP ("Unique Implication Point") + precond: the atom list is sorted by decreasing decision level *) + let backtrack_lvl _st (arr: atom array) : int * bool = + if Array.length arr <= 1 then ( + 0, true + ) else ( + let a = arr.(0) in + let b = arr.(1) in + assert(a.var.v_level > 0); + if a.var.v_level > b.var.v_level then ( + (* backtrack below [a], so we can propagate [not a] *) + b.var.v_level, true + ) else ( + assert (a.var.v_level = b.var.v_level); + assert (a.var.v_level >= 0); + max (a.var.v_level - 1) 0, false + ) + ) + + (* result of conflict analysis, containing the learnt clause and some + additional info. + + invariant: cr_history's order matters, as its head is later used + during pop operations to determine the origin of a clause/conflict + (boolean conflict i.e hypothesis, or theory lemma) *) + type conflict_res = { + cr_backtrack_lvl : int; (* level to backtrack to *) + cr_learnt: atom array; (* lemma learnt from conflict *) + cr_history: clause list; (* justification *) + cr_is_uip: bool; (* conflict is UIP? *) + } + + (* conflict analysis for SAT + Same idea as the mcsat analyze function (without semantic propagations), + except we look the the Last UIP (TODO: check ?), and do it in an imperative + and efficient manner. *) + let analyze st c_clause : conflict_res = + let pathC = ref 0 in + let learnt = ref [] in + let cond = ref true in + let blevel = ref 0 in + let to_unmark = st.to_clear in (* for cleanup *) + let c = ref (Some c_clause) in + let tr_ind = ref (Vec.size st.trail - 1) in + let history = ref [] in + assert (decision_level st > 0); + Vec.clear to_unmark; + let conflict_level = + if Plugin.has_theory + then Array.fold_left (fun acc p -> max acc p.var.v_level) 0 c_clause.atoms + else decision_level st + in + Log.debugf debug + (fun k -> k "(@[sat.analyze-conflict@ :c-level %d@ :clause %a@])" conflict_level Clause.debug c_clause); + while !cond do + begin match !c with + | None -> + Log.debug debug "(@[sat.analyze-conflict: skipping resolution for semantic propagation@])" + | Some clause -> + Log.debugf debug (fun k->k"(@[sat.analyze-conflict.resolve@ %a@])" Clause.debug clause); + if Clause.removable clause then ( + clause_bump_activity st clause; + ); + history := clause :: !history; + (* visit the current predecessors *) + for j = 0 to Array.length clause.atoms - 1 do + let q = clause.atoms.(j) in + assert (q.is_true || q.neg.is_true && q.var.v_level >= 0); (* unsure? *) + if q.var.v_level <= 0 then ( + assert (q.neg.is_true); + match q.var.reason with + | Some (Bcp cl | Bcp_lazy (lazy cl)) -> history := cl :: !history + | Some Decision | None -> assert false + ); + if not (Var.marked q.var) then ( + Var.mark q.var; + Vec.push to_unmark q.var; + if q.var.v_level > 0 then ( + var_bump_activity st q.var; + if q.var.v_level >= conflict_level then ( + incr pathC; + ) else ( + learnt := q :: !learnt; + blevel := max !blevel q.var.v_level + ) + ) + ) + done + end; + + (* look for the next node to expand *) + while + let a = Vec.get st.trail !tr_ind in + Log.debugf debug + (fun k -> k "(@[sat.analyze-conflict.at-trail-elt@ %a@])" Atom.debug a); + (not (Var.marked a.var)) || + (a.var.v_level < conflict_level) + do + decr tr_ind; + done; + let p = Vec.get st.trail !tr_ind in + decr pathC; + decr tr_ind; + match !pathC, p.var.reason with + | 0, _ -> + cond := false; + learnt := p.neg :: List.rev !learnt + | n, Some (Bcp cl | Bcp_lazy (lazy cl)) -> + assert (n > 0); + assert (p.var.v_level >= conflict_level); + c := Some cl + | _, (None | Some Decision) -> assert false + done; + Vec.iter Var.clear to_unmark; + Vec.clear to_unmark; + (* put high-level literals first, so that: + - they make adequate watch lits + - the first literal is the UIP, if any *) + let a = Array.of_list !learnt in + Array.fast_sort (fun p q -> compare q.var.v_level p.var.v_level) a; + (* put_high_level_atoms_first a; *) + let level, is_uip = backtrack_lvl st a in + { cr_backtrack_lvl = level; + cr_learnt = a; + cr_history = List.rev !history; + cr_is_uip = is_uip; + } + + (* add the learnt clause to the clause database, propagate, etc. *) + let record_learnt_clause st (confl:clause) (cr:conflict_res): unit = + let proof = if st.store_proof then History cr.cr_history else Empty_premise in + begin match cr.cr_learnt with + | [| |] -> assert false + | [|fuip|] -> + assert (cr.cr_backtrack_lvl = 0 && decision_level st = 0); + if fuip.neg.is_true then ( + (* incompatible at level 0 *) + report_unsat st (US_false confl) + ) else ( + let uclause = Clause.make_removable_a cr.cr_learnt proof in + (* no need to attach [uclause], it is true at level 0 *) + enqueue_bool st fuip ~level:0 (Bcp uclause) + ) + | _ -> + let fuip = cr.cr_learnt.(0) in + let lclause = Clause.make_removable_a cr.cr_learnt proof in + if Array.length lclause.atoms > 2 then ( + Vec.push st.clauses_learnt lclause; (* potentially gc'able *) + ); + attach_clause lclause; + clause_bump_activity st lclause; + assert (cr.cr_is_uip); + enqueue_bool st fuip ~level:cr.cr_backtrack_lvl (Bcp lclause) + end; + var_decay_activity st; + clause_decay_activity st + + (* process a conflict: + - learn clause + - backtrack + - report unsat if conflict at level 0 + *) + let add_boolean_conflict st (confl:clause): unit = + Log.debugf info (fun k -> k "(@[sat.add-bool-conflict@ %a@])" Clause.debug confl); + st.next_decisions <- []; + assert (decision_level st >= 0); + if decision_level st = 0 || + Array.for_all (fun a -> a.var.v_level <= 0) confl.atoms then ( + (* Top-level conflict *) + report_unsat st (US_false confl); + ); + let cr = analyze st confl in + cancel_until st (max cr.cr_backtrack_lvl 0); + record_learnt_clause st confl cr + + (* Get the correct vector to insert a clause in. *) + let[@inline] add_clause_to_vec st c = + if Clause.removable c then ( + Vec.push st.clauses_learnt c + ) else ( + Vec.push st.clauses_hyps c + ) + + (* Add a new clause, simplifying, propagating, and backtracking if + the clause is false in the current trail *) + let add_clause_ st (init:clause) : unit = + Log.debugf debug (fun k -> k "(@[sat.add-clause@ @[%a@]@])" Clause.debug init); + (* Insertion of new lits is done before simplification. Indeed, else a lit in a + trivial clause could end up being not decided on, which is a bug. *) + Array.iter (fun x -> insert_var_order st x.var) init.atoms; + try + let c = eliminate_duplicates init in + assert (c.flags = init.flags); + Log.debugf debug (fun k -> k "(@[sat.dups-removed@ %a@])" Clause.debug c); + let atoms, history = partition c.atoms in + let clause = + if history = [] then ( + (* just update order of atoms *) + List.iteri (fun i a -> c.atoms.(i) <- a) atoms; + c + ) else ( + let proof = if st.store_proof then History (c::history) else Empty_premise in + Clause.make ~flags:c.flags atoms proof + ) + in + assert (clause.flags = init.flags); + Log.debugf info (fun k->k "(@[sat.new-clause@ @[%a@]@])" Clause.debug clause); + match atoms with + | [] -> + report_unsat st @@ US_false clause + | [a] -> + cancel_until st 0; + if a.neg.is_true then ( + (* cannot recover from this *) + report_unsat st @@ US_false clause + ) else if a.is_true then ( + () (* atom is already true, nothing to do *) + ) else ( + Log.debugf debug + (fun k->k "(@[sat.add-clause.unit-clause@ :propagating %a@])" Atom.debug a); + add_clause_to_vec st clause; + enqueue_bool st a ~level:0 (Bcp clause) + ) + | a::b::_ -> + add_clause_to_vec st clause; + if a.neg.is_true then ( + (* Atoms need to be sorted in decreasing order of decision level, + or we might watch the wrong literals. *) + put_high_level_atoms_first clause.atoms; + attach_clause clause; + add_boolean_conflict st clause + ) else ( + attach_clause clause; + if b.neg.is_true && not a.is_true && not a.neg.is_true then ( + let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in + cancel_until st lvl; + enqueue_bool st a ~level:lvl (Bcp clause) + ) + ) + with Trivial -> + Log.debugf info + (fun k->k "(@[sat.add-clause@ :ignore-trivial @[%a@]@])" Clause.debug init) + + let[@inline never] flush_clauses_ st = + while not @@ Vec.is_empty st.clauses_to_add do + let c = Vec.pop st.clauses_to_add in + add_clause_ st c + done + + let[@inline] flush_clauses st = + if not @@ Vec.is_empty st.clauses_to_add then flush_clauses_ st + + type watch_res = + | Watch_kept + | Watch_removed + + (* boolean propagation. + [a] is the false atom, one of [c]'s two watch literals + [i] is the index of [c] in [a.watched] + @return whether [c] was removed from [a.watched] + *) + let propagate_in_clause st (a:atom) (c:clause) (i:int): watch_res = + let atoms = c.atoms in + let first = atoms.(0) in + if first == a.neg then ( + (* false lit must be at index 1 *) + atoms.(0) <- atoms.(1); + atoms.(1) <- first + ) else ( + assert (a.neg == atoms.(1)) + ); + let first = atoms.(0) in + if first.is_true + then Watch_kept (* true clause, keep it in watched *) + else ( + try (* look for another watch lit *) + for k = 2 to Array.length atoms - 1 do + let ak = atoms.(k) in + if not (ak.neg.is_true) then ( + (* watch lit found: update and exit *) + atoms.(1) <- ak; + atoms.(k) <- a.neg; + (* remove [c] from [a.watched], add it to [ak.neg.watched] *) + Vec.push ak.neg.watched c; + assert (Vec.get a.watched i == c); + Vec.fast_remove a.watched i; + raise_notrace Exit + ) + done; + (* no watch lit found *) + if first.neg.is_true then ( + (* clause is false *) + st.elt_head <- Vec.size st.trail; + raise_notrace (Conflict c) + ) else ( + enqueue_bool st first ~level:(decision_level st) (Bcp c) + ); + Watch_kept + with Exit -> + Watch_removed + ) + + (* propagate atom [a], which was just decided. This checks every + clause watching [a] to see if the clause is false, unit, or has + other possible watches + @param res the optional conflict clause that the propagation might trigger *) + let propagate_atom st a : unit = + let watched = a.watched in + let rec aux i = + if i >= Vec.size watched then () + else ( + let c = Vec.get watched i in + assert (Clause.attached c); + let j = + if Clause.dead c then ( + Vec.fast_remove watched i; + i + ) else ( + match propagate_in_clause st a c i with + | Watch_kept -> i+1 + | Watch_removed -> i (* clause at this index changed *) + ) + in + aux j + ) + in + aux 0 + + exception Th_conflict of Clause.t + + let[@inline] slice_get st i = Vec.get st.trail i + + let acts_add_clause st ?(keep=false) (l:formula list) (lemma:lemma): unit = + let atoms = List.rev_map (mk_atom st) l in + let flags = if keep then 0 else Clause.flag_removable in + let c = Clause.make ~flags atoms (Lemma lemma) in + Log.debugf info (fun k->k "(@[sat.th.add-clause@ %a@])" Clause.debug c); + Vec.push st.clauses_to_add c + + let acts_add_decision_lit (st:t) (f:formula) (sign:bool) : unit = + let a = mk_atom st f in + let a = if sign then a else Atom.neg a in + if not (Atom.has_value a) then ( + Log.debugf 10 (fun k->k "(@[sat.th.add-decision-lit@ %a@])" Atom.debug a); + st.next_decisions <- a :: st.next_decisions + ) + + let acts_raise st (l:formula list) proof : 'a = + let atoms = List.rev_map (mk_atom st) l in + (* conflicts can be removed *) + let c = Clause.make_removable atoms (Lemma proof) in + Log.debugf 5 (fun k->k "(@[@{sat.th.raise-conflict@}@ %a@])" Clause.debug c); + raise_notrace (Th_conflict c) + + let check_consequence_lits_false_ l : unit = + match List.find Atom.is_true l with + | a -> + invalid_argf + "slice.acts_propagate:@ Consequence should contain only true literals, but %a isn't" + Atom.debug (Atom.neg a) + | exception Not_found -> () + + let acts_propagate (st:t) f = function + | Solver_intf.Consequence mk_expl -> + let p = mk_atom st f in + if Atom.is_true p then () + else if Atom.is_false p then ( + let lits, proof = mk_expl() in + let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in + check_consequence_lits_false_ l; + let c = Clause.make_removable (p :: l) (Lemma proof) in + raise_notrace (Th_conflict c) + ) else ( + insert_var_order st p.var; + let c = lazy ( + let lits, proof = mk_expl () in + let l = List.rev_map (fun f -> Atom.neg @@ mk_atom st f) lits in + (* note: we can check that invariant here in the [lazy] block, + as conflict analysis will run in an environment where + the literals should be true anyway, since it's an extension of the + current trail + (otherwise the propagated lit would have been backtracked and + discarded already.) *) + check_consequence_lits_false_ l; + Clause.make_removable (p :: l) (Lemma proof) + ) in + let level = decision_level st in + enqueue_bool st p ~level (Bcp_lazy c) + ) + + let[@specialise] acts_iter st ~full head f : unit = + for i = (if full then 0 else head) to Vec.size st.trail-1 do + let a = Vec.get st.trail i in + f a.lit + done + + let eval_atom_ a = + if Atom.is_true a then Solver_intf.L_true + else if Atom.is_false a then Solver_intf.L_false + else Solver_intf.L_undefined + + let[@inline] acts_eval_lit st (f:formula) : Solver_intf.lbool = + let a = mk_atom st f in + eval_atom_ a + + let[@inline] acts_mk_lit st ?default_pol f : unit = + ignore (mk_atom ?default_pol st f : atom) + + let[@inline] current_slice st : _ Solver_intf.acts = { + Solver_intf. + acts_iter_assumptions=acts_iter st ~full:false st.th_head; + acts_eval_lit= acts_eval_lit st; + acts_mk_lit=acts_mk_lit st; + acts_add_clause = acts_add_clause st; + acts_propagate = acts_propagate st; + acts_raise_conflict=acts_raise st; + acts_add_decision_lit=acts_add_decision_lit st; + } + + (* full slice, for [if_sat] final check *) + let[@inline] full_slice st : _ Solver_intf.acts = { + Solver_intf. + acts_iter_assumptions=acts_iter st ~full:true st.th_head; + acts_eval_lit= acts_eval_lit st; + acts_mk_lit=acts_mk_lit st; + acts_add_clause = acts_add_clause st; + acts_propagate = acts_propagate st; + acts_raise_conflict=acts_raise st; + acts_add_decision_lit=acts_add_decision_lit st; + } + + (* Assert that the conflict is indeeed a conflict *) + let check_is_conflict_ (c:Clause.t) : unit = + if not @@ Array.for_all (Atom.is_false) c.atoms then ( + invalid_argf "conflict should be false: %a" Clause.debug c + ) + + (* some boolean literals were decided/propagated within Msat. Now we + need to inform the theory of those assumptions, so it can do its job. + @return the conflict clause, if the theory detects unsatisfiability *) + let rec theory_propagate st : clause option = + assert (st.elt_head = Vec.size st.trail); + assert (st.th_head <= st.elt_head); + if st.th_head = st.elt_head then ( + None (* fixpoint/no propagation *) + ) else ( + let slice = current_slice st in + st.th_head <- st.elt_head; (* catch up *) + match Plugin.partial_check st.th slice with + | () -> + flush_clauses st; + propagate st + | exception Th_conflict c -> + check_is_conflict_ c; + Array.iter (fun a -> insert_var_order st a.var) c.atoms; + Some c + ) + + (* fixpoint between boolean propagation and theory propagation + @return a conflict clause, if any *) + and propagate (st:t) : clause option = + (* First, treat the stack of lemmas added by the theory, if any *) + flush_clauses st; + (* Now, check that the situation is sane *) + assert (st.elt_head <= Vec.size st.trail); + if st.elt_head = Vec.size st.trail then ( + theory_propagate st + ) else ( + match + while st.elt_head < Vec.size st.trail do + let a = Vec.get st.trail st.elt_head in + propagate_atom st a; + st.elt_head <- st.elt_head + 1; + done; + with + | () -> theory_propagate st + | exception Conflict c -> Some c + ) + + (* compute unsat core from assumption [a] *) + let analyze_final (self:t) (a:atom) : atom list = + Log.debugf 5 (fun k->k "(@[sat.analyze-final@ :lit %a@])" Atom.debug a); + assert (Atom.is_false a); + let core = ref [a] in + let idx = ref (Vec.size self.trail - 1) in + Var.mark a.var; + let seen = ref [a.var] in + while !idx >= 0 do + let a' = Vec.get self.trail !idx in + if Var.marked a'.var then ( + match Atom.reason a' with + | Some Decision -> core := a' :: !core + | Some (Bcp c | Bcp_lazy (lazy c)) -> + Array.iter + (fun a -> + let v = a.var in + if not @@ Var.marked v then ( + seen := v :: !seen; + Var.mark v; + )) + c.atoms + | None -> () + ); + decr idx + done; + List.iter Var.unmark !seen; + Log.debugf 5 (fun k->k "(@[sat.analyze-final.done@ :core %a@])" (Format.pp_print_list Atom.debug) !core); + !core + + (* remove some learnt clauses. *) + let reduce_db (st:t) (n_of_learnts: int) : unit = + let v = st.clauses_learnt in + Log.debugf 3 (fun k->k "(@[sat.gc.start :keep %d :out-of %d@])" n_of_learnts (Vec.size v)); + assert (Vec.size v > n_of_learnts); + (* sort by decreasing activity *) + Vec.sort v (fun c1 c2 -> compare c2.activity c1.activity); + let n_collected = ref 0 in + while Vec.size v > n_of_learnts do + let c = Vec.pop v in + assert (Clause.removable c); + Clause.set_dead c; + assert (Clause.dead c); + incr n_collected; + done; + Log.debugf 3 (fun k->k "(@[sat.gc.done :collected %d@])" !n_collected); + () + + (* Decide on a new literal, and enqueue it into the trail *) + let rec pick_branch_aux st atom : unit = + let v = atom.var in + if v.v_level >= 0 then ( + assert (v.pa.is_true || v.na.is_true); + pick_branch_lit st + ) else ( + new_decision_level st; + let current_level = decision_level st in + enqueue_bool st atom ~level:current_level Decision; + (match st.on_decision with Some f -> f atom | None -> ()); + ) + + and pick_branch_lit st = + match st.next_decisions with + | atom :: tl -> + st.next_decisions <- tl; + pick_branch_aux st atom + | [] when decision_level st < Vec.size st.assumptions -> + (* use an assumption *) + let a = Vec.get st.assumptions (decision_level st) in + if Atom.is_true a then ( + new_decision_level st; (* pseudo decision level, [a] is already true *) + pick_branch_lit st + ) else if Atom.is_false a then ( + (* root conflict, find unsat core *) + let core = analyze_final st a in + raise (E_unsat (US_local {first=a; core})) + ) else ( + pick_branch_aux st a + ) + | [] -> + begin match H.remove_min st.order with + | v -> + pick_branch_aux st (if Var.default_pol v then v.pa else v.na) + | exception Not_found -> raise_notrace E_sat + end + + (* do some amount of search, until the number of conflicts or clause learnt + reaches the given parameters *) + let search (st:t) n_of_conflicts n_of_learnts : unit = + Log.debugf 3 + (fun k->k "(@[sat.search@ :n-conflicts %d@ :n-learnt %d@])" n_of_conflicts n_of_learnts); + let conflictC = ref 0 in + while true do + match propagate st with + | Some confl -> (* Conflict *) + incr conflictC; + (* When the theory has raised Unsat, add_boolean_conflict + might 'forget' the initial conflict clause, and only add the + analyzed backtrack clause. So in those case, we use add_clause + to make sure the initial conflict clause is also added. *) + if Clause.attached confl then ( + add_boolean_conflict st confl + ) else ( + add_clause_ st confl + ); + (match st.on_conflict with Some f -> f confl.atoms | None -> ()); + + | None -> (* No Conflict *) + assert (st.elt_head = Vec.size st.trail); + assert (st.elt_head = st.th_head); + if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then ( + Log.debug info "(sat.restarting)"; + cancel_until st 0; + raise_notrace Restart + ); + (* if decision_level() = 0 then simplify (); *) + + if n_of_learnts > 0 && + Vec.size st.clauses_learnt - Vec.size st.trail > n_of_learnts then ( + reduce_db st n_of_learnts; + ); + + pick_branch_lit st + done + + let eval_level (_st:t) (a:atom) = + let lvl = a.var.v_level in + if Atom.is_true a then ( + assert (lvl >= 0); + true, lvl + ) else if Atom.is_false a then ( + false, lvl + ) else ( + raise UndecidedLit + ) + + let[@inline] eval st lit = fst @@ eval_level st lit + + let[@inline] unsat_conflict st = st.unsat_at_0 + + (* fixpoint of propagation and decisions until a model is found, or a + conflict is reached *) + let solve_ (st:t) : unit = + Log.debugf 5 (fun k->k "(@[sat.solve :assms %d@])" (Vec.size st.assumptions)); + check_unsat_ st; + try + flush_clauses st; (* add initial clauses *) + let n_of_conflicts = ref (float_of_int restart_first) in + let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. learntsize_factor) in + while true do + begin try + search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) + with + | Restart -> + n_of_conflicts := !n_of_conflicts *. restart_inc; + n_of_learnts := !n_of_learnts *. learntsize_inc + | E_sat -> + assert (st.elt_head = Vec.size st.trail && + Vec.is_empty st.clauses_to_add && + st.next_decisions=[]); + begin match Plugin.final_check st.th (full_slice st) with + | () -> + if st.elt_head = Vec.size st.trail && + Vec.is_empty st.clauses_to_add && + st.next_decisions = [] + then ( + raise_notrace E_sat + ); + (* otherwise, keep on *) + flush_clauses st; + | exception Th_conflict c -> + check_is_conflict_ c; + Array.iter (fun a -> insert_var_order st a.var) c.atoms; + Log.debugf info (fun k -> k "(@[sat.theory-conflict-clause@ %a@])" Clause.debug c); + (match st.on_conflict with Some f -> f c.atoms | None -> ()); + Vec.push st.clauses_to_add c; + flush_clauses st; + end; + end + done + with E_sat -> () + + let assume st cnf lemma = + List.iter + (fun l -> + let atoms = List.rev_map (mk_atom st) l in + let c = Clause.make_permanent atoms (Hyp lemma) in + Log.debugf debug (fun k -> k "(@[sat.assume-clause@ @[%a@]@])" Clause.debug c); + Vec.push st.clauses_to_add c) + cnf + + (* Check satisfiability *) + let check_clause c = + let res = Array.exists (fun a -> a.is_true) c.atoms in + if not res then ( + Log.debugf debug + (fun k -> k "(@[sat.check-clause@ :not-satisfied @[%a@]@])" Clause.debug c); + false + ) else + true + + let check_vec v = Vec.for_all check_clause v + let check st : bool = + Vec.is_empty st.clauses_to_add && + check_vec st.clauses_hyps && + check_vec st.clauses_learnt + + let[@inline] theory st = st.th + + (* Unsafe access to internal data *) + + let hyps env = env.clauses_hyps + + let history env = env.clauses_learnt + + let trail env = env.trail + + (* Result type *) + type res = + | Sat of Formula.t Solver_intf.sat_state + | Unsat of (atom,clause,Proof.t) Solver_intf.unsat_state + + let pp_all st lvl status = + Log.debugf lvl + (fun k -> k + "(@[sat.full-state :res %s - Full summary:@,@[Trail:@\n%a@]@,\ + @[Hyps:@\n%a@]@,@[Lemmas:@\n%a@]@,@]@." + status + (Vec.pp ~sep:"" Atom.debug) (trail st) + (Vec.pp ~sep:"" Clause.debug) (hyps st) + (Vec.pp ~sep:"" Clause.debug) (history st) + ) + + let mk_sat (st:t) : Formula.t Solver_intf.sat_state = + pp_all st 99 "SAT"; + let t = trail st in + let iter_trail f = + Vec.iter (fun a -> f (Atom.formula a)) t + in + let[@inline] eval f = eval st (mk_atom st f) in + let[@inline] eval_level f = eval_level st (mk_atom st f) in + { Solver_intf. + eval; eval_level; iter_trail; + } + + let mk_unsat (st:t) (us: unsat_cause) : _ Solver_intf.unsat_state = + pp_all st 99 "UNSAT"; + let unsat_assumptions () = match us with + | US_local {first=_; core} -> core + | _ -> [] + in + let unsat_conflict = match us with + | US_false c -> fun() -> c + | US_local {core=[]; _} -> assert false + | US_local {first; core} -> + let c = lazy ( + let core = List.rev core in (* increasing trail order *) + assert (Atom.equal first @@ List.hd core); + let proof_of (a:atom) = match Atom.reason a with + | Some Decision -> Clause.make_removable [a] Local + | Some (Bcp c | Bcp_lazy (lazy c)) -> c + | None -> assert false + in + let other_lits = List.filter (fun a -> not (Atom.equal a first)) core in + let hist = + Clause.make_permanent [first] Local :: + proof_of first :: + List.map proof_of other_lits in + Clause.make_permanent [] (History hist) + ) in + fun () -> Lazy.force c + in + let get_proof () = + let c = unsat_conflict () in + Proof.prove c + in + { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } + + let add_clause_a st c lemma : unit = + try + let c = Clause.make_a ~flags:0 c (Hyp lemma) in + add_clause_ st c + with + | E_unsat (US_false c) -> + st.unsat_at_0 <- Some c + + let add_clause st c lemma : unit = + try + let c = Clause.make_permanent c (Hyp lemma) in + add_clause_ st c + with + | E_unsat (US_false c) -> + st.unsat_at_0 <- Some c + + let solve ?(assumptions=[]) (st:t) : res = + cancel_until st 0; + Vec.clear st.assumptions; + List.iter (Vec.push st.assumptions) assumptions; + try + solve_ st; + Sat (mk_sat st) + with E_unsat us -> + Unsat (mk_unsat st us) + + let true_at_level0 st a = + try + let b, lev = eval_level st a in + b && lev = 0 + with UndecidedLit -> false + + let[@inline] eval_atom _st a : Solver_intf.lbool = eval_atom_ a + + let export (st:t) : clause Solver_intf.export = + let hyps = hyps st in + let history = history st in + {Solver_intf.hyps; history; } +end +[@@inline][@@specialise] + + +module Make_cdcl_t(Plugin : Solver_intf.PLUGIN_CDCL_T) = + Make(struct + include Plugin + let has_theory = true + end) +[@@inline][@@specialise] + +module Make_pure_sat(Plugin : Solver_intf.PLUGIN_SAT) = + Make(struct + module Formula = Plugin.Formula + type t = unit + type proof = Plugin.proof + let push_level () = () + let pop_levels _ _ = () + let partial_check () _ = () + let final_check () _ = () + let has_theory = false +end) +[@@inline][@@specialise] diff --git a/src/sat/Solver.mli b/src/sat/Solver.mli deleted file mode 100644 index b256a4a8..00000000 --- a/src/sat/Solver.mli +++ /dev/null @@ -1,34 +0,0 @@ -(* -MSAT is free software, using the Apache license, see file LICENSE -Copyright 2014 Guillaume Bury -Copyright 2014 Simon Cruanes -*) - -(** mSAT safe interface - - This module defines a safe interface for the core solver. - It is the basis of the {!module:Solver} and {!module:Mcsolver} modules. -*) - -module type S = Solver_intf.S -(** Safe external interface of solvers. *) - -module Make_cdcl_t(Th : Solver_intf.PLUGIN_CDCL_T) - : S with type Term.t = Solver_intf.void - and module Formula = Th.Formula - and type lemma = Th.proof - and type theory = Th.t - -module Make_mcsat(Th : Solver_intf.PLUGIN_MCSAT) - : S with module Term = Th.Term - and module Formula = Th.Formula - and type lemma = Th.proof - and type theory = Th.t - -module Make_pure_sat(Th: Solver_intf.PLUGIN_SAT) - : S with type Term.t = Solver_intf.void - and module Formula = Th.Formula - and type lemma = Th.proof - and type theory = unit - - diff --git a/src/sat/Solver_intf.ml b/src/sat/Solver_intf.ml index ab375a4d..0abbbff8 100644 --- a/src/sat/Solver_intf.ml +++ b/src/sat/Solver_intf.ml @@ -13,7 +13,7 @@ Copyright 2016 Simon Cruanes type 'a printer = Format.formatter -> 'a -> unit -type ('term, 'form, 'value) sat_state = { +type 'form sat_state = { eval: 'form -> bool; (** Returns the valuation of a formula in the current state of the sat solver. @@ -24,11 +24,9 @@ type ('term, 'form, 'value) sat_state = { the atom to have this value; otherwise it is due to choices that can potentially be backtracked. @raise UndecidedLit if the literal is not decided *) - iter_trail : ('form -> unit) -> ('term -> unit) -> unit; - (** Iter thorugh the formulas and terms in order of decision/propagation + iter_trail : ('form -> unit) -> unit; + (** Iter through the formulas in order of decision/propagation (starting from the first propagation, to the last propagation). *) - model: unit -> ('term * 'value) list; - (** Returns the model found if the formula is satisfiable. *) } (** The type of values returned when the solver reaches a SAT state. *) @@ -68,14 +66,7 @@ type 'term eval_res = - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) *) -type ('term, 'formula, 'value) assumption = - | Lit of 'formula (** The given formula is asserted true by the solver *) - | Assign of 'term * 'value (** The term is assigned to the value *) -(** Asusmptions made by the core SAT solver. *) - -type ('term, 'formula, 'proof) reason = - | Eval of 'term list - (** The formula can be evalutaed using the terms in the list *) +type ('formula, 'proof) reason = | Consequence of (unit -> 'formula list * 'proof) (** [Consequence (l, p)] means that the formulas in [l] imply the propagated formula [f]. The proof should be a proof of the clause "[l] implies [f]". @@ -101,8 +92,8 @@ type lbool = L_true | L_false | L_undefined (** Valuation of an atom *) (* TODO: find a way to use atoms instead of formulas here *) -type ('term, 'formula, 'value, 'proof) acts = { - acts_iter_assumptions: (('term,'formula,'value) assumption -> unit) -> unit; +type ('formula, 'proof) acts = { + acts_iter_assumptions: ('formula -> unit) -> unit; (** Traverse the new assumptions on the boolean trail. *) acts_eval_lit: 'formula -> lbool; @@ -112,10 +103,6 @@ type ('term, 'formula, 'value, 'proof) acts = { (** Map the given formula to a literal, which will be decided by the SAT solver. *) - acts_mk_term: 'term -> unit; - (** Map the given term (and its subterms) to decision variables, - for the MCSAT solver to decide. *) - acts_add_clause: ?keep:bool -> 'formula list -> 'proof -> unit; (** Add a clause to the solver. @param keep if true, the clause will be kept by the solver. @@ -128,7 +115,7 @@ type ('term, 'formula, 'value, 'proof) acts = { The list of atoms must be a valid theory lemma that is false in the current trail. *) - acts_propagate: 'formula -> ('term, 'formula, 'proof) reason -> unit; + acts_propagate: 'formula -> ('formula, 'proof) reason -> unit; (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) @@ -139,11 +126,6 @@ type ('term, 'formula, 'value, 'proof) acts = { } (** The type for a slice of assertions to assume/propagate in the theory. *) -type ('a, 'b) gadt_eq = GADT_EQ : ('a, 'a) gadt_eq - -type void = (unit,bool) gadt_eq -(** A provably empty type *) - exception No_proof module type FORMULA = sig @@ -172,37 +154,6 @@ module type FORMULA = sig but one returns [Negated] and the other [Same_sign]. *) end -(** Formulas and Terms required for mcSAT *) -module type EXPR = sig - type proof - (** An abstract type for proofs *) - - module Term : sig - type t - (** The type of terms *) - - val equal : t -> t -> bool - (** Equality over terms. *) - - val hash : t -> int - (** Hashing function for terms. Should be such that two terms equal according - to {!equal} have the same hash. *) - - val pp : t printer - (** Printing function used among other for debugging. *) - end - - module Value : sig - type t - (** The type of semantic values (domain elements) *) - - val pp : t printer - (** Printing function used among other for debugging. *) - end - - module Formula : FORMULA -end - (** Signature for theories to be given to the CDCL(T) solver *) module type PLUGIN_CDCL_T = sig type t @@ -218,52 +169,18 @@ module type PLUGIN_CDCL_T = sig val pop_levels : t -> int -> unit (** Pop [n] levels of the theory *) - val partial_check : t -> (void, Formula.t, void, proof) acts -> unit + val partial_check : t -> (Formula.t, proof) acts -> unit (** Assume the formulas in the slice, possibly using the [slice] to push new formulas to be propagated or to raising a conflict or to add new lemmas. *) - val final_check : t -> (void, Formula.t, void, proof) acts -> unit + val final_check : t -> (Formula.t, proof) acts -> unit (** Called at the end of the search in case a model has been found. If no new clause is pushed, then proof search ends and "sat" is returned; if lemmas are added, search is resumed; if a conflict clause is added, search backtracks and then resumes. *) end -(** Signature for theories to be given to the Model Constructing Solver. *) -module type PLUGIN_MCSAT = sig - type t - (** The plugin state itself *) - - include EXPR - - val push_level : t -> unit - (** Create a new backtrack level *) - - val pop_levels : t -> int -> unit - (** Pop [n] levels of the theory *) - - val partial_check : t -> (Term.t, Formula.t, Value.t, proof) acts -> unit - (** Assume the formulas in the slice, possibly using the [slice] - to push new formulas to be propagated or to raising a conflict or to add - new lemmas. *) - - val final_check : t -> (Term.t, Formula.t, Value.t, proof) acts -> unit - (** Called at the end of the search in case a model has been found. - If no new clause is pushed, then proof search ends and "sat" is returned; - if lemmas are added, search is resumed; - if a conflict clause is added, search backtracks and then resumes. *) - - val assign : t -> Term.t -> Value.t - (** Returns an assignment value for the given term. *) - - val iter_assignable : t -> (Term.t -> unit) -> Formula.t -> unit - (** An iterator over the subTerm.ts of a Formula.t that should be assigned a value (usually the poure subTerm.ts) *) - - val eval : t -> Formula.t -> Term.t eval_res - (** Returns the evaluation of the Formula.t in the current assignment *) -end - (** Signature for pure SAT solvers *) module type PLUGIN_SAT = sig module Formula : FORMULA @@ -386,9 +303,7 @@ module type S = sig These are the internal modules used, you should probably not use them if you're not familiar with the internals of mSAT. *) - include EXPR - - type term = Term.t (** user terms *) + module Formula : FORMULA type formula = Formula.t (** user formulas *) @@ -406,6 +321,7 @@ module type S = sig type solver + (* TODO: keep this internal *) module Atom : sig type t = atom @@ -437,7 +353,6 @@ module type S = sig and type atom = atom and type formula = formula and type lemma = lemma - and type t = proof (** A module to manipulate proofs. *) type t = solver @@ -465,7 +380,7 @@ module type S = sig (** Result type for the solver *) type res = - | Sat of (term,formula,Value.t) sat_state (** Returned when the solver reaches SAT, with a model *) + | Sat of formula sat_state (** Returned when the solver reaches SAT, with a model *) | Unsat of (atom,clause,Proof.t) unsat_state (** Returned when the solver reaches UNSAT, with a proof *) exception UndecidedLit @@ -492,11 +407,6 @@ module type S = sig The assumptions are just used for this call to [solve], they are not saved in the solver's state. *) - val make_term : t -> term -> unit - (** Add a new term (i.e. decision variable) to the solver. This term will - be decided on at some point during solving, wether it appears - in clauses or not. *) - val make_atom : t -> formula -> atom (** Add a new atom (i.e propositional formula) to the solver. This formula will be decided on at some point during solving, diff --git a/src/sat/msat.mld b/src/sat/msat.mld deleted file mode 100644 index 50973961..00000000 --- a/src/sat/msat.mld +++ /dev/null @@ -1,92 +0,0 @@ -{1 mSAT} - -{2 License} - -This code is free, under the {{:https://github.com/Gbury/mSAT/blob/master/LICENSE}Apache 2.0 license}. - -{2 Contents} - -mSAT is an ocaml library providing SAT/SMT/McSat solvers. More precisely, -what mSAT provides are functors to easily create such solvers. Indeed, the core -of a sat solver does not need much information about either the exact representation -of terms or the inner workings of a theory. - -Most modules in mSAT actually define functors. These functors usually take one -or two arguments, usually an implementation of Terms and formulas used, and an implementation -of the theory used during solving. - -{4 Solver creation} - -The following modules allow to easily create a SAT or SMT solver (remark: a SAT solver is -simply an SMT solver with an empty theory). - -{!modules: -Msat -} - -The following modules allow the creation of a McSat solver (Model Constructing solver): - -{!modules: -Msat_mcsolver -} - -{4 Useful tools} - -An instanciation of a pure sat solver is also provided: - -{!modules: -Msat_sat -} - -Lastly, mSAT also provides an implementation of Tseitin's CNF conversion: - -{!modules: -Msat_tseitin -} - -{4 Proof Management} - -mSAT solvers are able to provide detailed proofs when an unsat state is reached. To do -so, it require the provided theory to give proofs of the tautologies it gives the solver. -These proofs will be called lemmas. The type of lemmas is defined by the theory and can -very well be [unit]. - -In this context a proof is a resolution tree, whose conclusion (i.e. root) is the -empty clause, effectively allowing to deduce [false] from the hypotheses. -A resolution tree is a binary tree whose nodes are clauses. Inner nodes' clauses are -obtained by performing resolution between the two clauses of the children nodes, while -leafs of the tree are either hypotheses, or tautologies (i.e. conflicts returned by -the theory). - -{!modules: -Msat__Res -Msat__Res_intf -} - -Backends for exporting proofs to different formats: - -{!modules: -Dot -Coq -Dedukti -Backend_intf -} - -{4 Internal modules} - -WARNING: for advanced users only ! These modules expose a lot of unsafe functions -that must be used with care to not break the required invariants. Additionally, these -interfaces are not part of the main API and so are subject to a lot more breaking changes -than the safe modules above. - -{!modules: -Dimacs -Internal -External -Solver_types -Solver_types_intf -} - -{2 Index} - -{!indexlist} From 1aa160fe56a20b1ae09e63f324f271c71e769aa2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 02:46:04 -0400 Subject: [PATCH 177/182] use a pure sat solver for cnf files --- sidekick.opam | 1 - src/backend/Dot.ml | 8 ++-- src/main/dune | 2 +- src/main/main.ml | 88 +++++++++++++++---------------------- src/main/pure_sat_solver.ml | 58 ++++++++++++++++++++++++ src/sat/Sidekick_sat.ml | 2 + src/sat/Solver.ml | 38 +++++++++------- src/sat/Solver_intf.ml | 2 +- src/smtlib/dune | 2 +- src/util/dune | 2 +- 10 files changed, 124 insertions(+), 79 deletions(-) create mode 100644 src/main/pure_sat_solver.ml diff --git a/sidekick.opam b/sidekick.opam index 7f27208f..03a6a890 100644 --- a/sidekick.opam +++ b/sidekick.opam @@ -14,7 +14,6 @@ depends: [ "dune" { >= "1.1" } "containers" { >= "3.0" & < "4.0" } "iter" { >= "1.0" & < "2.0" } - "msat" { >= "0.9" < "0.10" } "ocaml" { >= "4.04" } "alcotest" {with-test} "odoc" {with-doc} diff --git a/src/backend/Dot.ml b/src/backend/Dot.ml index 632f39a7..7085fc86 100644 --- a/src/backend/Dot.ml +++ b/src/backend/Dot.ml @@ -36,15 +36,15 @@ module Default(S : Sidekick_sat.S) = struct let hyp_info c = "hypothesis", Some "LIGHTBLUE", - [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.short_name c] let lemma_info c = "lemma", Some "BLUE", - [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.short_name c] let assumption_info c = "assumption", Some "PURPLE", - [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c] + [ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.short_name c] end @@ -57,7 +57,7 @@ module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom module Clause = S.Clause module P = S.Proof - let node_id n = Clause.name n.P.conclusion + let node_id n = Clause.short_name n.P.conclusion let proof_id p = node_id (P.expand p) let res_nn_id n1 n2 = node_id n1 ^ "_" ^ node_id n2 ^ "_res" let res_np_id n1 n2 = node_id n1 ^ "_" ^ proof_id n2 ^ "_res" diff --git a/src/main/dune b/src/main/dune index 621bc88c..e17a42e6 100644 --- a/src/main/dune +++ b/src/main/dune @@ -4,7 +4,7 @@ (name main) (public_name sidekick) (package sidekick-bin) - (libraries containers iter result msat sidekick.core sidekick-base + (libraries containers iter result sidekick.sat sidekick.core sidekick-base sidekick.msat-solver sidekick-bin.smtlib sidekick.tef) (flags :standard -safe-string -color always -open Sidekick_util)) diff --git a/src/main/main.ml b/src/main/main.ml index 6651aca3..b407d696 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -4,14 +4,14 @@ Copyright 2014 Guillaume Bury Copyright 2014 Simon Cruanes *) -open CCResult.Infix - module E = CCResult module Fmt = CCFormat module Term = Sidekick_base.Term module Solver = Sidekick_smtlib.Solver module Process = Sidekick_smtlib.Process +open E.Infix + type 'a or_error = ('a, string) E.t exception Out_of_time @@ -87,39 +87,6 @@ let argspec = Arg.align [ "--debug", Arg.Int Msat.Log.set_debug, " sets the debug verbose level"; ] |> List.sort compare -module Dimacs = struct - open Sidekick_base - module T = Term - - let parse_file tst (file:string) : Statement.t list or_error = - let atoms = Util.Int_tbl.create 32 in - let get_lit i = - let v = - match Util.Int_tbl.find atoms (abs i) with - | x -> Term.const tst x - | exception Not_found -> - let f = Sidekick_base.Fun.mk_undef_const - (ID.makef "%d" (abs i)) (Ty.bool()) in - Util.Int_tbl.add atoms (abs i) f; - Term.const tst f - in - if i<0 then Term.not_ tst v else v - in - try - CCIO.with_in file - (fun ic -> - let p = Dimacs_parser.create ic in - let stmts = ref [] in - Dimacs_parser.iter p - (fun c -> - let lits = List.rev_map get_lit c in - stmts := Statement.Stmt_assert_clause lits :: !stmts); - stmts := Statement.Stmt_check_sat [] :: !stmts; - Ok (List.rev !stmts)) - with e -> - E.of_exn_trace e -end - (* Limits alarm *) let check_limits () = let t = Sys.time () in @@ -130,25 +97,12 @@ let check_limits () = else if s > !size_limit then raise Out_of_space -let main () = - Sidekick_tef.setup(); - at_exit Sidekick_tef.teardown; - CCFormat.set_color_default true; - (* Administrative duties *) - Arg.parse argspec input_file usage; - if !file = "" then ( - Arg.usage argspec usage; - exit 2 - ); - let dot_proof = if !p_dot_proof = "" then None else Some !p_dot_proof in - check := !check || CCOpt.is_some dot_proof; (* dot requires a proof *) - let al = Gc.create_alarm check_limits in - Util.setup_gc(); +let main_smt ~dot_proof () : _ result = let tst = Term.create ~size:4_096 () in - let is_cnf = Filename.check_suffix !file ".cnf" in let solver = let theories = - if is_cnf then [] else [ + (* TODO: probes, to load only required theories *) + [ Process.th_bool; Process.th_data; Process.th_lra; @@ -163,8 +117,7 @@ let main () = Solver.add_theory solver Process.Check_cc.theory; ); begin - if is_cnf then Dimacs.parse_file tst !file - else Sidekick_smtlib.parse tst !file + Sidekick_smtlib.parse tst !file end >>= fun input -> (* process statements *) @@ -186,6 +139,35 @@ let main () = if !p_stat then ( Format.printf "%a@." Solver.pp_stats solver; ); + res + +let main_cnf () : _ result = + let solver = Pure_sat_solver.SAT.create ~size:`Big () in + Pure_sat_solver.Dimacs.parse_file solver !file >>= fun () -> + Pure_sat_solver.solve solver + +let main () = + Sidekick_tef.setup(); + at_exit Sidekick_tef.teardown; + CCFormat.set_color_default true; + (* Administrative duties *) + Arg.parse argspec input_file usage; + if !file = "" then ( + Arg.usage argspec usage; + exit 2 + ); + let dot_proof = if !p_dot_proof = "" then None else Some !p_dot_proof in + check := !check || CCOpt.is_some dot_proof; (* dot requires a proof *) + let al = Gc.create_alarm check_limits in + Util.setup_gc(); + let is_cnf = Filename.check_suffix !file ".cnf" in + let res = + if is_cnf then ( + main_cnf () + ) else ( + main_smt ~dot_proof () + ) + in if !p_gc_stat then ( Printf.printf "(gc_stats\n%t)\n" Gc.print_stat; ); diff --git a/src/main/pure_sat_solver.ml b/src/main/pure_sat_solver.ml new file mode 100644 index 00000000..14f79638 --- /dev/null +++ b/src/main/pure_sat_solver.ml @@ -0,0 +1,58 @@ + +(* pure SAT solver *) + +module E = CCResult +module SS = Sidekick_sat + +module Arg = struct + module Formula = struct + type t = int + let norm t = if t>0 then t, SS.Same_sign else -t, SS.Negated + let abs = abs + let sign t = t>0 + let equal = CCInt.equal + let hash = CCHash.int + let neg x = -x + let pp = Fmt.int + end + type proof=unit +end + +module SAT = Sidekick_sat.Make_pure_sat(Arg) + +module Dimacs = struct + open Sidekick_base + module T = Term + + let parse_file (solver:SAT.t) (file:string) : (unit, string) result = + let get_lit i : SAT.atom = SAT.make_atom solver i in + + try + CCIO.with_in file + (fun ic -> + let p = Dimacs_parser.create ic in + Dimacs_parser.iter p + (fun c -> + let atoms = List.rev_map get_lit c in + SAT.add_clause solver atoms ()); + Ok ()) + with e -> + E.of_exn_trace e +end + +let solve (solver:SAT.t) : (unit, string) result = + let res = + Profile.with_ "solve" (fun () -> SAT.solve solver) + in + let t2 = Sys.time () in + Printf.printf "\r"; flush stdout; + begin match res with + | SAT.Sat _ -> + let t3 = Sys.time () -. t2 in + Format.printf "Sat (%.3f/%.3f)@." t2 t3; + | SAT.Unsat _ -> + + let t3 = Sys.time () -. t2 in + Format.printf "Unsat (%.3f/%.3f)@." t2 t3; + end; + Ok () diff --git a/src/sat/Sidekick_sat.ml b/src/sat/Sidekick_sat.ml index a618d8a7..9ffa03ad 100644 --- a/src/sat/Sidekick_sat.ml +++ b/src/sat/Sidekick_sat.ml @@ -54,4 +54,6 @@ let pp_lbool out = function exception No_proof = Solver_intf.No_proof +module Solver = Solver module Make_cdcl_t = Solver.Make_cdcl_t +module Make_pure_sat = Solver.Make_pure_sat diff --git a/src/sat/Solver.ml b/src/sat/Solver.ml index bb827f8f..07609129 100644 --- a/src/sat/Solver.ml +++ b/src/sat/Solver.ml @@ -1,8 +1,8 @@ module type PLUGIN = sig val has_theory : bool - (** Is this a CDCL(T) plugin or mcsat plugin? - i.e does it have theories *) + (** [true] iff the solver is parametrized by a theory, not just + pure SAT. *) include Solver_intf.PLUGIN_CDCL_T end @@ -11,7 +11,7 @@ module type S = Solver_intf.S module type PLUGIN_CDCL_T = Solver_intf.PLUGIN_CDCL_T let invalid_argf fmt = - Format.kasprintf (fun msg -> invalid_arg ("msat: " ^ msg)) fmt + Format.kasprintf (fun msg -> invalid_arg ("sidekick.sat: " ^ msg)) fmt module Make(Plugin : PLUGIN) = struct @@ -66,6 +66,8 @@ module Make(Plugin : PLUGIN) (* Constructors *) module MF = Hashtbl.Make(Formula) + (* state for variables. declared separately because it simplifies our + life below, as it's required to construct new atoms/variables *) type st = { f_map: var MF.t; vars: var Vec.t; @@ -89,12 +91,12 @@ module Make(Plugin : PLUGIN) let get_elt st i = Vec.get st.vars i let iter_elt st f = Vec.iter f st.vars - let name_of_clause c = match c.cpremise with - | Hyp _ -> "H" ^ string_of_int c.cid - | Lemma _ -> "T" ^ string_of_int c.cid - | Local -> "L" ^ string_of_int c.cid - | History _ -> "C" ^ string_of_int c.cid - | Empty_premise -> string_of_int c.cid + let kind_of_clause c = match c.cpremise with + | Hyp _ -> "H" + | Lemma _ -> "T" + | Local -> "L" + | History _ -> "C" + | Empty_premise -> "" (* some boolean flags for variables, used as masks *) let seen_var = 0b1 @@ -224,7 +226,7 @@ module Make(Plugin : PLUGIN) | n, Some Decision -> Format.fprintf fmt "@@%d" n | n, Some Bcp c -> - Format.fprintf fmt "->%d/%s" n (name_of_clause c) + Format.fprintf fmt "->%d/%s/%d" n (kind_of_clause c) c.cid | n, Some (Bcp_lazy _) -> Format.fprintf fmt "->%d/" n @@ -268,7 +270,6 @@ module Make(Plugin : PLUGIN) let make ~flags l premise = make_a ~flags (Array.of_list l) premise let empty = make [] (History []) - let name = name_of_clause let[@inline] equal c1 c2 = c1.cid = c2.cid let[@inline] hash c = Hashtbl.hash c.cid let[@inline] atoms c = c.atoms @@ -311,20 +312,24 @@ module Make(Plugin : PLUGIN) let equal = equal end) + let short_name c = Printf.sprintf "%s%d" (kind_of_clause c) c.cid let pp fmt c = - Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms + Format.fprintf fmt "(cl[%s%d] : %a" (kind_of_clause c) c.cid Atom.pp_a c.atoms let debug_premise out = function | Hyp _ -> Format.fprintf out "hyp" | Lemma _ -> Format.fprintf out "th_lemma" | Local -> Format.fprintf out "local" | History v -> - List.iter (fun c -> Format.fprintf out "%s,@ " (name_of_clause c)) v - | Empty_premise -> Format.fprintf out "" + Format.fprintf out "(@[res"; + List.iter (fun c -> Format.fprintf out "@ %s%d," (kind_of_clause c) c.cid) v; + Format.fprintf out "@])" + | Empty_premise -> Format.fprintf out "none" let debug out ({atoms=arr; cpremise=cp;_}as c) = - Format.fprintf out "%s@[{@[%a@]}@ cpremise={@[%a@]}@]" - (name c) Atom.debug_a arr debug_premise cp + Format.fprintf out + "(@[cl[%s%d]@ {@[%a@]}@ :premise %a@])" + (kind_of_clause c) c.cid Atom.debug_a arr debug_premise cp end module Proof = struct @@ -774,7 +779,6 @@ module Make(Plugin : PLUGIN) st.on_conflict <- on_conflict; st - let[@inline] st t = t.st let[@inline] nb_clauses st = Vec.size st.clauses_hyps let[@inline] decision_level st = Vec.size st.var_levels diff --git a/src/sat/Solver_intf.ml b/src/sat/Solver_intf.ml index 0abbbff8..69a2b911 100644 --- a/src/sat/Solver_intf.ml +++ b/src/sat/Solver_intf.ml @@ -341,8 +341,8 @@ module type S = sig val atoms : t -> atom array val atoms_l : t -> atom list val equal : t -> t -> bool - val name : t -> string + val short_name : t -> string val pp : t printer module Tbl : Hashtbl.S with type key = t diff --git a/src/smtlib/dune b/src/smtlib/dune index 318b0cda..0ab68eb4 100644 --- a/src/smtlib/dune +++ b/src/smtlib/dune @@ -1,7 +1,7 @@ (library (name sidekick_smtlib) (public_name sidekick-bin.smtlib) - (libraries containers zarith msat sidekick.core sidekick.util + (libraries containers zarith sidekick.core sidekick.util sidekick-base sidekick-base.solver msat.backend smtlib-utils sidekick.tef) diff --git a/src/util/dune b/src/util/dune index 93411a31..584dcca7 100644 --- a/src/util/dune +++ b/src/util/dune @@ -1,4 +1,4 @@ (library (name sidekick_util) (public_name sidekick.util) - (libraries containers iter msat sidekick.sigs)) + (libraries containers iter sidekick.sigs)) From 5faa1d6ef721a10a4b3a5cbfa0d76ed0213e7f4a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 08:04:56 -0400 Subject: [PATCH 178/182] chore: try to build again --- src/main/main.ml | 4 ++-- src/smtlib/Typecheck.ml | 1 - src/smtlib/dune | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/main.ml b/src/main/main.ml index b407d696..64c1c8b0 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -83,8 +83,8 @@ let argspec = Arg.align [ "--time", Arg.String (int_arg time_limit), " [smhd] sets the time limit for the sat solver"; "-t", Arg.String (int_arg time_limit), " short for --time"; "--version", Arg.Unit (fun () -> Printf.printf "version: %s\n%!" Sidekick_version.version; exit 0), " show version and exit"; - "-d", Arg.Int Msat.Log.set_debug, " sets the debug verbose level"; - "--debug", Arg.Int Msat.Log.set_debug, " sets the debug verbose level"; + "-d", Arg.Int Log.set_debug, " sets the debug verbose level"; + "--debug", Arg.Int Log.set_debug, " sets the debug verbose level"; ] |> List.sort compare (* Limits alarm *) diff --git a/src/smtlib/Typecheck.ml b/src/smtlib/Typecheck.ml index 703912fc..1faed13b 100644 --- a/src/smtlib/Typecheck.ml +++ b/src/smtlib/Typecheck.ml @@ -5,7 +5,6 @@ open Sidekick_base module Loc = Smtlib_utils.V_2_6.Loc module Fmt = CCFormat -module Log = Msat.Log module PA = Smtlib_utils.V_2_6.Ast module BT = Sidekick_base diff --git a/src/smtlib/dune b/src/smtlib/dune index 0ab68eb4..4476ec7a 100644 --- a/src/smtlib/dune +++ b/src/smtlib/dune @@ -3,6 +3,6 @@ (public_name sidekick-bin.smtlib) (libraries containers zarith sidekick.core sidekick.util sidekick-base sidekick-base.solver - msat.backend smtlib-utils + sidekick.backend smtlib-utils sidekick.tef) (flags :standard -warn-error -a+8 -open Sidekick_util)) From ff5cf1239c1cdf8130a155cfcab2cb2e9f0c2675 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 09:29:55 -0400 Subject: [PATCH 179/182] chore: CI on PRs --- .github/workflows/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 09222c24..e4b3df6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,11 @@ name: Build sidekick-bin -on: [push] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: run: name: Build From c7bf4b01e71057709e11d70b94b52b5cce2cab61 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 10:17:44 -0400 Subject: [PATCH 180/182] feat: optional memtrace support --- Makefile | 2 +- sidekick-bin.opam | 5 ++++- src/main/dune | 3 ++- src/main/main.ml | 4 ++++ src/memtrace/dune | 10 ++++++++++ src/memtrace/sidekick_memtrace.dummy.ml | 2 ++ src/memtrace/sidekick_memtrace.mli | 3 +++ src/memtrace/sidekick_memtrace.real.ml | 1 + 8 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/memtrace/dune create mode 100644 src/memtrace/sidekick_memtrace.dummy.ml create mode 100644 src/memtrace/sidekick_memtrace.mli create mode 100644 src/memtrace/sidekick_memtrace.real.ml diff --git a/Makefile b/Makefile index aba219b0..2f4a1990 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ reindent: WATCH=@all watch: - @dune build $(WATCH) -w + @dune build $(WATCH) -w $(OPTS) --profile=release #@dune build @all -w # TODO: once tests pass .PHONY: clean doc all bench install uninstall remove reinstall bin test diff --git a/sidekick-bin.opam b/sidekick-bin.opam index 50202b34..3ebd039f 100644 --- a/sidekick-bin.opam +++ b/sidekick-bin.opam @@ -19,10 +19,13 @@ depends: [ "sidekick" { = version } "sidekick-base" { = version } "menhir" - "mtime" "ocaml" { >= "4.04" } "odoc" {with-doc} ] +depopts: [ + "memtrace" + "mtime" +] tags: [ "sat" "smt" ] homepage: "https://github.com/c-cube/sidekick" dev-repo: "git+https://github.com/c-cube/sidekick.git" diff --git a/src/main/dune b/src/main/dune index e17a42e6..11c38908 100644 --- a/src/main/dune +++ b/src/main/dune @@ -5,7 +5,8 @@ (public_name sidekick) (package sidekick-bin) (libraries containers iter result sidekick.sat sidekick.core sidekick-base - sidekick.msat-solver sidekick-bin.smtlib sidekick.tef) + sidekick.msat-solver sidekick-bin.smtlib sidekick.tef + sidekick.memtrace) (flags :standard -safe-string -color always -open Sidekick_util)) (rule diff --git a/src/main/main.ml b/src/main/main.ml index 64c1c8b0..15a7eae7 100644 --- a/src/main/main.ml +++ b/src/main/main.ml @@ -147,8 +147,12 @@ let main_cnf () : _ result = Pure_sat_solver.solve solver let main () = + + (* instrumentation and tracing *) Sidekick_tef.setup(); at_exit Sidekick_tef.teardown; + Sidekick_memtrace.trace_if_requested ~context:"sidekick" (); + CCFormat.set_color_default true; (* Administrative duties *) Arg.parse argspec input_file usage; diff --git a/src/memtrace/dune b/src/memtrace/dune new file mode 100644 index 00000000..95085599 --- /dev/null +++ b/src/memtrace/dune @@ -0,0 +1,10 @@ + +(library + (name sidekick_memtrace) + (public_name sidekick.memtrace) + (libraries + (select sidekick_memtrace.ml from + (memtrace -> sidekick_memtrace.real.ml) + (-> sidekick_memtrace.dummy.ml))) + (flags :standard -warn-error -a+8)) + diff --git a/src/memtrace/sidekick_memtrace.dummy.ml b/src/memtrace/sidekick_memtrace.dummy.ml new file mode 100644 index 00000000..fd147541 --- /dev/null +++ b/src/memtrace/sidekick_memtrace.dummy.ml @@ -0,0 +1,2 @@ + +let trace_if_requested ?context:_ ?sampling_rate:_ () = () diff --git a/src/memtrace/sidekick_memtrace.mli b/src/memtrace/sidekick_memtrace.mli new file mode 100644 index 00000000..28211463 --- /dev/null +++ b/src/memtrace/sidekick_memtrace.mli @@ -0,0 +1,3 @@ + + +val trace_if_requested : ?context:string -> ?sampling_rate:float -> unit -> unit diff --git a/src/memtrace/sidekick_memtrace.real.ml b/src/memtrace/sidekick_memtrace.real.ml new file mode 100644 index 00000000..f2e78004 --- /dev/null +++ b/src/memtrace/sidekick_memtrace.real.ml @@ -0,0 +1 @@ +let trace_if_requested = Memtrace.trace_if_requested From 041e83139d0f5cc07a0f83d065c88bd61303d935 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 10:31:03 -0400 Subject: [PATCH 181/182] feat: make mtime optional with dummy "tef" backend --- src/tef/Sidekick_tef.dummy.ml | 3 +++ src/tef/{Sidekick_tef.ml => Sidekick_tef.real.ml} | 0 src/tef/dune | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 src/tef/Sidekick_tef.dummy.ml rename src/tef/{Sidekick_tef.ml => Sidekick_tef.real.ml} (100%) diff --git a/src/tef/Sidekick_tef.dummy.ml b/src/tef/Sidekick_tef.dummy.ml new file mode 100644 index 00000000..994edeb7 --- /dev/null +++ b/src/tef/Sidekick_tef.dummy.ml @@ -0,0 +1,3 @@ + +let setup() = () +let teardown() = () diff --git a/src/tef/Sidekick_tef.ml b/src/tef/Sidekick_tef.real.ml similarity index 100% rename from src/tef/Sidekick_tef.ml rename to src/tef/Sidekick_tef.real.ml diff --git a/src/tef/dune b/src/tef/dune index db8f8b5e..a529f1cf 100644 --- a/src/tef/dune +++ b/src/tef/dune @@ -3,6 +3,10 @@ (name sidekick_tef) (public_name sidekick.tef) (synopsis "profiling backend based on TEF") - (optional) (flags :standard -warn-error -a+8) - (libraries sidekick.util unix threads mtime mtime.clock.os)) + (libraries + sidekick.util unix threads + (select Sidekick_tef.ml from + (mtime mtime.clock.os -> Sidekick_tef.real.ml) + (-> Sidekick_tef.dummy.ml))) + (optional)) From 15d86d7c62c78a3c2af040816bb100b8021ef619 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Jul 2021 19:18:42 -0400 Subject: [PATCH 182/182] refactor(sat): use first-class modules instead of records --- src/msat-solver/Sidekick_msat_solver.ml | 48 ++++++++----- src/sat/Sidekick_sat.ml | 30 ++------ src/sat/Solver.ml | 76 +++++++++++--------- src/sat/Solver_intf.ml | 94 +++++++++++++------------ 4 files changed, 124 insertions(+), 124 deletions(-) diff --git a/src/msat-solver/Sidekick_msat_solver.ml b/src/msat-solver/Sidekick_msat_solver.ml index aa35c35a..03963f12 100644 --- a/src/msat-solver/Sidekick_msat_solver.ml +++ b/src/msat-solver/Sidekick_msat_solver.ml @@ -88,11 +88,13 @@ module Make(A : ARG) module P = P module Lit = Lit type t = msat_acts - let[@inline] raise_conflict a lits pr = - a.Sidekick_sat.acts_raise_conflict lits pr - let[@inline] propagate a lit ~reason = + let[@inline] raise_conflict (a:t) lits pr = + let (module A) = a in + A.raise_conflict lits pr + let[@inline] propagate (a:t) lit ~reason = + let (module A) = a in let reason = Sidekick_sat.Consequence reason in - a.Sidekick_sat.acts_propagate lit reason + A.propagate lit reason end end @@ -241,23 +243,27 @@ module Make(A : ARG) let on_model_gen self f = self.mk_model <- f :: self.mk_model let push_decision (_self:t) (acts:actions) (lit:lit) : unit = + let (module A) = acts in let sign = Lit.sign lit in - acts.Sidekick_sat.acts_add_decision_lit (Lit.abs lit) sign + A.add_decision_lit (Lit.abs lit) sign - let[@inline] raise_conflict self acts c proof : 'a = + let[@inline] raise_conflict self (acts:actions) c proof : 'a = + let (module A) = acts in Stat.incr self.count_conflict; - acts.Sidekick_sat.acts_raise_conflict c proof + A.raise_conflict c proof - let[@inline] propagate self acts p ~reason : unit = + let[@inline] propagate self (acts:actions) p ~reason : unit = + let (module A) = acts in Stat.incr self.count_propagate; - acts.Sidekick_sat.acts_propagate p (Sidekick_sat.Consequence reason) + A.propagate p (Sidekick_sat.Consequence reason) let[@inline] propagate_l self acts p cs proof : unit = propagate self acts p ~reason:(fun()->cs,proof) - let add_sat_clause_ self acts ~keep lits (proof:P.t) : unit = + let add_sat_clause_ self (acts:actions) ~keep lits (proof:P.t) : unit = + let (module A) = acts in Stat.incr self.count_axiom; - acts.Sidekick_sat.acts_add_clause ~keep lits proof + A.add_clause ~keep lits proof let preprocess_term_ (self:t) ~add_clause (t:term) : term * proof = let mk_lit t = Lit.atom self.tst t in (* no further simplification *) @@ -375,7 +381,9 @@ module Make(A : ARG) let[@inline] add_clause_permanent self acts lits (proof:P.t) : unit = add_sat_clause_ self acts ~keep:true lits proof - let add_lit _self acts lit : unit = acts.Sidekick_sat.acts_mk_lit lit + let[@inline] add_lit _self (acts:actions) lit : unit = + let (module A) = acts in + A.mk_lit lit let add_lit_t self acts ?sign t = let lit = mk_lit self acts ?sign t in @@ -454,8 +462,10 @@ module Make(A : ARG) ); () - let[@inline] iter_atoms_ acts : _ Iter.t = - fun f -> acts.Sidekick_sat.acts_iter_assumptions f + let[@inline] iter_atoms_ (acts:actions) : _ Iter.t = + fun f -> + let (module A) = acts in + A.iter_assumptions f (* propagation from the bool solver *) let check_ ~final (self:t) (acts: msat_acts) = @@ -906,22 +916,22 @@ module Make(A : ARG) let r = Sat_solver.solve ~assumptions (solver self) in Stat.incr self.count_solve; match r with - | Sat_solver.Sat st -> + | Sat_solver.Sat (module SAT) -> Log.debug 1 "sidekick.msat-solver: SAT"; - let _lits f = st.iter_trail f in + let _lits f = SAT.iter_trail f in (* TODO: theory combination *) let m = mk_model self _lits in do_on_exit (); Sat m - | Sat_solver.Unsat us -> + | Sat_solver.Unsat (module UNSAT) -> let proof = lazy ( try - let pr = us.get_proof () in + let pr = UNSAT.get_proof () in if check then Sat_solver.Proof.check pr; Some (Pre_proof.make pr (List.rev self.si.t_defs)) with Sidekick_sat.Solver_intf.No_proof -> None ) in - let unsat_core = lazy (us.Sidekick_sat.unsat_assumptions ()) in + let unsat_core = lazy (UNSAT.unsat_assumptions ()) in do_on_exit (); Unsat {proof; unsat_core} diff --git a/src/sat/Sidekick_sat.ml b/src/sat/Sidekick_sat.ml index 9ffa03ad..873fb9b7 100644 --- a/src/sat/Sidekick_sat.ml +++ b/src/sat/Sidekick_sat.ml @@ -10,34 +10,14 @@ module type PROOF = Solver_intf.PROOF type lbool = Solver_intf.lbool = L_true | L_false | L_undefined -type 'form sat_state = 'form Solver_intf.sat_state = { - eval : 'form -> bool; - eval_level : 'form -> bool * int; - iter_trail : ('form -> unit) -> unit; -} - -type ('atom,'clause, 'proof) unsat_state = ('atom,'clause, 'proof) Solver_intf.unsat_state = { - unsat_conflict : unit -> 'clause; - get_proof : unit -> 'proof; - unsat_assumptions: unit -> 'atom list; -} -type 'clause export = 'clause Solver_intf.export = { - hyps : 'clause Vec.t; - history : 'clause Vec.t; -} +module type SAT_STATE = Solver_intf.SAT_STATE +type 'form sat_state = 'form Solver_intf.sat_state type ('formula, 'proof) reason = ('formula, 'proof) Solver_intf.reason = - | Consequence of (unit -> 'formula list * 'proof) + | Consequence of (unit -> 'formula list * 'proof) [@@unboxed] -type ('formula, 'proof) acts = ('formula, 'proof) Solver_intf.acts = { - acts_iter_assumptions: ('formula -> unit) -> unit; - acts_eval_lit: 'formula -> lbool; - acts_mk_lit: ?default_pol:bool -> 'formula -> unit; - acts_add_clause : ?keep:bool -> 'formula list -> 'proof -> unit; - acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; - acts_propagate : 'formula -> ('formula, 'proof) reason -> unit; - acts_add_decision_lit: 'formula -> bool -> unit; -} +module type ACTS = Solver_intf.ACTS +type ('formula, 'proof) acts = ('formula, 'proof) Solver_intf.acts type negated = Solver_intf.negated = Negated | Same_sign diff --git a/src/sat/Solver.ml b/src/sat/Solver.ml index 07609129..c341673b 100644 --- a/src/sat/Solver.ml +++ b/src/sat/Solver.ml @@ -1519,28 +1519,34 @@ module Make(Plugin : PLUGIN) let[@inline] acts_mk_lit st ?default_pol f : unit = ignore (mk_atom ?default_pol st f : atom) - let[@inline] current_slice st : _ Solver_intf.acts = { - Solver_intf. - acts_iter_assumptions=acts_iter st ~full:false st.th_head; - acts_eval_lit= acts_eval_lit st; - acts_mk_lit=acts_mk_lit st; - acts_add_clause = acts_add_clause st; - acts_propagate = acts_propagate st; - acts_raise_conflict=acts_raise st; - acts_add_decision_lit=acts_add_decision_lit st; - } + let[@inline] current_slice st : _ Solver_intf.acts = + let module M = struct + type nonrec proof = lemma + type nonrec formula = formula + let iter_assumptions=acts_iter st ~full:false st.th_head + let eval_lit= acts_eval_lit st + let mk_lit=acts_mk_lit st + let add_clause = acts_add_clause st + let propagate = acts_propagate st + let raise_conflict c pr=acts_raise st c pr + let add_decision_lit=acts_add_decision_lit st + end in + (module M) (* full slice, for [if_sat] final check *) - let[@inline] full_slice st : _ Solver_intf.acts = { - Solver_intf. - acts_iter_assumptions=acts_iter st ~full:true st.th_head; - acts_eval_lit= acts_eval_lit st; - acts_mk_lit=acts_mk_lit st; - acts_add_clause = acts_add_clause st; - acts_propagate = acts_propagate st; - acts_raise_conflict=acts_raise st; - acts_add_decision_lit=acts_add_decision_lit st; - } + let[@inline] full_slice st : _ Solver_intf.acts = + let module M = struct + type nonrec proof = lemma + type nonrec formula = formula + let iter_assumptions=acts_iter st ~full:true st.th_head + let eval_lit= acts_eval_lit st + let mk_lit=acts_mk_lit st + let add_clause = acts_add_clause st + let propagate = acts_propagate st + let raise_conflict c pr=acts_raise st c pr + let add_decision_lit=acts_add_decision_lit st + end in + (module M) (* Assert that the conflict is indeeed a conflict *) let check_is_conflict_ (c:Clause.t) : unit = @@ -1826,14 +1832,13 @@ module Make(Plugin : PLUGIN) let mk_sat (st:t) : Formula.t Solver_intf.sat_state = pp_all st 99 "SAT"; let t = trail st in - let iter_trail f = - Vec.iter (fun a -> f (Atom.formula a)) t - in - let[@inline] eval f = eval st (mk_atom st f) in - let[@inline] eval_level f = eval_level st (mk_atom st f) in - { Solver_intf. - eval; eval_level; iter_trail; - } + let module M = struct + type formula = Formula.t + let iter_trail f = Vec.iter (fun a -> f (Atom.formula a)) t + let[@inline] eval f = eval st (mk_atom st f) + let[@inline] eval_level f = eval_level st (mk_atom st f) + end in + (module M) let mk_unsat (st:t) (us: unsat_cause) : _ Solver_intf.unsat_state = pp_all st 99 "UNSAT"; @@ -1866,7 +1871,15 @@ module Make(Plugin : PLUGIN) let c = unsat_conflict () in Proof.prove c in - { Solver_intf.unsat_conflict; get_proof; unsat_assumptions; } + let module M = struct + type nonrec atom = atom + type clause = Clause.t + type proof = Proof.t + let get_proof = get_proof + let unsat_conflict = unsat_conflict + let unsat_assumptions = unsat_assumptions + end in + (module M) let add_clause_a st c lemma : unit = try @@ -1901,11 +1914,6 @@ module Make(Plugin : PLUGIN) with UndecidedLit -> false let[@inline] eval_atom _st a : Solver_intf.lbool = eval_atom_ a - - let export (st:t) : clause Solver_intf.export = - let hyps = hyps st in - let history = history st in - {Solver_intf.hyps; history; } end [@@inline][@@specialise] diff --git a/src/sat/Solver_intf.ml b/src/sat/Solver_intf.ml index 69a2b911..afe2dfc6 100644 --- a/src/sat/Solver_intf.ml +++ b/src/sat/Solver_intf.ml @@ -13,38 +13,49 @@ Copyright 2016 Simon Cruanes type 'a printer = Format.formatter -> 'a -> unit -type 'form sat_state = { - eval: 'form -> bool; +module type SAT_STATE = sig + type formula + + val eval : formula -> bool (** Returns the valuation of a formula in the current state of the sat solver. @raise UndecidedLit if the literal is not decided *) - eval_level: 'form -> bool * int; + + val eval_level : formula -> bool * int (** Return the current assignement of the literals, as well as its decision level. If the level is 0, then it is necessary for the atom to have this value; otherwise it is due to choices that can potentially be backtracked. @raise UndecidedLit if the literal is not decided *) - iter_trail : ('form -> unit) -> unit; + + val iter_trail : (formula -> unit) -> unit (** Iter through the formulas in order of decision/propagation (starting from the first propagation, to the last propagation). *) -} +end + +type 'form sat_state = (module SAT_STATE with type formula = 'form) (** The type of values returned when the solver reaches a SAT state. *) -type ('atom, 'clause, 'proof) unsat_state = { - unsat_conflict : unit -> 'clause; - (** Returns the unsat clause found at the toplevel *) - get_proof : unit -> 'proof; - (** returns a persistent proof of the empty clause from the Unsat result. *) - unsat_assumptions: unit -> 'atom list; - (** Subset of assumptions responsible for "unsat" *) -} -(** The type of values returned when the solver reaches an UNSAT state. *) +module type UNSAT_STATE = sig + type atom + type clause + type proof -type 'clause export = { - hyps: 'clause Vec.t; - history: 'clause Vec.t; -} -(** Export internal state *) + val unsat_conflict : unit -> clause + (** Returns the unsat clause found at the toplevel *) + + val get_proof : unit -> proof + (** returns a persistent proof of the empty clause from the Unsat result. *) + + val unsat_assumptions: unit -> atom list + (** Subset of assumptions responsible for "unsat" *) +end + +type ('atom, 'clause, 'proof) unsat_state = + (module UNSAT_STATE with type atom = 'atom + and type clause = 'clause + and type proof = 'proof) +(** The type of values returned when the solver reaches an UNSAT state. *) type negated = | Negated (** changed sign *) @@ -52,22 +63,8 @@ type negated = (** This type is used during the normalisation of formulas. See {!val:Expr_intf.S.norm} for more details. *) -type 'term eval_res = - | Unknown (** The given formula does not have an evaluation *) - | Valued of bool * ('term list) (** The given formula can be evaluated to the given bool. - The list of terms to give is the list of terms that - were effectively used for the evaluation. *) -(** The type of evaluation results for a given formula. - For instance, let's suppose we want to evaluate the formula [x * y = 0], the - following result are correct: - - [Unknown] if neither [x] nor [y] are assigned to a value - - [Valued (true, [x])] if [x] is assigned to [0] - - [Valued (true, [y])] if [y] is assigned to [0] - - [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number) -*) - type ('formula, 'proof) reason = - | Consequence of (unit -> 'formula list * 'proof) + | Consequence of (unit -> 'formula list * 'proof) [@@unboxed] (** [Consequence (l, p)] means that the formulas in [l] imply the propagated formula [f]. The proof should be a proof of the clause "[l] implies [f]". @@ -91,39 +88,46 @@ type ('formula, 'proof) reason = type lbool = L_true | L_false | L_undefined (** Valuation of an atom *) -(* TODO: find a way to use atoms instead of formulas here *) -type ('formula, 'proof) acts = { - acts_iter_assumptions: ('formula -> unit) -> unit; +module type ACTS = sig + type formula + type proof + + val iter_assumptions: (formula -> unit) -> unit (** Traverse the new assumptions on the boolean trail. *) - acts_eval_lit: 'formula -> lbool; + val eval_lit: formula -> lbool (** Obtain current value of the given literal *) - acts_mk_lit: ?default_pol:bool -> 'formula -> unit; + val mk_lit: ?default_pol:bool -> formula -> unit (** Map the given formula to a literal, which will be decided by the SAT solver. *) - acts_add_clause: ?keep:bool -> 'formula list -> 'proof -> unit; + val add_clause: ?keep:bool -> formula list -> proof -> unit (** Add a clause to the solver. @param keep if true, the clause will be kept by the solver. Otherwise the solver is allowed to GC the clause and propose this partial model again. *) - acts_raise_conflict: 'b. 'formula list -> 'proof -> 'b; + val raise_conflict: formula list -> proof -> 'b (** Raise a conflict, yielding control back to the solver. The list of atoms must be a valid theory lemma that is false in the current trail. *) - acts_propagate: 'formula -> ('formula, 'proof) reason -> unit; + val propagate: formula -> (formula, proof) reason -> unit (** Propagate a formula, i.e. the theory can evaluate the formula to be true (see the definition of {!type:eval_res} *) - acts_add_decision_lit: 'formula -> bool -> unit; + val add_decision_lit: formula -> bool -> unit (** Ask the SAT solver to decide on the given formula with given sign before it can answer [SAT]. The order of decisions is still unspecified. Useful for theory combination. This will be undone on backtracking. *) -} +end + +(* TODO: find a way to use atoms instead of formulas here *) +type ('formula, 'proof) acts = + (module ACTS with type formula = 'formula + and type proof = 'proof) (** The type for a slice of assertions to assume/propagate in the theory. *) exception No_proof @@ -418,7 +422,5 @@ module type S = sig val eval_atom : t -> atom -> lbool (** Evaluate atom in current state *) - - val export : t -> clause export end