From e2efc4b8cd43ac785bce7eaa2c03c18470c18a04 Mon Sep 17 00:00:00 2001 From: arran Date: Fri, 20 Oct 2017 22:21:43 +0800 Subject: [PATCH] Import of code from J Slaney web-page. url: http://users.cecs.anu.edu.au/~jks/software/magic.2.2.1.tar.gz --- README | 47 + dat/BTW.show | 19 + dat/CUT.show | 15 + dat/FDL.show | 21 + dat/LOG.show | 21 + dat/MEN.show | 41 + dat/OUT.show | 22 + dat/WFF.show | 22 + dat/ba.16 | Bin 0 -> 39 bytes dat/dl.10 | Bin 0 -> 1397 bytes dat/dln.14 | Bin 0 -> 3204 bytes dat/l.8 | 1 + dat/ln.10 | Bin 0 -> 7119 bytes dat/po.6 | Bin 0 -> 4265 bytes dat/pon.7 | Bin 0 -> 5146 bytes dat/pont.8 | Bin 0 -> 10128 bytes dat/pot.8 | Bin 0 -> 214996 bytes dat/tn.16 | Bin 0 -> 282 bytes dat/to.16 | Bin 0 -> 278 bytes src/MaGIC.c | 142 +++ src/MaGIC.h | 61 ++ src/Makefile | 98 +++ src/Mdef.h | 139 +++ src/Mglob.h | 131 +++ src/Mproto.h | 301 +++++++ src/Mtypes.h | 187 ++++ src/RM.c | 230 +++++ src/RM.h | 295 +++++++ src/RM_input.c | 272 ++++++ src/RM_output.c | 710 +++++++++++++++ src/axiom_tests.c | 583 +++++++++++++ src/axioms.c | 368 ++++++++ src/axioms.h | 125 +++ src/dialog.c | 568 ++++++++++++ src/embedded.c | 11 + src/getjob.c | 1298 ++++++++++++++++++++++++++++ src/hmi.c | 329 +++++++ src/hmi.h | 17 + src/homomorphic.c | 11 + src/image.c | 9 + src/isom.c | 528 ++++++++++++ src/logic_io.c | 835 ++++++++++++++++++ src/logic_pretest.c | 184 ++++ src/logic_set.c | 530 ++++++++++++ src/logic_test.c | 362 ++++++++ src/mp_parse.c | 266 ++++++ src/nt_homomorphic.c | 11 + src/one_plus_t_gen.c | 94 ++ src/onegen.c | 152 ++++ src/setup.c | 464 ++++++++++ src/sub_irr.c | 202 +++++ src/u2p.c | 50 ++ src/u2pic.c | 76 ++ src/u2tex.c | 54 ++ src/vntr.c | 1956 ++++++++++++++++++++++++++++++++++++++++++ src/vntr.h | 343 ++++++++ src/wffs.c | 499 +++++++++++ 57 files changed, 12700 insertions(+) create mode 100644 README create mode 100644 dat/BTW.show create mode 100644 dat/CUT.show create mode 100644 dat/FDL.show create mode 100644 dat/LOG.show create mode 100644 dat/MEN.show create mode 100644 dat/OUT.show create mode 100644 dat/WFF.show create mode 100644 dat/ba.16 create mode 100644 dat/dl.10 create mode 100644 dat/dln.14 create mode 100644 dat/l.8 create mode 100644 dat/ln.10 create mode 100644 dat/po.6 create mode 100644 dat/pon.7 create mode 100644 dat/pont.8 create mode 100644 dat/pot.8 create mode 100644 dat/tn.16 create mode 100644 dat/to.16 create mode 100644 src/MaGIC.c create mode 100644 src/MaGIC.h create mode 100644 src/Makefile create mode 100644 src/Mdef.h create mode 100644 src/Mglob.h create mode 100644 src/Mproto.h create mode 100644 src/Mtypes.h create mode 100644 src/RM.c create mode 100644 src/RM.h create mode 100644 src/RM_input.c create mode 100644 src/RM_output.c create mode 100644 src/axiom_tests.c create mode 100644 src/axioms.c create mode 100644 src/axioms.h create mode 100644 src/dialog.c create mode 100644 src/embedded.c create mode 100644 src/getjob.c create mode 100644 src/hmi.c create mode 100644 src/hmi.h create mode 100644 src/homomorphic.c create mode 100644 src/image.c create mode 100644 src/isom.c create mode 100644 src/logic_io.c create mode 100644 src/logic_pretest.c create mode 100644 src/logic_set.c create mode 100644 src/logic_test.c create mode 100644 src/mp_parse.c create mode 100644 src/nt_homomorphic.c create mode 100644 src/one_plus_t_gen.c create mode 100644 src/onegen.c create mode 100644 src/setup.c create mode 100644 src/sub_irr.c create mode 100644 src/u2p.c create mode 100644 src/u2pic.c create mode 100644 src/u2tex.c create mode 100644 src/vntr.c create mode 100644 src/vntr.h create mode 100644 src/wffs.c diff --git a/README b/README new file mode 100644 index 0000000..d45ccc9 --- /dev/null +++ b/README @@ -0,0 +1,47 @@ +MaGIC (Matrix Generator for Implication Connectives) is a tool that +produces small algebraic models of non-classical propositional logics, +especially of the substructural sort. The logics are considered as +Hilbert (axiom) systems. Interaction with MaGIC is through a plain +text "terminal" window and is based on a menu. Help is available from +the menu. + +Synopsis: + magic [-b ] [-t] [-x] + +Options: + +-b runs the program in batch mode with the specified file + as input. The file should be one previously saved with the "Save" + option from a previous run of MaGIC. This allows output to be piped + through another program such as one of the utilities provided with + MaGIC. + +-t produces "terse" output, mainly meaning that it suppresses system + calls such as those that clear the window each time the menu is + printed. + +-x causes the dialogue to be in a form suitable for "xmagic", the (now + defunct) X Windows interface to MaGIC. It is the result of a + message from Gustav Meglicki to John Slaney to the effect that the + machine-readable I/O was "insufficiently user-hostile". Since + xmagic is no longer supported, this option is a mere relic. + + +Installation: + + The main directory containing this README should have two + subdirectories, one containing the program sources and the other + containing various data files used by MaGIC. To make, go to the src + directory and edit the Makefile. Only the top few lines require any + change - mainly it is a matter of filling in the directories where + the data files and the binaries are to be kept. Then "make" should + make the programs and "make install" should simply move them and the + data into the specified directories.As usual, "make clean" removes + the .o files and possibly other junk. + + +Problems in installing or using MaGIC should be reported to the author + John.Slaney@anu.edu.au + +It is also recommended that if you install MaGIC you let us know, so +that we can inform you of any bug fixes or updates. diff --git a/dat/BTW.show b/dat/BTW.show new file mode 100644 index 0000000..1db52e9 --- /dev/null +++ b/dat/BTW.show @@ -0,0 +1,19 @@ + B Axioms 1 - 8 as for FD. + 9. ((A -> B) & (A -> C)) -> (A -> (B & C)) + 10. ((A -> C) & (B -> C)) -> ((A v B) -> C) + + Rules 1 - 2, 8 - 12 as for FD. + Rules 6 and 7 replaced by the above axioms. + Rules 3 - 5 replaced by: + + 4'. A -> B ==> (C -> A) -> (C -> B) + 5'. A -> B ==> (B -> C) -> (A -> C) + + DW is B with rule 8 replaced by the axiom form: + + Axiom 11. (A -> ~B) -> (B -> ~A) + + TW is DW with rules 4' and 5' replaced by the axiom forms: + + Axiom 12. (A -> B) -> ((C -> A) -> (C -> B)) + 13. (A -> B) -> ((B -> C) -> (A -> C)) diff --git a/dat/CUT.show b/dat/CUT.show new file mode 100644 index 0000000..849cd86 --- /dev/null +++ b/dat/CUT.show @@ -0,0 +1,15 @@ + A user-defined connective may optionally be marked with a "cut". + This serves to control backtracking much as in Prolog. MaGIC will + not generate two models whose most significant difference is in + a connective with a cut. + + Thus for example, where `A' is a primitive 0-adic connective and + a rule + A v ~A / + has been added, MaGIC will normally generate failures of the law + of the excluded middle. However, if `A' has been marked with a cut + then MaGIC will generate models in which that law fails: if it + fails for more than one element of a model, only the smallest such + element will be reported. + + diff --git a/dat/FDL.show b/dat/FDL.show new file mode 100644 index 0000000..3d740a7 --- /dev/null +++ b/dat/FDL.show @@ -0,0 +1,21 @@ + FD Axioms: 1. A -> A + 2. (A & B) -> A + 3. (A & B) -> B + 4. A -> (A v B) + 5. B -> (A v B) + 6. ~~A -> A + 7. F -> A + 8. A -> T + + Rules: 1. A -> B, A ==> B + 2. A, B ==> A & B + 3. A -> B, B -> C ==> A -> C + 4. A -> B, B -> A ==> (C -> A) -> (C -> B) + 5. A -> B, B -> A ==> (B -> C) -> (A -> C) + 6. A -> B, A -> C ==> A -> (B & C) + 7. A -> C, B -> C ==> (A v B) -> C + 8. A -> ~B ==> B -> ~A + 9. A ==> t -> A + 10. t -> A ==> A + 11. A -> (B -> C) ==> (A o B) -> C + 12. (A o B) -> C ==> A -> (B -> C) diff --git a/dat/LOG.show b/dat/LOG.show new file mode 100644 index 0000000..7013cb6 --- /dev/null +++ b/dat/LOG.show @@ -0,0 +1,21 @@ + Axioms for the stronger logics are as follows: + + EW is TW plus ((A -> A) -> B) -> B + + C is TW plus A -> ((A -> B) -> B) + + T is TW plus (A -> (A -> B)) -> (A -> B) + + and (A -> ~A) -> ~A + + E is T plus ((A -> A) -> B) -> B + + R is C plus (A -> (A -> B)) -> (A -> B) + + CK is C plus A -> (B -> A) + + S4 is E plus A -> (B -> B) + + Axiomatisations of fragments of these logics result by + selecting the axioms and rules which explicitly mention + the appropriate connectives. diff --git a/dat/MEN.show b/dat/MEN.show new file mode 100644 index 0000000..e505f02 --- /dev/null +++ b/dat/MEN.show @@ -0,0 +1,41 @@ + + Menu options: + + a: Add a pre- or user-defined axiom or rule to the logic. + + b: Specify a formula to fail in the matrices found. + + c: Define a connective. E.g. + defined ~a -> b. + + d: Delete either an axiom, a user-defined connective or the bad guy. + + e: Exit from MAGIC. + + f: Choose the fragment (pre-defined connectives). + + g: Search for matrices. + + h: Display this page. + + i: Change the output formats. + + j: Set a condition on which to jump out of the search. + + k: Re-initialise everything. + + l: Change your mind about your favourite logic. + + m: Display information about MaGIC (version number etc). + + n: Change number of processes (parallel version only). + + o: Re-order dyadic connectives for scope or significance purposes. + + p: Select which tables to print when matrices are found. + + q: Quit MaGIC (equivalent to e). + + r: Read a job specification from a named file. + + s: Write the current job specification to a named file. + diff --git a/dat/OUT.show b/dat/OUT.show new file mode 100644 index 0000000..a1d4190 --- /dev/null +++ b/dat/OUT.show @@ -0,0 +1,22 @@ + + Output formats are as follows: + + "Pretty": Matrices are printed in human-readable form. + This is the default for tty output. Each + matrix is followed by an assignment of values + falsifying the "bad guy" if there is one. + + "Ugly": Matrices are printed in a standard machine- + readable form with -1 as break character. + No headings, comments, etc. are included. + + "Summary": Only the numbers of matrices are printed. + This is useful for checking the size of a + job before getting a printout of matrices. + It runs slightly faster than "pretty" or + "ugly". + + "None": No matrices are printed, although the final + statistics are still sent to the tty. This + is the default for file output. + diff --git a/dat/WFF.show b/dat/WFF.show new file mode 100644 index 0000000..ae9873d --- /dev/null +++ b/dat/WFF.show @@ -0,0 +1,22 @@ + + + The following conventions govern typing of formulas. + + (a) Variables are any (upper or lower case) letters other + than those (such as 'o' and 'v') used as connectives. + 't', 'f', 'T' and 'F' are sentential constants. + + (b) Parentheses are as normal, with the features that + connectives of smaller scope bind more tightly than + those of larger scope, and that except as scope and + parentheses dictate, association is to the left. + + Unary connectives have minimal scope. The binary ones + in order of increasing scope are o & v -> followed + by user-defined ones. The scope order may be changed. + + A period may replace a left parenthesis whose right + mate is as far to the right as is reasonable. + + (c) Spaces are ignored. RETURN terminates the formula. + diff --git a/dat/ba.16 b/dat/ba.16 new file mode 100644 index 0000000000000000000000000000000000000000..9483121cd9c9beb17e14f893212c7eaeefdab38e GIT binary patch literal 39 xcmV+?0NDRu^t(n>|En4Asu2UCs`~$5Rn5$Zcz8hr1OC;FcU9Ghhyeiu00O1K5`h2! literal 0 HcmV?d00001 diff --git a/dat/dl.10 b/dat/dl.10 new file mode 100644 index 0000000000000000000000000000000000000000..75b589ad489c9120af7ff6fac037cbf253fb9a58 GIT binary patch literal 1397 zcmYLJO>7%Q7A*Z@%Z7laBs$*H8#H~+{-Shug8pd ztm)u~fK2nERbBY7H3&@-CL^kZt!*uOz`wk2 zZ%jSOY8|HYU)JUm-i+44#>YSV^Kb1$Lc8JN=wcxe)HFRY1K;CjxKr0zb4weukODqs zU9YUCtVrm>?Dp#V+Z6?2{n%mdgW0pG7S)DbH~nhrNh_yM)*_+P22NHBY*00XYYJ~x zPZC7)gj9t!iDi?Ahw9d-1zv^2{0j9_1s%BlJPS%vh6x<8P}U~*dBa5Tk$&0h%Ww!n zvk&33Ul9xya&lpV31Z2%@B#5iXD?WeVkwA>73~7+^b5i!h(?~0EWnWv%|Qy~n}Vdm z1N`;5cWL42S96I!md`FmLLKj4{QGW|zn6R6%bv0D-_5{Zc)k)%ZjE%1i3`5O+OLiA zmS$LysD*B8!L)-rQD%*dmEiA__VDEb_i_`JY&4M-&Mu11$@!Gn*RR#I@mi#32=hwt zN)bQhFRv`?H?mq2a=8xwel|uh;lPcf)0Pcl5(ZXcl*Y$JO*s*$B3j zAkGBDkL>tZ=ay!)q@XaMD2anz7HsQZ$l@76+vm^fhN!8MCXFBb&cp{&P@Fp2fNvfe zu!b=92;SGPpHnb+u@1lJKgcJ%fnKBp={*ma&Y(oHt}a6?d1$*8w4)d=;Rr> zs%C^t8W;8WKj+S*`W#G)O>_&lR}@E@)u7#+oR`frr`pihrZ}`p%Agu|ARR8jt%3h? z)0+}j>*>L)lCol0$+Mw!PPuh>uWWSGdLVNw9=r%944%xAb8Z09U?*=31(A7_ z=pvefjub~4yj#c&I?``N5bWirD?_u0aYTa~y+KL({;1@s!8Y}jGtv;P5uGAP1X49m zAtEnjh-e7v(_p=AJ-wyH=<5Ner>%B2@REEX8N`XJ7|<2dMsOMfI_S$7#6Z4-lGQhh HIIsQ>pjdz? literal 0 HcmV?d00001 diff --git a/dat/dln.14 b/dat/dln.14 new file mode 100644 index 0000000000000000000000000000000000000000..cefb1df6bb6e153747cee32ec3a60104f642e157 GIT binary patch literal 3204 zcmai0U2GKB6`s*ZzI|wR=&OtO8ug*Cl}acz_Rb<#qA!(EswyvaNgG<+zl62~FUHh9=X~Ee_s)^%PmRGDmV2w(xej4(Vtsb_Mfw8aRsZg?1&~c86C2E)PLOpM z1}W_<0aSBSx}NwL-v7b%%<9>LT5#;@-5>XNcvY~=w{*JKVwF zf5C^4pQqWpN+b*T(=1vhG~=in_D3t$NFB{<&`oJ)*YT4zxSTz8gT zD^H6HlSXMgF=T2bhNZwI7WLDH*^vi(bB|HwVr42?>E>bBNnYP-PraIU&M_nqste}=n~?`S_U zzi6p>C_kp(vww5--P6I;g>$L*$%ff!X+uiF6fRrzPX6)AAiRC@9er`Ux0~dZ-3qs1 z?$i3dWAoyxmD|ZV?NmBpsr!|H!3VFFhwq%^1CPISV1fAQ6@`8g~gtLoBDnGwg zW*6TS=Xy$H)l6=00c*^jAB4N?s165>%fwT<(%Nyw0}-MeZY7u&GpwPDal;}drPI_b zDUSWnMlcx^X-OL~6Gd5}lpcRs7=UG?L?We2W&>`# zfe#)P6Re2Y^u#p4M)@6Hg)QUr$snD~YZ<92V%J8dLl>T4!4F$I0>jraG$7#6HYl@O z(3UpTiM2s^0V@;J@PJ)FZBj+jP?B*rxa7)Cie$n0SQ}E51vejVz%8iJNMnv(i?VSXsUv;)hE0f~jh(!;Rl6pevREQ~T;BtU$Tn;Fogd40# zYwgSOECKG;SwsUO)G>vc9Wt!>#P0=xapuq>si{L!G?$^w?z{`<;S*M(kqk4FX;M^= z$IEf3Eti*BjW$T6W|c0P3@*gU0B3^oCad){444Qr(%BVwi|wK%OfsYFZXP$b4D?~E zElJ|2*k-MV!8AJfY&UA+ayM$TRIqaesm*m-p9~LXtz?@v7<|pmt<1;iRLIu&sF{od z1^nd?5YE&Kpa1>QOTXMOzV&5KKSPGJvuaVX;0W-k`GD~|_b!P;_a|UHP);c?qTC*Y z_Iv!;Qf?M^gx|m68ArR;^oQgJgA!*GN4A)?-&OgNSpMw4=d8pOYJgVg4 zdn05FZz{B=fAa>UnUhLWr#%omLS7sg$cfN|8YrWv5@v zZKxZ{NIT|Y__AOAEXG_+2Kq%3Xsf8w4ve@0zTdEb;w2{;sBHXTVQ zM1ECZ{JpR|h96P}P_Ex2T&t^G8B_Wcs_5GoUWRQ9Cv^?}2PQsETRgJZ2!|rmOtO`S z?CC9R=9*EXHEf&wwrc=JmiJY0Bv#-7>=9NLU6XCoat#xWn;Bh$DL~gY)W$P%43j%Z z@&+r#%xT962&!!L11Jw)I$1Yf!m(cdlE16aC*y*q1>4] ku@~}btpg>9:8߷_O'LJsÃ@tbO?324/ݟO [ƆC鱡@u~lhp??>4ߟώGÃ!!F _HW22"]2bЀMẗ́7#53F 4"]3iĠ7a5 ?).2h@Lfʘ!ʤ 4p@Lf̐AGgEs 4p@0MpȀՉtՈ!ʤ˦ 4p@LlΘ!蟥IM2p@,Nl!߅HM17"]7cĐw1uG п 0fȠ2)c 8w&]7g̐AҤ&2h]t1C ?.17#]7sĐg1e ?.9rp ߕKM7dwqCK7dQuѠWUF 8j]8iUQ ?[.5huQ ?.4j]7aԠTI п[5jؠwuG 8n]7aܰAwu 4p@ݺlؠwu 4p@ݺpڰAޭ 4p@ݺp޸a[6hu п|7bؠ-ו# 蟶I2h@Mtޘ!߾I׎2h@]t޸!ZO4p@xպx֨aZO5huQ[O6huq ?.8nؠ#3F пN4bЀuu _ZWO5huQ пn6jؠױG 4p@պzިa_\WO9huu п9rؠ!F ߏH1d@>L~˜!ˤ만2h@L~Θ!Ӥ'2h}tqCߗKO7dwy п8nvi ?.7nؠF 8j]?iԠF 4p@>ֺ~Aí'6h}uq пo6nؠ 4p@>޺~a߇\O9h@>~a߷\O9lz!שՓF8z]=mAc'4{]=zؠF4p@>~a߷^O=l 8{]?{Aç'2_>]?eA ߇_O?lI 8~]?mA'6hua߇O?|Ѐ}w ?.>h@~4_8p@2frȁ3#?/9xm~đogkg:xm~䑃N;ti~ok:xq~ԡ׎:x~ܱCпٯ9ty~ԡ;tC߇O9p@>f~ȁ33G8_?a':x}~ԱCпۯ;vy|~':p@~~׏:p@8xɣп=|у߇O=x}уO=xп0~~xt# H1h@LʐAgs 4&?i6ic 8"?o7yc 82?m䐁 8j?iUY 8k?qԠF 8n?mؠ 4p@?޺aKO2h@?׺A'6hu Z5l# \9l#2h@_!6huQ[7l# ^=l 8?? 8?}8p@fȡ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 E9~{Z6<^TWy literal 0 HcmV?d00001 diff --git a/dat/po.6 b/dat/po.6 new file mode 100644 index 0000000000000000000000000000000000000000..1b2b0c390af6f767b7d8eec0f7e389e815cb0cca GIT binary patch literal 4265 zcmZ8kdpOkj8gEZ~cy?WSl$A8gVI8!J4y{UJNsh&;qt+@`x{z!Uh1|yO@kq8>m0D~U zM5Qfjbs@Jr9cxkAacgy(=CRlrrDTnUVP?+f{r-kJ=bw4r-}n1|-^=H{&ZRq&^>rJ5 zTo*6*)kyqyc8Yjj;1$XEZ@Es1-`cGdlyt^_yJ%d>3q1A|XO|Oieq>R1rf8GkU)ADp z;YewFjkrX#M>@3JZbpi6hFROtI4^5+&T5M`as2!Fp}DJV+Qh?;&TsCUiQ6r0;ysfU z1BM~ZRj$OW^I?P>N-8qzi&%PBY!A+YD2G-=`cbxrZuIH@!WOV!sn0F@Buu6Th zag^Cv^%=)7IGVtF7!0R)@?co5#HV}{2v1o8@B6*X*+Op_J&|<6>34gMc? zd3w$ymHSq_uZ*jFu;W^uc7@ckIz!kCQDS z*6&4__mPT(+S9sAvu}C0nW&$H+1>XX<9Ki6X*(U*Fv;ju{Kk{$q>j z2Y*pa))~9xG&F5lJXdSM9QZX9=Qz33BCY(0{nvbFW#k2bXlJOkV=k_GqL$9r*`aLzg+pzd>TN}`69SWV+Q;1! zrU1@{g5zLMEfe(izjRnZ8a7_mzwLfa9|2FSAX8x6@nxS8~jj!gN{x$~xv) z%(D5g@_N96i*HrkBnML%jao)cJLr#3FCOKfv`YE(XAj@v~THDbpO~==;Pk%>XKU4Bp}$8s~rmP^FX)J!q6n0bIX?|?9k zT2%&Nc2>s4h_SCEcBW2hf&yaHyn{9+Yh)ByO3Wv~Z%5n^NT=LE|TP#r3&Slsrh3aQUj z1$Z8CcQHvtuiS1w?@;<7Pll?{7j1jHeZoSGt)`Z<70GDK=)>C<8~9nY3z|e|qYr#= zwf)VjV-dJw%|Ls@b>=>LV(_K*kmgIPr<#4S@GP{}3wJIt{4;Wzy+uJ`E)ZsMUhrM0 zL6xYA$Ur6gurfc#8q3SY1lATmu4!qa2Q>S5sgvTToJ);frb`!a)>3Jncz67h0Z68Z zyyHDUqX`2gtDyl0U!HUOorzIKmUZ@0tzP#tja`6xpaXgWju(FD{WYg`Ns;w?$8tKT zEyg)gEKApWZt~)W49BYkFUF8An@i?SZ`%9&y4Uu4rqC-+gLk_00u2+E0m^mcHM!@u zUOt}al)O;maEUs_O%5Vr<{pqqy`suVcP@qc(3IyiUb{-DOHB%cFWl+DSny7NjK`xe z-X?<{zt>R&-Va z5(woV7H@|&-RN>C_3;e@tg6V2Qf|uY{;}oak(v|SFe3fODmz$lAO$O72`2Hf+=J)b zl>QuQ#I6LpF&>(#tf^pNf?FU5+$@@h<`o>K6sz>U3RZfos%jQdXPSi|ZCuz_d5*G% z*lYhM1ZqhXNl(5v>w+}CJ49p*%aHM?sa%;CP5!(rH`MRN?GQGrAY+E&wqMS1gP5Th z69nt(oY4Urf!&o}h8+iRBCe9rfLNTeyj?b_nP!i?0dQC_H6XqXtHNJl6W73i;$STW zzLrN|thA+`iCKe)3ph!X3^Ha3759}1CX)g&kb)#f+kuG5n}`;pr1jLfC`_r$K^ueF z_MKdqPicRjHvk96s(8K{_gY_P5Gp+eOx;*4s&Tc|bfrCKMYgMpy@EWmOnVx}6^A9( zj+#6!(k&qi&7;oKh-oRfnWuC%iMr5$vp$^BItH;FE}iHSEd?sPF!t4G98~AaFK%IR z1P7KEGr$P!)NBt%wkD ztg=&GuZPm5&#BOu2qi>tR7^Z+Jfo|*)wE^><r z)oH#ndr{>=NqQ)KP{~>{s7X)&8a!%s2kM7*8hi;F+~p4hhAH={?e4L<{v&|b3+vQY9n^U;L>UK=l;{i$4|3+ zwg!`HcLztg8LTM;Mcciu{J}Ho<<)hZsjl%!>VEv^D=hdeu@AT634DaRS5L3Rjz?ciZ@|HH%lzasz7n76 z>3R%C)LkKnPG08dYWQpFNYMIV2g@n6PIaqR#A literal 0 HcmV?d00001 diff --git a/dat/pon.7 b/dat/pon.7 new file mode 100644 index 0000000000000000000000000000000000000000..1bc685c040619709f53469e4117b3b8f0bd78ca9 GIT binary patch literal 5146 zcmZWsdpwkR`&N!Qj8lcuIJF%WI!;a#YN#x2+xvF1a%j`RGNKS>ehgvBVQZIi7?QQU zok(pI29qRd9Ja+$3|m8FFyl1F%aEP`2+(gcK*UNU!+}r{xD?J)L*~icE^KbYM=Y;S0$gVBT z!7mmK@Sdl%hp6jn4@?OkaKgT>FK+zLI_)@n#t>XvrYFEf+1HJh>4_8;_i{?tWA)N? z=~v+cRes zxje!tIe{I$-)i`gi-Ppx)oOw8L4T=w>8nD4Ev0K)a^y2se;QOuc(F)sE>h@$)1*1d z31)@1l+CG3rk91@#<*AoK5yzWJPIsr8?ekwOwbzOa3`kA4M$GjZSk?x)i!JHfg9mM z4}ARd&U26T-_$wYNXU$)h&)s2jcosInTaav2RQwl3t{?8y_;D*Lu0|ax|{x8Q0KMA z^kp6cZt$kdZE>Nc@6K3dCdF;hwo;xGR^o{n%+53L`aAIYp|OjX4IhwbcMrSVbEMK4 z?HuWi@}nD;YM0ACk)m|uPFhEt$e#L;)jz~17;1d-xpQ#OJtr#tVSPRO+%|okS?sex zgca%ye?U=#@4fnR`r_Z*q>X{BT>*t~5qm~_w zb&j`AN**bPmLKz9sw0-XkN~JpHSl@7u(BKKudksuh1skMSVJpGtGGd-;5FCM9)+SW z=Hit7O45WmB70(L5wqr~%cMK1omq3te6_d;odYOtQy#lGnqf*yC}69xkV!ohKHff40cwAwEF3 ztQ=B&jq4tj-4gDD!wo;2E^6^E&bV8*Ki_qa%Wfj|VLj{P=nk8!INFT_6`A+}zpvP& zv{l<`@m!S1lMbHm+C$!tXsTyJ!AWtkMd+oGhIplj@}n+%>7(AwJCm+D_8pPx6I_zknn|g`Yg$}$ zc(N6^!@DWHpW6HFLr*1Q7|UY= zDeET%n3(x~A}I(YXQtM9ZC-xC6q**!obGg0@-?wIG#64q;b2+Sf#?yyIsqrU|xH}u^X)3#ua7vRO>z)+G{cGN^XQ#Dh%JRWJha62~jpgQMrY6R?Uj{iJ z+3oLI>g#GM%b%3ozf+k1b9!9#F01t>+O=B$Cz=z^3TCFKCMWp3@v+gN&w~T~eeB*3 z-G6ob`A2K>>sPha6=jc$f6L3xyq>1wt-hNoii#3OL{Lh^^<>ltO z-zC;qCyz)JRodAJpIHma2w__;SXw%&s5aB7Cym4(aw(p8%6F@xJPf!>Bk`AaPslHC! zw_lP<*!#rN}pcbj@_g3&0@h64^hxmjR{2NVqfNB!)s&bF4u24>BZlEVD-=d5?~~akA4{*9QgP*ySJnHRrURQzZKrjQ}K4&EvAT~z_}x4FAKtF!lo}xhVU

cAR(ct?S8;Dbu+oCQC@r`~ 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 EKiGr3od5s; literal 0 HcmV?d00001 diff --git a/dat/to.16 b/dat/to.16 new file mode 100644 index 0000000000000000000000000000000000000000..6fc16a8fa2a989aa331e610e28ac3445abba063e GIT binary patch literal 278 zcmXwzF;2xW5JcCwa2MYoM&bapR54eg`vr;}QBYFQlcNwiI*!181&J~d4T~^47@}-5 zyQ4RwJ%7B%!x>)RubzRvYnu;XdAakxc|+aC*b5J~n!x9n%F%_xWd2VqN#~&KIyTMe zA5iBzVvgk|uC|-(1$}KUpd;oaUWdp|sLgf{RNyCRz%8Brj}eTd$Ot~jT+Re zQ>#X)r>-&$ZEx(LqCl{MF(HFMN`-`0V!G5F+H}E)s7We2z^W6^A;;P)#3oxh`30Lf By0HKN literal 0 HcmV?d00001 diff --git a/src/MaGIC.c b/src/MaGIC.c new file mode 100644 index 0000000..691ac27 --- /dev/null +++ b/src/MaGIC.c @@ -0,0 +1,142 @@ +/* MaGIC1.c V2.1 +* +* This is the serial version of MaGIC intended eventually to be +* compatible with xmagic version 2.1. It requires such +* structures as JOB to be defined in MAGIC.h. +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + + +#define TOPFILE +#include "MaGIC.h" + + +static boolean interrupted; + + + +int main(argc,argv) +int argc; +char *argv[]; +{ + int i, option; + boolean batch; + char batchfile[80]; + extern char *optarg; + void timesup(), intrupt(); + int getopt(); + + noclear = false; + batch = false; + filing = false; + xdialog = false; + + while ((option = getopt (argc, argv, "b:tx#:")) != -1) + switch( option ) { + case 'x': + xdialog = true; + case 't': + noclear = true; + break; + case 'b': + batch = true; + i = 0; + while ((batchfile[i] = optarg[i])) i++; + } + + theJob = (JOB*) malloc(sizeof(JOB)); + signal( SIGALRM, timesup ); + interrupted = false; + signal( SIGINT, intrupt ); + + strings_initial(); + tests_initial(); + logics_initial(); + job_defaults( batch ); + perm_initial(); + + while ((i = dialog(batch,batchfile))) + if ( i > 0 ) { + if ( !batch ) { + printf("\n Searching.....\n"); + fflush(stdout); + } + job_start(); + subf_set(); + interrupted = false; + alarm(theJob->maxtime); + + if ( newsiz() ) + do if ( pre_set() ) { + setperm(); + transref(&(tr_par)); + } + while ( newdes() ); + + job_stop(batch); + alarm(0); + if ( batch ) exit(0); + } + if ( !noclear ) { +#ifdef __CYGWIN__ + puts( "\033[2J" ); +#else + system("clear"); +#endif + } + return 0; +} + + + + +/* +* Action on signal from alarm. +*/ + +void timesup() +{ + tr_par.done = true; +} + + + +/* +* Action on signal from ^C or whatever SIGINT may be. +*/ + +void intrupt() +{ + tr_par.done = true; + if ( interrupted++ ) exit(3); +} diff --git a/src/MaGIC.h b/src/MaGIC.h new file mode 100644 index 0000000..bb875d3 --- /dev/null +++ b/src/MaGIC.h @@ -0,0 +1,61 @@ +/* +* MaGIC.h May 1993 +*/ + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +#include +#include +#include + +#ifdef SYSTEMV +#include +#endif + +#include "vntr.h" +#include "Mdef.h" +#include "axioms.h" +#include "Mtypes.h" +#include "Mglob.h" +#include "Mproto.h" + + + +/* +* We need more timing apparatus if times.h is available. +*/ + +#ifdef HASTIMES +#include +#include +struct tms time_buffer; +#endif diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..28ebbcd --- /dev/null +++ b/src/Makefile @@ -0,0 +1,98 @@ +# Edit the first few lines of this file to suit your setup. +# You *must* set MAGLIB to the appropriate data directory. + +# Where the data files live (must be readable to the MaGIC user) + +MAGLIB = /FullPathnameOfMyDataDirectory !!! EDIT !!! + +# Where the binaries live + +BIN = /FullPathnameOfMyBin !!! EDIT !!! + +# Set the optimization level to O3 (includes inlining of functions) for +# performance, or g for debugging. + +OPTIMIZATION = -O3 +# OPTIMIZATION = -g -Wall + +# +# Uncomment WARNINGS to see warning messages during compilation. + +# WARNINGS = -w + +# If you have times.h (NB _not_ time.h) on your system leave the next +# item alone. If you don't, comment it out. + +HASTIMES = -DHASTIMES + +########################################################################## +# If you have no gcc or if you wish to compile MaGIC with another # +# compiler, change the next line to suit yourself. # +########################################################################## + +CC = gcc + +########################################################################## +# You should not have to change the stuff below # +########################################################################## + +CFLAGS = $(OPTIMIZATION) $(HASTIMES) $(WARNINGS) -DDATA_DIR=\"$(MAGLIB)/\" +MOBJECTS = MaGIC.o axioms.o dialog.o getjob.o wffs.o mp_parse.o isom.o logic_set.o logic_test.o logic_pretest.o setup.o logic_io.o axiom_tests.o +RMOBJECTS = RM.o RM_input.o RM_output.o +UTES = u2p u2pic onegen one_plus_t_gen u2tex sub_irr +MORPHS = image embedded homomorphic nt_homomorphic +POSTPROCS = $(UTES) $(MORPHS) +UTOBJECTS = u2p.o u2pic.o onegen.o one_plus_t_gen.o u2tex.o sub_irr.o +MORPHOBJECTS = image.o embedded.o homomorphic.o nt_homomorphic.o hmi.o + + +all: magic utilities + +magic: $(MOBJECTS) vntr.o + $(CC) -o magic $(MOBJECTS) vntr.o $(LOCLIB) + +utilities: $(POSTPROCS) + +u2p: u2p.o $(RMOBJECTS) + $(CC) -o u2p u2p.o $(RMOBJECTS) + +u2pic: u2pic.o $(RMOBJECTS) + $(CC) -o u2pic u2pic.o $(RMOBJECTS) + +onegen: onegen.o $(RMOBJECTS) + $(CC) -o onegen onegen.o $(RMOBJECTS) + +one_plus_t_gen: one_plus_t_gen.o $(RMOBJECTS) + $(CC) -o one_plus_t_gen one_plus_t_gen.o $(RMOBJECTS) + +u2tex: u2tex.o $(RMOBJECTS) + $(CC) -o u2tex u2tex.o $(RMOBJECTS) + +sub_irr: sub_irr.o $(RMOBJECTS) + $(CC) -o sub_irr sub_irr.o $(RMOBJECTS) + +image: image.o hmi.o $(RMOBJECTS) + $(CC) -o image image.o hmi.o $(RMOBJECTS) + +embedded: embedded.o hmi.o $(RMOBJECTS) + $(CC) -o embedded embedded.o hmi.o $(RMOBJECTS) + +homomorphic: homomorphic.o hmi.o $(RMOBJECTS) + $(CC) -o homomorphic homomorphic.o hmi.o $(RMOBJECTS) + +nt_homomorphic: nt_homomorphic.o hmi.o $(RMOBJECTS) + $(CC) -o nt_homomorphic nt_homomorphic.o hmi.o $(RMOBJECTS) + +install: all + mkdir -p $(BIN) + mv magic $(POSTPROCS) $(BIN) + mkdir -p $(MAGLIB) + cp ../dat/*.* $(MAGLIB) + +clean: + rm -f *.o core $(POSTPROCS) magic + +$(MOBJECTS): MaGIC.h vntr.h Mproto.h Mdef.h Mglob.h Mtypes.h axioms.h +vntr.o: vntr.h +$(RMOBJECTS) $(UTOBJECTS) $(MORPHOBJECTS): RM.h +$(MORPHOBJECTS): hmi.h diff --git a/src/Mdef.h b/src/Mdef.h new file mode 100644 index 0000000..305a826 --- /dev/null +++ b/src/Mdef.h @@ -0,0 +1,139 @@ +/* +* Mdef.h +* +* This is part of the header for MaGIC. It contains the definitions +* of symbolic constants. +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +#define VERSION "2.1" +#define RELEASE_DATE "May 1993" + + +/* +* We need to distinguish between the main file where global +* variables are mostly defined and the other files where they are +* just declared. +*/ + +#ifdef TOPFILE +#define GLOBAL +#else +#define GLOBAL extern +#endif + + +/* +* The input data files are all in a directory: +*/ + +#ifndef DATA_DIR +#define DATA_DIR "/usr/local/lib/MaGIC/" +#endif + +/* +* The clock returns times in 1/TICK sec.: +*/ + +#define TICK 100 + + +/* +* Next the bounds for various arrays. These are the maxima for +* meaningful symbols (SYMBOLMAX), user-defined connectives (CMAX), +* (sub)formulae (FMAX), testable rules (TMAX) and isomorphs +* per process (ISOMAX). VMAX is the maximum number of variables +* for formulae. RTMAX is the maximum number of premises (or +* conclusions) in a rule. +*/ + +#define SYMBOLMAX 256 +#define CMAX 32 +#define FMAX 1024 +#define TMAX 64 +#define ISOMAX 8192 +#define SH_VL 128 +#define VMAX 22 +#define RTMAX 8 +#define ISLMAX (ISOMAX * SH_VL) +#define SLEN 80 + +/* +* And a dummy formula offset for primitives. +*/ + +#define PRIMITIVE -1 + +/* +* And the maximum sizes for the various fragments +*/ + +#ifdef SMALLDISK +#define S_pot 7 +#else +#define S_pot 8 +#endif +#define S_pO 6 +#define S_pont 8 +#define S_poN 7 +#define S_ln 10 +#define S_lat 8 +#define S_ba 16 +#define S_dln 14 +#define S_dlat 10 +#define S_Ton 16 +#define S_to 16 + + +/* +* The null premise and null conclusion are negative integers +*/ + +#define TRIVIAL -1 +#define ABSURD -2 + + +/* +* These constructs are useful abbreviations +*/ + +#define FORALL(x) for (x=0; x<=siz; x++) +#define FORaLL(x) for (x=siz; x>=0; x--) +#define READLN { fflush(stdout); while (getchar() != '\n') ; } +#define IFF(x,y) ((x && y) || (!x && !y)) +#define REMOVE(x,y) y &= ~((unsigned)1 << x) +#define ADDTO(x,y) y |= ((unsigned)1 << x) +#define IN(x,y) ((y & (1 << x)) != 0) +#define SINGLETON(x) (!(x & (x-1))) +#define EP { if ( xdialog ) { printf("E\n"); fflush(stdout); }} +#define outfml(p,q,f) outformula(p,q,f,VARS); +#define symbol_listed(x,s) (symbol_position(x,s) >= 0) diff --git a/src/Mglob.h b/src/Mglob.h new file mode 100644 index 0000000..1ca310e --- /dev/null +++ b/src/Mglob.h @@ -0,0 +1,131 @@ +/* +* Mglob.h +* +* This file contains the declarations of global variables for +* MaGIC. Note that TR.h contains its own globals. +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +GLOBAL JOB *theJob; /* Run specification */ + +GLOBAL TRIN tr_par; /* vntr job specification */ + +GLOBAL FILE + *outfil, + *fopen(); + +GLOBAL int + nulladic[CMAX], /* Defined sential constants */ + monadic[CMAX][SZ], /* Defined connectives */ + dyadic[CMAX][SZ][SZ], /* Ditto */ + vu[VMAX], + rvu[VMAX], /* Variables used in wffs */ + badvalue[VMAX], /* Refuting assignment */ + kost[TMAX], /* Matrix lookups for axioms */ + siz, /* Highest value */ + infil, /* File descriptor for input */ + input_bit, /* Offset of next bit in input */ + start_time, /* Clock reading */ + stop_time, /* Clock reading */ + tot, /* Total matrices tested */ + isoms, isoms2, /* Isomorphs omitted */ + begin_timer, + end_timer, /* For timing of serial version */ + Sizmax, /* Local version of sizmax */ + Vmax, /* Local version of VMAX */ + zero; /* Dummy integer */ + +GLOBAL boolean + xdialog, /* Running from xmagic */ + noclear, /* No system calls */ + filing, /* Output file open */ + default_fragment[LOGMAX][PCMAX]; /* Connectives */ + +GLOBAL char + buffa, /* 8 bits of input data */ + answer[SLEN], /* For input of axioms etc */ + thisvector[V_LENGTH], /* Current matrix stretched out */ + thatvector[V_LENGTH], /* Last good matrix ditto */ + isolist[ISLMAX]; /* Blanks for isomorphisms */ + +GLOBAL WFF + *tx; /* Last active subformula */ + +GLOBAL ISM istak[ISOMAX]; /* Spare records for morphisms */ + +GLOBAL PRM *perm; /* List of permutations */ + +GLOBAL int + C[SZ][SZ], /* The implication matrix */ + ord[SZ][SZ], /* The partial order table */ + K[SZ][SZ], /* The conjunction matrix */ + A[SZ][SZ], /* The disjunction matrix */ + fus[SZ][SZ], /* The fusion matrix */ + N[SZ], /* The negation matrix */ + box[SZ], /* The necessity matrix */ + diamond[SZ], /* The possibility matrix */ + desig[SZ], /* Designated values */ + maximal[SZ], /* Maximal guys under ord */ + atom[2][CMAX], /* 'a' and 'b' for definitions */ + impindex[SZ][SZ], /* Info cell for C[x][y] */ + boxindex[SZ], /* Info cell for box[x] */ + ucc0[CMAX], /* Info cell for nulladic[x] */ + ucc1[CMAX][SZ], /* Info cell for monadic[x][y] */ + ucc2[CMAX][SZ][SZ]; /* And dyadic[x][y][z] */ + +GLOBAL int + good, /* Matrices accepted */ + negno, /* Negation #, this size */ + ordno, /* Order #, this negation */ + desno, /* Des #, this order */ + matno, /* Matrix #, this des */ + boxno, /* Box #, this matrix */ + matplus[CMAX], /* Matno for user's connectives */ + matsum, /* Matrices this des choice */ + des, /* Least designated value */ + undes, /* Greatest undesignated value */ + firstchange, /* First cell changed this test */ + firstarrow, /* First cell of -> matrix */ + firstbox, /* First cell of ! matrix */ + got_a_fail, /* Badguy fails in matrix */ + F_N, /* f_n and contraposition */ + afx; /* Affixing is in force */ + +GLOBAL unsigned + greater_than[SZ], /* {y: ord[x][y]} */ + Greater_than[SZ], /* {y: ord[x][y],x!=y} */ + less_than[SZ], /* {y: ord[y][x]} */ + Less_than[SZ]; /* less_than[x] \ x */ + +GLOBAL L_DEFAULT + default_orders[LOGMAX]; /* Defaults for logics */ diff --git a/src/Mproto.h b/src/Mproto.h new file mode 100644 index 0000000..231991b --- /dev/null +++ b/src/Mproto.h @@ -0,0 +1,301 @@ +/* +* Mproto.h (included in MaGIC.h) +* +* This file contains the function prototypes for MaGIC. Note that +* the prototypes for vntr.c are in the file vntr.h (also included +* in MaGIC.h). +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +/* +* In file MaCIC.c +*/ + +int main(); +void timesup(); +void intrupt(); + + +/* +* In file axioms.c +*/ + +void strings_initial(); +void tests_initial(); +void logics_initial(); +void init_B(); +void init_DW(); +void init_TW(); +void init_EW(); +void init_RW(); +void init_LIN(); +void init_CK(); +void init_T(); +void init_E(); +void init_R(); +void init_S4(); + + +/* +* In file axiom_tests.c +*/ + +boolean Exmid(); +boolean Boolalg(); +boolean SemiBool(); +boolean paradox(); +boolean f_arrow_t(); + +void E_assertion(unsigned info[]); +void contraction(unsigned info[]); +void Wstar(unsigned info[]); +void Reductio(unsigned info[]); +void assertion(unsigned info[]); +void ata(unsigned info[]); +void TaT(unsigned info[]); +void mingle(unsigned info[]); +void t_atomic(unsigned info[]); +void FF_T(unsigned info[]); +void PARADOX(unsigned info[]); +void RWX(unsigned info[]); +void necessity(unsigned info[]); +void necessitation(unsigned info[]); +void NKI_test(unsigned info[]); +void set_prefix(unsigned info[]); +void set_suffix(unsigned info[]); +void squeeze(unsigned info[], int a, int b, int c, int d); + +void pretest_prefix(); +void pretest_suffix(); +void test_S4axiom(); +void test_S5axiom(); +void test_Daxiom(); +void test_NK(); + +boolean test_mslat(trs T); +boolean test_jslat(trs T); +boolean Btest(TRS *T); +boolean B2test(TRS *T); +boolean Stest(TRS *T); +boolean Ctest(TRS *T); +boolean WBtest(TRS *T); +boolean NecImpDist(TRS *T); +boolean NecAdj(TRS *T); +boolean NecW(TRS *T); + + +/* +* In file dialog.c +*/ + +int dialog(boolean batch, char *batchfile); +char menu(); +char readin(char *str); +void paws(); +void job_defaults(boolean batch); +void set_frag( boolean set_sizmax ); +void check_frag(int x); +void print_axiom(FILE *f, AXIOM x); +void display(); +void disp(FILE *f); +void help(helpcode x); +void put_out(char *f_nm); + + +/* +* In file getjob.c +*/ + +void add_axioms(); +void add_one_axiom( AXIOM select ); +void bad_guy(); +void connective(); +void deletion(); +void fragment(); +void input_direct(); +void jump_condition(); +void logic_choice(); +void set_logic( LOGIC lptr ); +void print_version(); +int n_of_procs(); +void order_change(); +void user_order(symb oldsymbols[], char *string, int neworder[]); +void print_options(); +boolean read_job(char *batchfile); +void store_job(); +void injob(FILE *f); +void outjob(FILE *f); +void trim(); +void nospace(char *s); +void delete_saxiom(); +void delete_uaxiom(); +void delete_connective(); +void nodelcon(char *s, symb spec, int formula); + + +/* +* In file isom.c +*/ + +void perm_initial(); +void setperm(); +int lower_than(int x); +int higher_than(int x); +void newperm(int *vec); +boolean isomorphic(ism ptr, trs T); +void snip(ism p); +void subst(ism p1, ism p2); +boolean isomorphic_anyhow(trs T); +void add_isoms(trs T); +boolean add_this(char mat[], ism i_tree); +ism tack_on(ism p, char mat[]); + + +/* +* In file logic_io.c +*/ +int newsiz(); +int newneg(); +int neword(); +int newdes(); +void sep(int *x); +int next_bit(); +int got_siz(); +boolean got_neg(); +boolean got_ord(); +boolean got_des(); +void mat_print(); +void newmatplus(int x, int y); +void siz_print(FILE *f, output_style x); +void neg_print(FILE *f, output_style x); +void ord_print(FILE *f, output_style x); +void des_print(FILE *f, output_style x); +void C_print(FILE *f, output_style x); +void box_print(FILE *f, output_style x); +void u_print0(FILE *f, output_style x, int y); +void u_print1(FILE *f, output_style x, int y); +void u_print2(FILE *f, output_style x, int y); +void printup(FILE *f, output_style x); +void pretty_size(FILE *f); +void pretty_negno(FILE *f); +void pretty_ordno(FILE *f); +void pretty_desno(FILE *f); +void pretty_matno(FILE *f); +void pretty_boxno(FILE *f); +void pretty_umat(FILE *f, int x); +void fail_print(FILE *f); +void insert_badvalues( int offset ); +void stats_print(); + + +/* +* In file logic_pretest.c +*/ +void new_two_ref(int a, int x, int b, int y); +boolean find_twos(unsigned info[], TRS *Tr); +void pretest_fus(); +void affix_case(int a, int b, int c, int d); +void test_assertion(); +void test_contraction(); +void test_TW_upper_bounds(); + + +/* +* In file logic_set.c +*/ +boolean pre_set(); +boolean utest(int x, unsigned info[]); +void set_vuloc(int r, int rr); +boolean badcase(int x, unsigned info[]); +int eval(int r, unsigned z[]); +int anothercase(int x); +boolean set_poss(unsigned info[], trs T); +void fusion(unsigned info[]); +boolean permutable(int a, int b, int c, unsigned info[]); +boolean logic_poss(unsigned info[]); +void logic_axioms(boolean x); +void efficient_logic_set(); + + +/* +* In file logic_test.c +*/ +boolean Good_matrix(unsigned info[], trs T); +void vect_into_C(unsigned info[]); +boolean fus_test(trs T); +boolean axtest(trs T); +void set_used(int x, trs T, boolean topper); +void setcon(); +int getval(int y); + + +/* +* In file mp_parse.c +*/ +int parse( symb fla[] ); +int s_parse( symb fla[] ); +int Match( symb start[] ); +int Finish( int subf[], symb conn[], int tot ); +int Loc( symb mn, int lft, int rgt ); +boolean is_var(symb x); +int symbol_position( int x, symb s ); + + +/* +* In file setup.c +*/ +void subf_set(); +int worstcase(int x, int ntop); +void set_u(int arr[], int x); +void CLoCK(int *timer); +void set_up_cc(); +void job_start(); +void job_stop(boolean batch); +void set_orders(char s[]); +void uglydisp(FILE *f); +void set_up_trin(); + +/* +* In file wffs.c +*/ + +symb newsymbol( char *string, symb symbol_list1[], symb symbol_list2[] ); +void wff_initial(); +boolean got_formula(int x, int y, int yy, char *s); +int infml(input_case x, int y, int yy); +boolean next_symbol( char longs[] ); +symb this_symbol( char *string ); +boolean seek_symbol( char longs[], symb fml[], int *k ); +void fix_atoms(int y, int wf); +void outformula(int p, int q, FILE *f, varmode vm); +boolean is_in(symb s, int w); +void purge( symb badsym ); diff --git a/src/Mtypes.h b/src/Mtypes.h new file mode 100644 index 0000000..b670f19 --- /dev/null +++ b/src/Mtypes.h @@ -0,0 +1,187 @@ +/* +* Mtypes.h +* +* This file is part of the header for MaGIC. It contains +* the type definitions. The special header axioms.h has to +* be included from here, as it uses the enumerated type +* boolean and defines such constants as AXMAX which are in +* turn used here in the definitions of the big structures. +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +/* +* First the output codes. This is just an enumeration. +*/ + +typedef enum +{ + NONE, + PRETTY, + UGLY, + SUMMARY +} output_style; + + +/* +* There is another enumeration of cases for formula input +*/ + +typedef enum +{ + IN_CONC, + IN_PREM, + IN_BGUY, + IN_DEFN +} input_case; + + +/* +* And one for formula output +*/ + +typedef enum +{ + VARS, + VALS +} varmode; + + +/* +* Then the various help codes +*/ + +typedef enum +{ + MEN, + WFF1, + WFF2, + FDL, + BTW, + LOG, + OUT, + HELPMAX +} helpcode; + + +/* +* It remains to define the structure types used. Each +* "struct xxxtype" expression is abbreviated using typedef +* to preserve sanity later on. JOB is for communication of +* the major problem specification between the front end and +* MaGIC itself. The others are fairly self-explanatory. +*/ + + +typedef struct isomorph +{ char *icv; /* Isomorphic version of info */ + struct isomorph + *left, + *right, + *parent; /* Links to make a binary tree */ +} ISM, *ism; + + +typedef struct symbol_list +{ + char s[SLEN]; /* Connective as a string */ + struct symbol_list + *next, *last; /* Links for list */ +} SYMB, *symb; + + +typedef struct +{ symb sym; /* The main symbol */ + int lsub, + rsub, /* Offsets of the subformulas */ + *mtx, /* Start of the relevant matrix */ + *lv, + *rv, /* To values of subformulas */ + val; /* Currently assigned value */ +} WFF; + + +typedef struct PerMutAtion +{ + char h[SZ]; /* Image under homomorphism */ + struct PerMutAtion + *pup; /* Pointer to the next guy up */ +} PRM; + + +typedef enum { + lattices, + distributive_lattices, + total_orders +} L_DEFAULT; + + +typedef enum { + n_exists, + lat_exists, + fus_exists, + nec_exists, + PCMAX +} XF; + + +typedef struct +{ + int adicity[CMAX], /* Of defined connectives */ + croot[TMAX][RTMAX], /* Roots of rule conclusions */ + proot[TMAX][RTMAX], /* Roots of rule premises */ + defcon[CMAX], /* Roots of defined connectives */ + failure, /* Root of badguy */ + maxtime, /* Maximum clock reading */ + maxmat, /* Maximum good matrices */ + sizmax; /* Maximum matrix size */ + output_style + tty_out, + fil_out; /* Output formats */ + char data_dir[SLEN], /* Text name of input directory */ + outfil_name[SLEN]; /* Text name of output file */ + WFF form[FMAX]; /* (Subformulas of) axioms etc */ + + SYMB symtable[SYMBOLMAX]; /* Symbol table for connectives */ + symb symtab, /* List of symbols used */ + symbol[3][CMAX+8], /* Symbol table entries */ + dcs[CMAX+1]; /* Defined connectives */ + boolean axiom[AXMAX], /* Selected axioms (default none) */ + concut[CMAX], /* Flags connectives with "cuts" */ + f_n, f_lat, + f_t, f_T, + f_F, /* The fragment (default 1) */ + f_fus, f_nec, /* The fragment (default 0) */ + sizmax_ismax, /* (Boolean) Let sizmax float up */ + distrib, /* Distributive lattice requested */ + totord; /* Total orders requested */ + LOGIC logic; /* The system (default FD) */ +} JOB; diff --git a/src/RM.c b/src/RM.c new file mode 100644 index 0000000..5077319 --- /dev/null +++ b/src/RM.c @@ -0,0 +1,230 @@ +/* RM.c (Read_Matrices) March 1991 +* +* This is the basic post-processor for MaGIC. +* +* Selectmats reads in matrix representations of algebraic +* models for propositional logics and performs an operation +* on each one that satisfies a condition. It is called with +* three parameters, being a pointer to a MATRIX structure, +* the name of the user-defined selection function and the +* print mode (UGLY, PRETTY, TeX or NONE). +* +* The selector is allowed to return TRUE (1), FALSE (0) or +* END_OF_JOB (-1). Any other value is treated as 0. +* +* See RM.h for a description of the data structures. +*/ + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "RM.h" + + +void selectmats(int (*selector)(), PRINTMODE printmode) +{ + int i, done; + MATRIX *m; + + m = mat_initial(); + m->total_got = m->total_put = 0; + done = 0; + read_header(stdin,m); + if ( newsiz(stdin,m) ) + do { + m->total_got++; + for (i=0; imy_values[i] = -1; + *(m->my_string) = '\0'; + switch ((*selector)(m)) { + case 1: + m->total_put++; + increment_ok(m); + printmat(m,printmode,SELECTED,stdout); + break; + case -1: + done = 1; + } + } + while (!done && newcase(stdin,m)); + switch(printmode) { + case UGLY: + FORALLCON((m),i) printf(" -1"); + printf(" -1 -1 -1 -1 -1\n"); + break; + case PRETTY: + printf("\n\n Total read: %d\n Total written: %d\n\n", + m->total_got, m->total_put ); + break; + case TeX: + printf("\n\n\\end{document}\n"); + break; + case NONE: + break; + } +} + + + + +void relatemats(int (*selector)(), char *filename, char *relname) +{ + int i, done; + MATRIX *m1; + FILE *f1; + MATRIX *m2; + FILE *f2; + + f1 = fopen(filename,"r"); + m1 = mat_initial(); + m1->total_got = m1->total_put = 0; + done = 0; + read_header(f1,m1); + if (newsiz(f1,m1)) + do { + m1->total_got++; + for (i=0; imy_values[i] = -1; + m1->my_string[0] = 0; + f2 = fopen(filename,"r"); + m2 = mat_initial(); + m2->total_got = m1->total_put = 0; + done = 0; + read_header(f2,m2); + if (newsiz(f2,m2)) + do { + m2->total_got++; + for (i=0; imy_values[i] = -1; + *m2->my_string = 0; + switch ((*selector)(m1,m2)) { + case 1: + print_related(m1,m2,relname); + break; + case -1: + done = 1; + } + } + while (!done && newcase(f2,m2)); + fclose(f2); + } + while (!done && newcase(f1,m1)); +} + + + + +/* +* True is a dummy function which can be used to force every +* matrix to be printed out just as it is read in. +*/ + +int True(MATRIX *m) +{ + return 1; +} + + + + + + +/* +* There are two versions of the matrix numbers. The ones +* prefixed with "ok" record the numbers on which compute has +* been performed. Those without record the numbers read in. +* +* Before compute happens, the "ok" numbers are incremented. +*/ + +void increment_ok(MATRIX *m) +{ + int first, i; + + for ( first = 0; first < m->cmax && m->okmatplus[first]; first++ ) ; + for ( i = first; i < m->cmax; i++ ) m->okmatplus[i] = 1; + if ( first ) m->okmatplus[first-1]++; + else { + if ( !m->okmatno ) { + if ( !m->okdesno ) { + if ( !m->okordno ) { + if ( m->fragment[NEG] ) + m->oknegno++; + } + m->okordno++; + } + m->okdesno++; + } + m->okmatno++; + } +} + + + + + +/* +* Mat_malloc allocates memory to a MATRIX pointer and does +* the necessary initialisation. +*/ + +MATRIX *mat_initial() +{ + MATRIX *m; + + m = (MATRIX *) malloc(sizeof(MATRIX)); +/* +* WFF initialisation will go in here +*/ + return m; +} + + + + + +/* +* An abort function is provided. This causes a clean exit +* after printing an error message (with integer insert). +* +* Setting the integer argument to -1 disables this option. +*/ + + +void Abort( char *s, int x) +{ + fprintf( stderr, "\n\n Aborting on detection of an error\n " ); + if ( x == -1 ) fprintf( stderr, s ); + else fprintf( stderr, s, x ); + fprintf( stderr, "\n" ); + exit(1); +} diff --git a/src/RM.h b/src/RM.h new file mode 100644 index 0000000..794c886 --- /dev/null +++ b/src/RM.h @@ -0,0 +1,295 @@ +/* Header for Read_Matrices +* +* Selectmats reads in matrix representations of algebraic +* models for propositional logics and prints out each one +* that satisfies a condition. It is called with three +* parameters, being a pointer to a MATRIX, the name of the +* user-defined selection function and the print mode +* (PRETTY, UGLY or TeX). +*/ + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +/* +* The setup is read into MATRIX fields as follows: +* +* siz the greatest-numbered value. So the +* values are the integers 0...siz. +* +* neg the negation table (if present). +* +* ord the partial order of implication. +* +* A, K disjunction and conjunction tables. +* +* designated designation and undesignation of values. +* +* tee the least designated value (if defined). +* +* eff the negation of tee (if defined). +* +* C the implication table. +* +* box the necessity table. +* +* fus the fusion table (if defined). +* +* fis the fission table (if defined). +* +* dcs the symbols for defined connectives. +* +* adicity of the defined connectives +* +* nulladic +* monadic +* dyadic matrices for the user's connectives. +* +* fused boolean flag: fusion is defined. +* +* tee_exists boolean flag: tee is defined. +* +* negno +* ordno +* desno +* matno Offset numbers of the current setup. +* +* oknegno +* okordno +* okdesno +* okmatno Offset numbers of the latest good setup. +* +* fragment connectives defined +* +* +* In addition, the constant SZ is defined as the greatest +* possible number of values. That is, siz < SZ always. +* +* The universal quantifier FORALL is defined for use in the +* host program if desired. FORALLCON is also provided to run +* through the user-defined primitives in their change order. +* +* The data type WFF is exactly as in MaGIC, for input and +* testing of formulas. The parser may be linked in. +* +* The array my_values and the string my_string are provided +* for communication of the results of tests etc to the +* printout routine. They are initialised to a series of +* -1s and to the null string respectively. These values +* should be used for the null communication. +* +* Several other MATRIX fields are used to control the +* environment, mostly to determine printing options. +* +* cmax number of user-added primitives +* total_got number of matrices read in +* total_put number which have passes the test +* singlematrix treat as though the only matrix +* +* Those are all integers. There are a couple of defined +* enumeration types, PRINTMODE and PRINTCOUNTER. These are +* the types of the parameters printmode and printcountermode +* passed to printmat. The former of these determines whether +* output is PRETTY, UGLY or TeX, while the latter determines +* whether the numbering of matrices is to be incremental +* within the output only or whether the numbers remain as on +* input. +*/ + + + +#include +#include +#include +#include + +#define SZ 16 +#define FMAX 256 +#define CMAX 32 +#define VALUEMAX 10 + +#define FORALL(m,x) for ( x = 0; x <= m->siz; x++ ) +#define FOREACH(m,x) for ( x = m->siz; x >= 0; x-- ) +#define FORALLCON(m,x) for ( x = 0; x < m->cmax; x++) + +typedef enum { NONE, PRETTY, UGLY, TeX } PRINTMODE; +typedef enum { SELECTED, UNSELECTED } COUNTERMODE; +enum { NEG, TEE, TOP, BOT, FUS, LAT, BOX, FRAGMAX } ; + +typedef struct well_formed_formula { + char *sym; /* The main symbol */ + int lsub; + int rsub; /* Offsets of the subformulas */ + int *mtx; /* Start of the relevant matrix */ + int *lv; + int *rv; /* To values of subformulas */ + int val; /* Currently assigned value */ +} WFF; + + +/* +* Now for the big one: this is the matrix structure. +*/ + +typedef struct { + WFF form[FMAX]; + WFF *tx; + + int siz; + int neg[SZ]; + int ord[SZ][SZ]; + int designated[SZ]; + int tee; + int eff; + int A[SZ][SZ]; + int K[SZ][SZ]; + int C[SZ][SZ]; + int fus[SZ][SZ]; + int fis[SZ][SZ]; + int box[SZ]; + int diamond[SZ]; + int adicity[CMAX]; + int nulladic[CMAX]; + int monadic[CMAX][SZ]; + int dyadic[CMAX][SZ][SZ]; + int negno; + int oknegno; + int ordno; + int okordno; + int desno; + int okdesno; + int matno; + int okmatno; + int boxno; + int okboxno; + int matplus[CMAX]; + int okmatplus[CMAX]; + int fused; + int tee_exists; + int cmax; + int total_got; + int total_put; + int fragment[FRAGMAX]; + int my_values[VALUEMAX]; + + char my_string[256]; + char dcs[CMAX][16]; + char symbols[3][15]; +} MATRIX; + + + +FILE *fopen(); + +/* +* Finally, here are the function prototypes for RM.c +*/ + +void selectmats(int (*selector)(), PRINTMODE printmode); +void relatemats(int (*selector)(), char *filename, char *relname); +int True(MATRIX *m); +void increment_ok(MATRIX *m); +MATRIX* mat_initial(); +void Abort(char *s, int x); + +/* +* These are the prototypes for RM_input.c +*/ +void read_header(FILE *f, MATRIX *m); +int newsiz(FILE *f, MATRIX *m); +int newneg(FILE *f, MATRIX *m); +int neword(FILE *f, MATRIX *m); +int newdes(FILE *f, MATRIX *m); +int newC(FILE *f, MATRIX *m); +int newbox(FILE *f, MATRIX *m); +int newcon(FILE *f, MATRIX *m, int x); +int newcase(FILE *f, MATRIX *m); +int gotsiz(FILE *f, MATRIX *m); +int gotneg(FILE *f, MATRIX *m); +int gotord(FILE *f, MATRIX *m); +int gotdes(FILE *f, MATRIX *m); +int gotaro(FILE *f, MATRIX *m); +int gotbox(FILE *f, MATRIX *m); +int gotcon(FILE *f, MATRIX *m, int x); + + +/* +* These are the prototypes for RM_output.c +*/ + +void displaymat(MATRIX *m, char *legend); +void printmat(MATRIX *m, PRINTMODE printmode, + COUNTERMODE countermode, FILE *f); +void write_header(MATRIX *m); +void pu_print(MATRIX *m, int x, FILE *f); +void uu_print(MATRIX *m, int x); +void pbox_print( MATRIX *m, FILE *f ); +void ubox_print(MATRIX *m); +void pC_print(MATRIX *m, FILE *f); +void uC_print(MATRIX *m); +void pdesprint(MATRIX *m, FILE *f); +void udesprint(MATRIX *m); +void pordprint(MATRIX *m, FILE *f); +void uordprint(MATRIX *m); +void pnegprint(MATRIX *m, FILE *f); +void unegprint(MATRIX *m); +void psizprint(MATRIX *m, FILE *f); +void usizprint(MATRIX *m); +void neg_number(MATRIX *m, FILE *f); +void ord_number(MATRIX *m, FILE *f); +void des_number(MATRIX *m, FILE *f); +void C_number(MATRIX *m, FILE *f); +void box_number(MATRIX *m, FILE *f); +void u_number(MATRIX *m, int x, FILE *f); +void nullprint(MATRIX *m, char *s, int x, FILE *f); +void unullprint(MATRIX *m, int x); +void monprint(MATRIX *m, char *s, char c, int arr[], FILE *f); +void umonprint(MATRIX *m, int arr[]); +void dyprint(MATRIX *m, char *s1, char c, int arr[][SZ], + char *s2, char d, int darr[][SZ], FILE *f); +void udyprint(MATRIX *m, int arr[][SZ]); +void tex_header(); +void tex_mystuff(MATRIX *m); +void texchar(char c); +void tex_uprint(MATRIX *m, int x); +void tex_boxprint(MATRIX *m); +void tex_Cprint(MATRIX *m); +void tex_desprint(MATRIX *m); +void tex_ordprint(MATRIX *m); +void tex_negprint(MATRIX *m); +void tex_sizprint(MATRIX *m); +void tex_nullprint(MATRIX *m, char *s, int x); +void tex_monprint(MATRIX *m, char *s, char c, int arr[]); +void tex_dyprint(MATRIX *m, char *s1, char c, int arr[][SZ], + char *s2, char d, int darr[][SZ]); +void tex_dymatprint(MATRIX *m, int offset, char *s, char c, int arr[][SZ]); +void print_related(MATRIX *m1, MATRIX *m2, char *relname); diff --git a/src/RM_input.c b/src/RM_input.c new file mode 100644 index 0000000..2762659 --- /dev/null +++ b/src/RM_input.c @@ -0,0 +1,272 @@ +/* Input.c March 1991 +* +* Functions to read matrices for MaGIC postprocessor. +*/ + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "RM.h" + + + + +void read_header(FILE *f, MATRIX *m) +{ + int i, j = 0; + + for (i=0; ifragment+i); + fscanf(f,"%d",&(m->cmax)); + FORALLCON(m,i) { + fscanf(f,"%d",m->adicity+i); + do + fscanf(f,"%c",m->dcs[i]); + while (isspace(m->dcs[i][0])); + do + fscanf(f,"%c",m->dcs[i]+(++j)); + while (!isspace(m->dcs[i][j])); + m->dcs[i][j] = 0; + } +} + + + + +int newsiz(FILE *f, MATRIX *m) +{ + return(gotsiz(f,m)? (m->fragment[NEG]? newneg(f,m): neword(f,m)): 0); +} + +int newneg(FILE *f, MATRIX *m) +{ + return(gotneg(f,m)? neword(f,m): newsiz(f,m)); +} + +int neword(FILE *f, MATRIX *m) +{ + return(gotord(f,m)? newdes(f,m): + (m->fragment[NEG]? newneg(f,m): newsiz(f,m))); +} + +int newdes(FILE *f, MATRIX *m) +{ + return(gotdes(f,m)? newC(f,m): neword(f,m)); +} + +int newC(FILE *f, MATRIX *m) +{ + return(gotaro(f,m)? (m->fragment[BOX]? newbox(f,m): newcon(f,m,0)): + newdes(f,m)); +} + +int newbox(FILE *f, MATRIX *m) +{ + return(gotbox(f,m)? newcon(f,m,0): newC(f,m)); +} + +int newcon(FILE *f, MATRIX *m, int x) +{ + if (x >= m->cmax) return 1; + return(gotcon(f,m,x)? newcon(f,m,x+1): + (x? newcon(f,m,x-1): + (m->fragment[BOX]? newbox(f,m): newC(f,m))) ); +} + + +int newcase(FILE *f, MATRIX *m) +{ + if (m->cmax) + return newcon(f,m,m->cmax-1); + if (m->fragment[BOX] ) + return newbox(f,m); + return newC(f,m); +} + + + + +int gotsiz(FILE *f, MATRIX *m) +{ + fscanf(f,"%d",&(m->siz)); + m->negno = m->ordno = 0; + m->oknegno = m->okordno = 0; + return (m->siz < SZ && m->siz > 0); +} + + +int gotneg(FILE *f, MATRIX *m) +{ + int i; + + fscanf(f,"%d",m->neg); + if (*(m->neg) < 0) + return 0; + m->negno++; m->ordno = 0; + m->okordno = 0; + for (i=1; i<=m->siz; i++ ) + fscanf(f,"%d", m->neg+i); + return 1; +} + + +int gotord(FILE *f, MATRIX *m) +{ + int i,j; + + fscanf(f,"%d",*(m->ord)); + if (**(m->ord) < 0) + return 0; + m->ordno++; m->desno = 0; + m->okdesno = 0; + FORALL(m,i) FORALL(m,j) if (i||j) + fscanf(f,"%d",m->ord[i]+j); + FORALL(m,i) FORALL(m,j) { + FORALL(m,m->A[i][j]) + if (m->ord[i][m->A[i][j]] && m->ord[j][m->A[i][j]]) + break; + FOREACH(m,m->K[i][j]) + if (m->ord[m->K[i][j]][i] && m->ord[m->K[i][j]][j]) + break; + } + return 1; +} + + +int gotdes(FILE *f, MATRIX *m) +{ + int i; + + fscanf(f,"%d",m->designated); + if (*(m->designated) < 0) + return 0; + FORALL(m,i) + if (i) + fscanf(f,"%d",m->designated+i); + FORALL(m,m->tee) + if ( m->designated[m->tee] ) + break; + m->tee_exists = 1; + FORALL(m,i) + if (m->designated[i] && !m->ord[m->tee][i]) + m->tee_exists = 0; + m->desno++; m->matno = 0; + m->okmatno = 0; + m->eff = m->neg[m->tee]; + return 1; +} + + +int gotaro(FILE *f, MATRIX *m) +{ + int i, j, k; + + fscanf(f,"%d",*(m->C)); + if (**(m->C) < 0) + return 0; + m->matno++; + if (m->fragment[BOX]) { + m->boxno = 0; + m->okboxno = 0; + } + else { + *(m->matplus) = 0; + *(m->okmatplus) = 0; + } + FORALL(m,i) FORALL(m,j) + if (i||j) + fscanf(f,"%d",m->C[i]+j); + m->fused = 1; + FORALL(m,i) FORALL(m,j) { + FORALL(m,m->fus[i][j]) + if (m->ord[i][m->C[j][m->fus[i][j]]]) + break; + if (m->fus[i][j] > m->siz) + m->fused = 0; + } + if (m->fused) { + FORALL(m,i) FORALL(m,j) FORALL(m,k) + if (m->ord[m->fus[i][j]][k] && !m->ord[i][m->C[j][k]]) + m->fused = 0; + if (m->ord[i][m->C[j][k]] && !m->ord[m->fus[i][j]][k]) + m->fused = 0; + } + if (m->fragment[NEG]) + FORALL(m,i) FORALL(m,j) + m->fis[i][j] = m->C[m->neg[i]][j]; + return 1; +} + + +int gotbox(FILE *f, MATRIX *m) +{ + int i; + + fscanf(f,"%d",m->box); + if (*(m->box) < 0) + return 0; + m->boxno++; + *(m->matplus) = 0; + *(m->okmatplus) = 0; + for (i=1; i<=m->siz; i++) + fscanf(f,"%d",m->box+i); + return 1; +} + + +int gotcon(FILE *f, MATRIX *m, int x) +{ + int i, j, k; + + fscanf(f,"%d",&k); + if (k < 0) + return 0; + m->matplus[x]++; + m->matplus[x+1] = 0; + m->okmatplus[x+1] = 0; + switch(m->adicity[x]) { + case 0: + m->nulladic[x] = k; + break; + case 1: + m->monadic[x][0] = k; + FORALL(m,i) if (i) + fscanf(f,"%d",m->monadic[x]+i); + break; + case 2: + m->dyadic[x][0][0] = k; + FORALL(m,i) FORALL(m,j) + if ( i||j ) + fscanf(f,"%d",m->dyadic[x][i]+j); + } + return 1; +} diff --git a/src/RM_output.c b/src/RM_output.c new file mode 100644 index 0000000..9f91c4d --- /dev/null +++ b/src/RM_output.c @@ -0,0 +1,710 @@ +/* Output.c March 1991 +* +* These are the functions for ugly, pretty and TeX output of +* matrices. They are called from RM.c which is used by all +* MaGIC post-processing programs. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.0 * + * * + * (C) 1991 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "RM.h" + + +static int pnegno; +static int pordno; +static int pdesno; +static int pmatno; +static int pboxno; +static int pmatplus[CMAX]; +static int singlematrix; + + +void displaymat(MATRIX *m, char *legend) +{ + FILE *f; + + f = fopen("/dev/tty","w"); + singlematrix = 1; + system( "clear > /dev/tty" ); + printmat( m, PRETTY, UNSELECTED, f ); + fprintf( f, "\n\n %s", legend ); + singlematrix = 0; + fclose(f); +} + + + +void printmat(MATRIX *m, PRINTMODE printmode, COUNTERMODE countermode, FILE *f) +{ + int i; + + if ( countermode == SELECTED ) { + pnegno = m->oknegno; + pordno = m->okordno; + pdesno = m->okdesno; + pmatno = m->okmatno; + pboxno = m->okboxno; + FORALLCON(m,i) + pmatplus[i] = m->okmatplus[i]; + } + else if ( countermode == UNSELECTED ) { + pnegno = m->negno; + pordno = m->ordno; + pdesno = m->desno; + pmatno = m->matno; + pboxno = m->boxno; + FORALLCON(m,i) + pmatplus[i] = m->matplus[i]; + } + else Abort("Unrecognised print_counter mode: %d", countermode); + + switch (printmode) { + case PRETTY: + pu_print( m, m->cmax-1, f ); + if ( m->my_values[0] != -1 || m->my_string[0] ) { + fprintf( f, "\n %s", m->my_string ); + for ( i = 0; m->my_values[i] != -1; i++ ) + fprintf( f, " %d", m->my_values[i] ); + fprintf( f, "\n" ); + } + return; + case UGLY: + if ( m->total_put == 1 ) write_header( m ); + uu_print( m, m->cmax-1 ); + return; + case TeX: + if ( m->total_put == 1 ) tex_header(); + tex_uprint( m, m->cmax-1 ); + if ( m->my_values[0] != -1 || m->my_string[0] ) + tex_mystuff( m ); + return; + case NONE: + return; + } + Abort( "Unrecognised print mode: %d", printmode ); +} + + + +void write_header(MATRIX *m) +{ + int i; + + for ( i = 0; i < FRAGMAX; i++ ) + printf( " %d", m->fragment[i] ); + printf( "\n %d", m->cmax ); + FORALLCON(m,i) + printf( " %d %s", m->adicity[i], m->dcs[i] ); + printf( "\n"); +} + + + +void pu_print(MATRIX *m, int x, FILE *f) +{ + if ( x < 0 ) { + if ( m->fragment[BOX] ) pbox_print( m, f ); + else pC_print( m, f ); + } + else { + if ( pmatplus[x] == 1 || singlematrix ) + pu_print( m, x-1, f ); + fprintf( f, "\n\n %s table ", m->dcs[x] ); + u_number( m, x, f ); + switch( m->adicity[x] ) { + case 0: + nullprint( m, m->dcs[x], m->nulladic[x], f ); + break; + case 1: + monprint( m, m->dcs[x], 0, m->monadic[x], f ); + break; + case 2: + dyprint( m, m->dcs[x], 0, m->dyadic[x], "", 0, 0, f ); + } + } +} + + +void uu_print(MATRIX *m, int x) +{ + if ( x < 0 ) { + if ( m->fragment[BOX] ) ubox_print( m ); + else uC_print( m ); + } + else { + if ( pmatplus[x] == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n" ); + uu_print( m, x-1 ); + } + switch( m->adicity[x] ) { + case 0: unullprint( m, m->nulladic[x] ); + break; + case 1: umonprint( m, m->monadic[x] ); + break; + case 2: udyprint( m, m->dyadic[x] ); + } + } +} + + + +void pbox_print( MATRIX *m, FILE *f ) +{ + if ( pboxno == 1 || singlematrix ) pC_print( m, f ); + fprintf( f, "\n\n Necessity " ); + box_number( m, f ); + monprint( m, "!", 0, m->box, f ); +} + + + +void ubox_print(MATRIX *m) +{ + if ( pboxno == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n"); + uC_print( m ); + } + umonprint( m, m->box ); +} + + + +void pC_print(MATRIX *m, FILE *f) +{ + if ( pmatno == 1 || singlematrix ) pdesprint( m, f ); + fprintf( f, "\n\n Implication " ); + C_number( m, f ); + if ( m->fused ) + dyprint( m, "->", 0, m->C, "o", 0, m->fus, f ); + else + dyprint( m, "->", 0, m->C, "", 0, 0, f ); +} + + + +void uC_print(MATRIX *m) +{ + if ( pmatno == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n"); + udesprint( m ); + } + udyprint( m, m->C ); +} + + + +void pdesprint(MATRIX *m, FILE *f) +{ + if ( pdesno == 1 || singlematrix ) pordprint( m, f ); + fprintf( f, "\n\n\n Choice of truths " ); + des_number( m, f ); + monprint( m, "True", '-', m->designated, f ); +} + + + +void udesprint(MATRIX *m) +{ + if ( pdesno == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n"); + uordprint( m ); + } + umonprint( m, m->designated ); +} + + +void pordprint(MATRIX *m, FILE *f) +{ + if ( pordno == 1 || singlematrix ) { + if ( m->fragment[NEG] ) pnegprint( m, f ); + else psizprint( m, f ); + } + fprintf( f, "\n\n\n Order table " ); + ord_number( m, f ); + dyprint( m, "_\b<", '-', m->ord, "", 0, 0, f ); +} + + +void uordprint(MATRIX *m) +{ + if ( pordno == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n"); + if ( m->fragment[NEG] ) unegprint( m ); + else usizprint( m ); + } + udyprint( m, m->ord ); +} + + + +void pnegprint(MATRIX *m, FILE *f) +{ + if ( pnegno == 1 || singlematrix ) psizprint( m, f ); + fprintf( f, "\n\n\n Negation table " ); + neg_number( m, f ); + monprint( m, "~", 0, m->neg, f ); +} + + + +void unegprint(MATRIX *m) +{ + if ( pnegno == 1 || singlematrix ) { + if ( m->total_put > 1 ) printf( " -1\n"); + usizprint( m ); + } + umonprint( m, m->neg ); +} + + + +void psizprint(MATRIX *m, FILE *f) +{ + fprintf( f, "\n\n\n Size: %d\n", m->siz+1 ); +} + + + +void usizprint(MATRIX *m) +{ + printf( " %d\n", m->siz ); +} + + + +void neg_number(MATRIX *m, FILE *f) +{ + fprintf( f, "%d.%d", m->siz+1, pnegno ); +} + + +void ord_number(MATRIX *m, FILE *f) +{ + if ( !m->fragment[NEG] ) fprintf( f, "%d", m->siz+1 ); + else neg_number( m, f ); + fprintf( f, ".%d", pordno ); +} + + +void des_number(MATRIX *m, FILE *f) +{ + ord_number( m, f ); + fprintf( f, ".%d", pdesno ); + +} + + +void C_number(MATRIX *m, FILE *f) +{ + des_number( m, f ); + fprintf( f, ".%d", pmatno ); +} + + +void box_number(MATRIX *m, FILE *f) +{ + C_number( m, f ); + fprintf( f, ".%d", pboxno ); +} + + +void u_number(MATRIX *m, int x, FILE *f) +{ + if ( !x ) { + if ( m->fragment[BOX] ) box_number( m, f ); + else C_number( m, f ); + } + else u_number( m, x-1, f ); + fprintf( f, ".%d", pmatplus[x] ); +} + + + + +void nullprint(MATRIX *m, char *s, int x, FILE *f) +{ + fprintf( f, " %s = %d\n", s, x ); +} + + + + +void unullprint(MATRIX *m, int x) +{ + printf( "%d\n", x ); +} + + + +void monprint(MATRIX *m, char *s, char c, int arr[], FILE *f) +{ + int i, offset; + + fprintf( f, "\n\n " ); + for ( offset = 0; s[offset]; offset++ ) fprintf( f, " " ); + if ( offset > 1 ) fprintf(f, " "); + fprintf( f, "a |" ); + FORALL(m,i) fprintf( f, " %x", i ); + fprintf( f, "\n " ); + for ( i = 0; i < offset; i++ ) fprintf( f, "-" ); + if ( offset > 1 ) fprintf( f, "--" ); + fprintf( f, "--+" ); + FORALL(m,i) fprintf( f, "--" ); + fprintf( f, "\n " ); + if ( offset > 1 ) fprintf( f, "%s(a) |", s ); + else fprintf( f, "%sa |", s ); + FORALL(m,i) + if ( c == '-' ) fprintf( f, " %c", arr[i]? '+': '-' ); + else fprintf( f, " %x", arr[i] ); + fprintf( f, "\n" ); +} + + + +void umonprint(MATRIX *m, int arr[]) +{ + int i; + + FORALL(m,i) + printf( " %d", arr[i] ); + printf( "\n" ); +} + + + +void dyprint(MATRIX *m, char *s1, char c, int arr[][SZ], + char *s2, char d, int darr[][SZ], FILE *f) +{ + int i, j, offset = 0; + + for ( i = 0; s1[i]; i++ ) + if ( s1[i] == '\b' ) offset--; else offset++; + fprintf( f, "\n\n %s |", s1 ); + FORALL(m,i) fprintf( f, " %x", i ); + if ( *s2 ) { + fprintf( f, "%15s |", s2 ); + FORALL(m,i) fprintf( f, " %x", i ); + } + fprintf( f, "\n " ); + for ( i = 1; i < offset; i++ ) fprintf( f, "-" ); + fprintf( f, "---+" ); + FORALL(m,i) fprintf( f, "--" ); + if ( *s2 ) { + for ( i = 14; i; i-- ) + fprintf( f, (i > strlen(s2)? " ": "-") ); + fprintf( f, "--+" ); + FORALL(m,i) fprintf( f, "--" ); + } + FORALL(m,i) { + fprintf( f, "\n " ); + for ( j = 1; j < offset; j++ ) fprintf( f, " " ); + fprintf( f, "%x |", i ); + FORALL(m,j) + if ( c == '-' ) + fprintf( f, " %c", arr[i][j]? '+': '-' ); + else fprintf( f, " %x", arr[i][j] ); + if ( *s2 ) { + fprintf( f, "%15x |", i ); + FORALL(m,j) + if ( d == '-' ) + fprintf( f, " %c", darr[i][j]? '+': '-' ); + else fprintf( f, " %x", darr[i][j] ); + } + } + fprintf( f, "\n" ); +} + + + +void udyprint(MATRIX *m, int arr[][SZ]) +{ + int i, j; + + FORALL(m,i) FORALL(m,j) + printf( " %d", arr[i][j] ); + printf( "\n" ); +} + + + +void tex_header() +{ + printf( "\\documentstyle{article}\n"); + printf( "\\oddsidemargin=15mm\n"); + printf( "\\evensidemargin=15mm\n"); + printf( "\\textwidth=160mm\n"); + printf( "\\topmargin=0mm\n"); + printf( "\\textheight=250mm\n"); + printf( "\\begin{document}\n\n"); + printf( "\\pagestyle{empty}\n"); + printf( "\\newcommand{\\C}{\\rightarrow}\n"); + printf( "\\newcommand{\\N}{\\neg}\n"); + printf( "\\newcommand{\\A}{\\mbox{$\\,${\\footnotesize"); + printf( " $\\vee$}$\\,$}}\n"); + printf( "\\newcommand{\\fs}{\\circ}\n"); + printf( "\\newcommand{\\hugeskip}{\\vspace{10mm}}\n"); + printf( "\\setlength{\\tabcolsep}{1.5mm}\n\n"); +} + + + +void tex_mystuff(MATRIX *m) +{ + int i; + + printf( "\n%s", m->my_string); + for ( i = 0; m->my_values[i] != -1; i++ ) + printf( " \\ %d", m->my_values[i]); +} + + + +void texchar(char c) +{ + switch(c) { + case '&': + case '%': + case '$': + case '#': + case 'v': + case '^': + case '_': + case '{': + case '}': printf( "\\%c", c ); return; + case '~': printf( "$\\N$" ); return; + case '>': printf( "$\\C$" ); return; + case '<': printf( "$\\leq$" ); return; + case 'o': printf( "$\\fs$" ); return; + case '-': printf( "$-$" ); return; + case '!': printf( "$\\Box$" ); return; + case '?': printf( "$\\Diamond$" ); return; + } + putchar( c ); +} + + + +void texstring(char *s) +{ + do texchar( *s ); + while ( *s++ ); +} + + + +void tex_uprint(MATRIX *m, int x) +{ + if ( x < 0 ) { + if ( m->fragment[BOX] ) tex_boxprint( m ); + else tex_Cprint( m ); + } + else { + if ( pmatplus[x] == 1 || singlematrix ) + tex_uprint( m, x-1 ); + printf( + "\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{"); + texstring( m->dcs[x] ); + printf( " table " ); + u_number( m, x, stdout ); + switch( m->adicity[x] ) { + case 0: + tex_nullprint( m, m->dcs[x], m->nulladic[x] ); + break; + case 1: + tex_monprint( m, m->dcs[x], ' ', m->monadic[x] ); + break; + case 2: + tex_dyprint( m, m->dcs[x], 0, m->dyadic[x], "", 0, 0 ); + } + printf( "}" ); + } +} + + + + +void tex_boxprint(MATRIX *m) +{ + if ( pboxno == 1 || singlematrix ) tex_Cprint( m ); + printf( + "\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{Necessity " ); + box_number( m, stdout ); + tex_monprint( m, "!", ' ', m->box ); + printf( "}" ); +} + + + +void tex_Cprint(MATRIX *m) +{ + if ( pmatno == 1 || singlematrix ) tex_desprint( m ); + printf( + "\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{Implication " ); + C_number( m, stdout ); + if ( m->fused ) + tex_dyprint( m, ">", 0, m->C, "o", 0, m->fus ); + else + tex_dyprint( m, ">", 0, m->C, "", 0, 0 ); + printf( "}" ); +} + + + +void tex_desprint(MATRIX *m) +{ + if ( pdesno == 1 || singlematrix ) tex_ordprint( m ); + printf("\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{Choice of truths " ); + des_number( m, stdout ); + tex_monprint( m, "True", '-', m->designated ); + printf( "}" ); +} + + + +void tex_ordprint(MATRIX *m) +{ + if ( pordno == 1 || singlematrix ) { + if ( m->fragment[NEG] ) tex_negprint( m ); + else tex_sizprint( m ); + } + printf("\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{Order table " ); + ord_number( m, stdout ); + tex_dyprint( m, "<", '-', m->ord, "", 0, 0 ); + printf( "}" ); +} + + + +void tex_negprint(MATRIX *m) +{ + if ( pnegno == 1 || singlematrix ) tex_sizprint( m ); + printf("\n\n\\hugeskip\\noindent\\parbox[t]{160mm}{Negation table " ); + neg_number( m, stdout ); + tex_monprint( m, "~", ' ', m->neg ); + printf( "}" ); +} + + + +void tex_sizprint(MATRIX *m) +{ + if ( m->total_put > 1 ) printf( "\n\n\\newpage"); + printf( "\\noindent Size: %d", m->siz+1 ); +} + + + +void tex_nullprint(MATRIX *m, char *s, int x) +{ + printf( " \\ "); + texstring( s ); + printf( " = %d", x ); +} + + + + +void tex_monprint(MATRIX *m, char *s, char c, int arr[]) +{ + int i; + + printf("\\\\[3mm]\n\\hspace*{5mm}\\begin{tabular}{r|" ); + FORALL(m,i) putchar( 'c' ); + printf( "}\na &"); + FORALL(m,i) { + printf( " %x ", i ); + if ( i < m->siz ) putchar( '&' ); + } + printf( "\\\\\\hline\n"); + texstring( s ); + printf("(a)"); + printf( " & "); + FORALL(m,i) { + if ( c == '-' ) + texchar( arr[i]? '+': '-' ); + else printf( " %x ", arr[i] ); + if ( i < m->siz ) putchar( '&' ); + else printf( "\\\\\n"); + } + printf( "\\end{tabular}"); +} + + + + +void tex_dyprint(MATRIX *m, char *s1, char c, int arr[][SZ], + char *s2, char d, int darr[][SZ]) +{ + printf( "\\\\[3mm]\n" ); + tex_dymatprint( m, 5, s1, c, arr ); + if ( *s2 ) tex_dymatprint( m, 10, s2, d, darr); +} + + + + +void tex_dymatprint(MATRIX *m, int offset, char *s, char c, int arr[][SZ]) +{ + int i, j; + + printf( "\\hspace*{%dmm}\\begin{tabular}{r|", offset ); + FORALL(m,i) putchar( 'c' ); + printf( "}\n"); + texstring( s ); + printf( " &"); + FORALL(m,i) { + printf( " %x ", i ); + if ( i < m->siz ) putchar( '&' ); + } + printf( "\\\\\\hline\n"); + + FORALL(m,i) { + printf( "%x &", i ); + FORALL(m,j) { + if ( c == '-' ) + texchar( arr[i][j]? '+': '-' ); + else printf( " %x ", arr[i][j] ); + if ( j < m->siz ) putchar( '&' ); + else printf( "\\\\\n"); + } + } + printf( "\\end{tabular}"); +} + + + +void print_related(MATRIX *m1, MATRIX *m2, char *relname) +{ + printf("%s m%d m%d\n", relname, m1->total_got, m2->total_got); +} diff --git a/src/axiom_tests.c b/src/axiom_tests.c new file mode 100644 index 0000000..43a7c7a --- /dev/null +++ b/src/axiom_tests.c @@ -0,0 +1,583 @@ + +#include "MaGIC.h" + +/* +* This file contains the code for testing each axiom. There are +* four types of case. First are axioms which do not depend on the +* contents of the implication, or box matrices at all. These give +* rise to 0-refutations. That is, they are either true or false +* of the extensional setup. They can be tested before entry to the +* search routine. An example is the law of the excluded middle +* A v ~A which depends only on the lattice, the negation operation +* and the choice of designated values. +* +* The second group consists of axioms such as A & (A->B) -> B, +* which require one reference to the implication or box matrix. +* These are tested at the stage of setting up the search space +* and secured by taking out the impossible values (such as any x +* for cell [A->B] such that not A&x ord B). +* +* The third group consists of axioms which require a small number +* of matrix references. `Small' means 2 in MaGIC at present. An +* example is the S4 axiom !A -> !!A, any failure of which requires +* values x for !A and y for !x such that not x ord y. +* +* Finally, there are a few cases in which multiple matrix entries +* are involved. These are treated by `lazy constraint generation' +* at the testing stage after candidate matrices have been found. +*/ + + +/* +* GROUP 0 (0-refutations) +*/ + +boolean f_arrow_t() /*** f -> t ***/ +{ + return ( ord[N[des]][des] ); +} + +boolean Exmid() /*** a v ~a ***/ +{ + int a; + + FORALL(a) + if ( !desig[A[a][N[a]]]) return false; + return true; +} + + +boolean Boolalg() /*** a&~a -> b ***/ +{ + int a; + + FORALL(a) + if ( A[a][N[a]] != siz ) return false; + return true; +} + + +boolean SemiBool() /*** a&~a -> bv~b ***/ +{ + int a, b; + + FORALL(a) FORALL(b) + if ( !ord[K[a][N[a]]][A[b][N[b]]] ) return false; + return true; +} + + +boolean paradox() /*** des = T ***/ +{ + int a; + + FORALL(a) if ( !ord[a][des] ) return false; + return true; +} + + +/**********************************************************************/ + +/* +* GROUP 1 (1-refutations) +* +* The parameter here is the vector of possible-value sets. +* The general form of the procedure is +* for each relevant possible-value set s do +* for each impossible value x of s do +* remove x from s +* end for +* end for +* The vector is indexed by impindex and boxindex. Impindex[x][y] +* picks out the offset of the vector entry corresponding to the +* implication [x->y] while boxindex[x] picks out that corresponding +* to [!x]. REMOVE is a macro for taking a value out of a set. +* +* Note that no value needs to be returned by these operations, as +* only the side effect of diminishing the possible-value sets is +* intended. +*/ + +void E_assertion(unsigned info[]) /*** t->a -> a ***/ +{ + int a, b; + + FORALL(b) if ( desig[b] ) + FORALL(a) + info[impindex[b][a]] &= less_than[a]; +} + + +void contraction(unsigned info[]) /*** (a->.a->b) ->. a->b ***/ +{ + int a, b, c; + + if ( theJob->f_lat ) Wstar(info); + if ( theJob->f_n ) Reductio(info); + + FORALL(a) FORALL(b) if ( !ord[a][b] ) + FORALL(c) if ( ord[a][c] ) + REMOVE(c,info[impindex[a][b]]); +} + + +void Wstar(unsigned info[]) /*** a & (a->b) -> b ***/ +{ + int a, b, c; + + FORALL(a) FORALL(b) FORALL(c) + if ( !ord[K[a][c]][b] ) + REMOVE(c,info[impindex[a][b]]); +} + + +void Reductio(unsigned info[]) /*** a->~a -> ~a ***/ +{ + int a, b; + + if ( theJob->f_lat ) Exmid(); + FORALL(a) FORALL(b) + if ( !ord[b][N[a]] ) + REMOVE(b,info[impindex[a][N[a]]]); +} + + +void assertion(unsigned info[]) /*** 0->a = T, t->a = a ***/ +{ + int a, b; + + FORALL(a) FORALL(b) { + if ( b < siz ) REMOVE(b,info[impindex[0][a]]); + if ( b != a ) REMOVE(b,info[impindex[des][a]]); + } +} + + +void ata(unsigned info[]) /*** a ->. t->a ***/ +{ + int a; + + FORALL(a) + info[impindex[des][a]] &= greater_than[a]; +} + + +void TaT(unsigned info[]) /*** T ->. a->T ***/ +{ + int a, b; + + FORALL(a) if ( !ord[a][siz] ) { + *info = 0; + return; + } + FORALL(b) if ( b < siz ) + FORALL(a) + REMOVE(b,info[impindex[a][siz]]); +} + + +void mingle(unsigned info[]) /*** a ->. a->a ***/ +{ + int a; + + FORALL(a) + info[impindex[a][a]] &= greater_than[a]; +} + + +void t_atomic(unsigned info[]) /*** p v (p -> q) ***/ +{ + int a, b, c; + + FORALL(a) FORALL(c) if ( !desig[A[a][c]] ) + FORALL(b) + REMOVE(c,info[impindex[a][b]]); +} + + +void FF_T(unsigned info[]) /*** T ->. F -> F ***/ +{ + int a, b; + + FORALL(a) + if ( !ord[0][a] || !ord[a][siz] ) *info = 0; + + FORALL(b) if ( b < siz ) + FORALL(a) + REMOVE(b,info[impindex[0][a]]); + + if ( F_N || theJob->axiom[AxB] || theJob->axiom[AxB2] ) + FORALL(a) FORALL(b) + if ( b < siz ) + REMOVE(b,info[impindex[a][siz]]); +} + + +void PARADOX(unsigned info[]) /*** a ->. b->a ***/ +{ + int a, b; + + if ( !paradox() ) *info = 0; + FORALL(a) FORALL(b) + info[impindex[a][b]] &= greater_than[b]; +} + + +void RWX(unsigned info[]) /*** If LEM, a+1 & (a+1->0) = 0 ***/ +{ + int a, b; + + FORALL(a) + if ( !desig[A[a][N[a]]] ) return; + + FORALL(a) if ( a ) + FORALL(b) if ( K[a][b] ) + REMOVE(b,info[impindex[a][0]]); +} + + +void necessity(unsigned info[]) /* !a implies a */ +{ + int a, x; + + FORALL(a) + FORALL(x) if ( !ord[x][a] ) + REMOVE(x,info[boxindex[a]]); +} + + +void necessitation(unsigned info[]) /* des(a) ==> des(!a) */ +{ + int a, x; + + FORALL(a) if ( desig[a] ) + FORALL(x) if ( !desig[x] ) + REMOVE(x,info[boxindex[a]]); +} + + +void NKI_test(unsigned info[]) /* des(a) ==> !b implies a */ +{ + int a, b, x; + + FORALL(a) if ( desig[a] ) + FORALL(x) if ( !ord[x][a] ) + FORALL(b) + REMOVE(x,info[boxindex[b]]); +} + +/* +* This affixing check takes considerable time, but pays. +*/ + +void set_prefix(unsigned info[]) +{ + int i, j, k; + + FORALL(i) FORALL(j) + for ( k = j+1; k <=siz; k++ ) + if ( ord[j][k] && !( F_N && j < N[k] )) + if ( !(Greater_than[j] & Less_than[k]) ) + squeeze( info, i, j, i, k ); +} + +void set_suffix(unsigned info[]) +{ + int i, j, k; + + FORALL(i) FORALL(j) + for ( k = 0; k < i; k++ ) + if ( ord[k][i] && !( F_N && i < N[k] )) + if ( !(Greater_than[k] & Less_than[i]) ) + squeeze( info, i, j, k, j ); +} + +/* +* Squeeze, a subroutine of the above, squeezes out of the +* search space any values which are incompatible with this +* particular case of affixing: ord[ a->b ][ c->d ]. +*/ + +void squeeze(unsigned info[], int a, int b, int c, int d) +{ + int i; + int k, m; + + k = impindex[a][b]; m = impindex[c][d]; + + FORALL(i) { + if ( IN(i,info[k]) ) { + if ( !(greater_than[i] & info[m]) ) + REMOVE(i,info[impindex[a][b]]); + } + if ( IN(i,info[m]) ) { + if ( !(less_than[i] & info[k]) ) + REMOVE(i,info[impindex[c][d]]); + } + } +} + + +/**********************************************************************/ + +/* +* GROUP 2 (2-refutations) +*/ + +void pretest_prefix() +{ + int i, j, k; + + FORALL(i) FORALL(j) if ( !( F_N && i < N[j] )) + for ( k = siz; k > j; k-- ) if ( ord[j][k] ) + if ( !(Greater_than[j] & Less_than[k]) ) + affix_case( i, j, i, k ); +} + + +void pretest_suffix() +{ + int i, j, k; + + FORALL(i) FORALL(j) if ( !( F_N && i < N[j] )) + for ( k = 0; k < i; k++ ) if ( ord[k][i] ) + if ( !(Greater_than[k] & Less_than[i]) ) + affix_case( i, j, k, j ); +} + + +void test_S4axiom() +{ + int a, x, y; + + FORALL(x) + FORALL(y) if ( !ord[x][y] ) + FORALL(a) + new_two_ref( boxindex[a], x, boxindex[x], y ); +} + + +void test_S5axiom() +{ + int a, x, y; + + FORALL(a) + FORALL(x) if ( !ord[a][x] ) + FORALL(y) + new_two_ref( boxindex[N[a]], y, boxindex[N[y]], x ); +} + + +void test_Daxiom() +{ + int a, x, y; + + FORALL(x) + FORALL(y) if ( !ord[x][N[y]] ) + FORALL(a) + new_two_ref( boxindex[a], x, boxindex[N[a]], y ); +} + + +void test_NK() +{ + int a, b, x, y; + + FORALL(a) + FORALL(x) if ( !ord[a][x] ) + FORALL(b) + FORALL(y) + new_two_ref( boxindex[b], y, impindex[y][a], x ); +} + + + +/**********************************************************************/ + +/* +* GROUP 3 (many-refutations) +* +* The general form of the axiom test is +* for each assignment of values to the variables +* if the axiom fails +* call Ref with each cell used +* return false +* endif +* endfor +* return true +*/ + + +boolean test_mslat(trs T) +{ + int a, b, c; + + FORaLL(b) + for ( c = siz-1; c > b; c-- ) + if ( !ord[b][c] ) + FORaLL(a) + if ( K[C[a][b]][C[a][c]] != C[a][K[b][c]] ) { + Ref( impindex[a][b], T ); + Ref( impindex[a][c], T ); + Ref( impindex[a][K[b][c]], T ); + return false; + } + return true; +} + + + +boolean test_jslat(trs T) +{ + int a, b, c; + + FORaLL(a) + for ( b = siz-1; b > a; b-- ) + if ( !ord[a][b] ) + FORaLL(c) + if ( K[C[a][c]][C[b][c]] != C[A[a][b]][c] ) { + Ref( impindex[a][c], T ); + Ref( impindex[b][c], T ); + Ref( impindex[A[a][b]][c], T ); + return false; + } + return true; +} + + + +boolean Btest(trs T) +{ + int a, b, c; + + FORaLL(a) FORaLL(b) FORaLL(c) + if ( !ord[C[a][b]] [C[C[c][a]][C[c][b]]] ) { + Ref(impindex[a][b], T); + Ref(impindex[c][a], T); + Ref(impindex[c][b], T); + Ref(impindex[C[c][a]][C[c][b]], T); + return false; + } + return true; +} + +/**********************************/ + +boolean B2test(trs T) +{ + int a, b, c; + + FORaLL(a) FORaLL(b) FORaLL(c) + if ( !ord[C[a][b]] [C[C[b][c]][C[a][c]]] ) { + Ref(impindex[a][b], T); + Ref(impindex[b][c], T); + Ref(impindex[a][c], T); + Ref(impindex[C[b][c]][C[a][c]], T); + return false; + } + return true; +} + +/**********************************/ + +boolean Stest(trs T) +{ + int a, b, c; + + FORaLL(a) FORaLL(b) FORaLL(c) + if ( !ord[C[a][C[b][c]]] [C[C[a][b]][C[a][c]]] ) { + Ref(impindex[a][b], T); + Ref(impindex[b][c], T); + Ref(impindex[a][c], T); + Ref(impindex[a][C[b][c]], T); + Ref(impindex[C[a][b]][C[a][c]], T); + return false; + } + return true; +} + +/*********************************/ + +boolean Ctest(trs T) +{ + int a, b, c; + + FORaLL(a) FORaLL(c) if ( a < c ) + FORaLL(b) + if ( C[c][C[a][b]] != C[a][C[c][b]] ) { + Ref(impindex[a][b], T); + Ref(impindex[c][b], T); + Ref(impindex[a][C[c][b]], T); + Ref(impindex[c][C[a][b]], T); + return false; + } + return true; +} + +/*********************************/ + +boolean WBtest(trs T) +{ + int a, b, c; + + FORaLL(a) FORaLL(b) FORaLL(c) + if ( !ord[K[C[a][b]][C[b][c]]] [C[a][c]] ) { + Ref(impindex[a][b], T); + Ref(impindex[b][c], T); + Ref(impindex[a][c], T); + return false; + } + return true; +} + +/*********************************/ + +boolean NecImpDist(trs T) +{ + int a, b; + + FORaLL(a) FORaLL(b) + if ( !ord[box[C[a][b]]] [C[box[a]][box[b]]] ) { + Ref(impindex[a][b], T); + Ref(impindex[box[a]][box[b]], T); + Ref(boxindex[a], T); + Ref(boxindex[b], T); + Ref(boxindex[C[a][b]], T); + return false; + } + return true; +} + +/*********************************/ + +boolean NecAdj(trs T) +{ + int a, b; + + FORaLL(a) FORaLL(b) + if ( !ord[K[box[a]][box[b]]] [box[K[a][b]]] ) { + Ref(boxindex[a], T); + Ref(boxindex[b], T); + Ref(boxindex[K[a][b]], T); + return false; + } + return true; +} + +/*********************************/ + +boolean NecW(trs T) +{ + int a, b; + + FORaLL(a) FORaLL(b) + if ( !ord[C[box[a]][C[box[a]][b]]] [C[box[a]][b]] ) { + Ref(boxindex[a], T); + Ref(impindex[box[a]][b], T); + Ref(impindex[box[a]][C[box[a]][b]], T); + return false; + } + return true; +} + +/*********************************/ diff --git a/src/axioms.c b/src/axioms.c new file mode 100644 index 0000000..3465af4 --- /dev/null +++ b/src/axioms.c @@ -0,0 +1,368 @@ +/* +* axioms.c +* +* This file contains the code specific to the pre-defined +* axioms and logics. To add or remove an axiom, or to add, +* remove or alter the definition of a logic, various changes +* must be made in this file and in axioms.h, as described in +* the documentation. +*/ + +#include "MaGIC.h" + +/* +* Strings_initial simply sets the strings containing the names +* of known logics and the surface forms of known axioms +*/ + +void strings_initial() +{ + strcpy(logic_name[Null_logic], ""); + strcpy(logic_name[FD], "FD"); + strcpy(logic_name[B], "B"); + strcpy(logic_name[DW], "DW"); + strcpy(logic_name[TW], "TW"); + strcpy(logic_name[EW], "EW"); + strcpy(logic_name[RW], "C"); + strcpy(logic_name[LIN], "LIN"); + strcpy(logic_name[T], "T"); + strcpy(logic_name[E], "E"); + strcpy(logic_name[R], "R"); + strcpy(logic_name[CK], "CK"); + strcpy(logic_name[S4], "S4"); + + strcpy(ax_string[RulPref], "p -> q / (r -> p) -> (r -> q)"); + strcpy(ax_string[RulSuff], "p -> q / (q -> r) -> (p -> r)"); + strcpy(ax_string[AxKcomp], "((p -> q) & (p -> r)) -> (p -> (q & r))"); + strcpy(ax_string[AxAcomp], "((p -> r) & (q -> r)) -> ((p v q) -> r)"); + strcpy(ax_string[AxX], "p v ~p"); + strcpy(ax_string[AxBA], "(p & ~p) -> q"); + strcpy(ax_string[AxSBA], "(p & ~p) -> (q v ~q)"); + strcpy(ax_string[AxW2], "(p & (p -> q)) -> q"); + strcpy(ax_string[AxK], "p -> (q -> p)"); + strcpy(ax_string[AxK2], "p / q -> p"); + strcpy(ax_string[AxM], "p -> (p -> p)"); + strcpy(ax_string[AxRED], "(p -> ~p) -> ~p"); + strcpy(ax_string[RulC3], "p / (p -> q) -> q"); + strcpy(ax_string[AxCt], "p -> (t -> p)"); + strcpy(ax_string[Axat], "p v (p -> q)"); + strcpy(ax_string[AxTF], "T -> (F -> F)"); + strcpy(ax_string[AxC2], "p -> ((p -> q) -> q)"); + strcpy(ax_string[AxFN], "(p -> ~q) -> (q -> ~p)"); + strcpy(ax_string[AxW], "(p -> (p -> q)) -> (p -> q)"); + strcpy(ax_string[AxB], "(q -> r) -> ((p -> q) -> (p -> r))"); + strcpy(ax_string[AxB2], "(p -> q) -> ((q -> r) -> (p -> r))"); + strcpy(ax_string[AxS], "(p -> (q -> r)) -> ((p -> q) -> (p -> r))"); + strcpy(ax_string[AxC], "(p -> (q -> r)) -> (q -> (p -> r))"); + strcpy(ax_string[AxWB], "((p -> q) & (q -> r)) -> (p -> r)"); + strcpy(ax_string[RulNec], "p / !p"); + strcpy(ax_string[AxNec], "!p -> p"); + strcpy(ax_string[Ax4], "!p -> !!p"); + strcpy(ax_string[AxNID], "!(p -> q) -> (!p -> !q)"); + strcpy(ax_string[AxNand], "(!p & !q) -> !(p & q)"); + strcpy(ax_string[AxNW], "(!p -> (!p -> q)) -> (!p -> q)"); + strcpy(ax_string[AxNK], "p -> (!q -> p)"); + strcpy(ax_string[AxNKI], "p / !q -> p"); + strcpy(ax_string[Ax5], "p -> !?p"); + strcpy(ax_string[AxD], "!p -> ?p"); +} + + + + +/* +* Tests_initial sets the list of axiom test functions. +* The zero_tests are executed when the extensional +* setup has been read, before any call to transref. +* The one_tests are executed when impossible values are +* being removed from the search space inside transref. +* The two_tests are called by transref to pre-process +* two-refutations in order to avoid having to generate +* before testing them. The many_tests are the default +* test routines called when the candidate matrix has +* been generated. All tests are null by default +*/ + +void tests_initial() +{ + AXIOM a; + + for ( a = AxNull; a < AXMAX; a++ ) { + TL[a].zero_test = TL[a].many_test = 0; + TL[a].one_test = TL[a].two_test = TL[a].three_test = 0; + } + + TL[AxX].zero_test = Exmid; + TL[AxBA].zero_test = Boolalg; + TL[AxSBA].zero_test = SemiBool; + TL[AxK2].zero_test = paradox; + + TL[RulPref].one_test = set_prefix; + TL[RulSuff].one_test = set_suffix; + TL[AxW2].one_test = Wstar; + TL[AxK].one_test = PARADOX; + TL[AxM].one_test = mingle; + TL[AxRED].one_test = Reductio; + TL[RulC3].one_test = E_assertion; + TL[AxCt].one_test = ata; + TL[Axat].one_test = t_atomic; + TL[AxTF].one_test = FF_T; + TL[RulNec].one_test = necessitation; + TL[AxNec].one_test = necessity; + TL[AxNKI].one_test = NKI_test; + + TL[RulPref].two_test = pretest_prefix; + TL[RulSuff].two_test = pretest_suffix; + TL[Ax4].two_test = test_S4axiom; + TL[Ax5].two_test = test_S5axiom; + TL[AxD].two_test = test_Daxiom; + TL[AxC2].two_test = test_assertion; + TL[AxW].two_test = test_contraction; + TL[AxNK].two_test = test_NK; + + TL[AxKcomp].many_test = test_mslat; + TL[AxAcomp].many_test = test_jslat; + TL[AxNID].many_test = NecImpDist; + TL[AxNand].many_test = NecAdj; + TL[AxB].many_test = Btest; + TL[AxB2].many_test = B2test; + TL[AxS].many_test = Stest; + TL[AxC].many_test = Ctest; + TL[AxWB].many_test = WBtest; + TL[AxNW].many_test = NecW; +} + + +/* +* Logics_initial sets the arrays showing which axioms are valid in +* which logics. Note that each logic has its own subroutine, except +* for the base logics FD and B, which have no proper axioms. +*/ + +void logics_initial() +{ + LOGIC l; + AXIOM a; + + for ( l = Null_logic; l < LOGMAX; l++ ) { + for ( a = AxNull; a < AXMAX; a++ ) + valid[l][a] = false; + default_orders[l] = distributive_lattices; + default_fragment[l][n_exists] = true; + default_fragment[l][lat_exists] = true; + default_fragment[l][fus_exists] = false; + default_fragment[l][nec_exists] = false; + } + + init_B(); + init_DW(); + init_TW(); + init_EW(); + init_RW(); + init_LIN(); + init_CK(); + init_T(); + init_E(); + init_R(); + init_S4(); +} + + + +void init_B() +{ + valid[B][RulPref] = true; + valid[B][RulSuff] = true; + valid[B][AxKcomp] = true; + valid[B][AxAcomp] = true; +} + + + +void init_DW() +{ + valid[DW][AxFN] = true; + valid[DW][RulPref] = true; + valid[DW][RulSuff] = true; + valid[DW][AxKcomp] = true; + valid[DW][AxAcomp] = true; +} + + +void init_TW() +{ + valid[TW][AxFN] = true; + valid[TW][AxB] = true; + valid[TW][AxB2] = true; + valid[TW][RulPref] = true; + valid[TW][RulSuff] = true; + valid[TW][AxKcomp] = true; + valid[TW][AxAcomp] = true; +} + + +void init_EW() +{ + valid[EW][AxFN] = true; + valid[EW][AxB] = true; + valid[EW][AxB2] = true; + valid[EW][RulC3] = true; + valid[EW][RulPref] = true; + valid[EW][RulSuff] = true; + valid[EW][AxKcomp] = true; + valid[EW][AxAcomp] = true; +} + + +void init_RW() +{ + valid[RW][AxFN] = true; + valid[RW][AxB] = true; + valid[RW][AxB2] = true; + valid[RW][RulC3] = true; + valid[RW][AxCt] = true; + valid[RW][AxTF] = true; + valid[RW][AxC2] = true; + valid[RW][AxC] = true; + valid[RW][RulPref] = true; + valid[RW][RulSuff] = true; + valid[RW][AxKcomp] = true; + valid[RW][AxAcomp] = true; +} + + +void init_LIN() +{ + valid[LIN][AxFN] = true; + valid[LIN][AxB] = true; + valid[LIN][AxB2] = true; + valid[LIN][RulC3] = true; + valid[LIN][AxCt] = true; + valid[LIN][AxTF] = true; + valid[LIN][AxC2] = true; + valid[LIN][AxC] = true; + valid[LIN][RulPref] = true; + valid[LIN][RulSuff] = true; + valid[LIN][AxKcomp] = true; + valid[LIN][AxAcomp] = true; + valid[LIN][RulNec] = true; + valid[LIN][AxNec] = true; + valid[LIN][Ax4] = true; + valid[LIN][AxD] = true; + valid[LIN][AxNID] = true; + valid[LIN][AxNW] = true; + valid[LIN][AxNK] = true; + valid[LIN][AxNKI] = true; + + default_orders[LIN] = lattices; + default_fragment[LIN][nec_exists] = true; +} + + +void init_T() +{ + valid[T][AxFN] = true; + valid[T][AxB] = true; + valid[T][AxB2] = true; + valid[T][AxS] = true; + valid[T][AxX] = true; + valid[T][AxW] = true; + valid[T][AxW2] = true; + valid[T][AxWB] = true; + valid[T][AxRED] = true; + valid[T][RulPref] = true; + valid[T][RulSuff] = true; + valid[T][AxKcomp] = true; + valid[T][AxAcomp] = true; + valid[T][AxNW] = true; +} + + +void init_E() +{ + valid[E][AxFN] = true; + valid[E][AxB] = true; + valid[E][AxB2] = true; + valid[T][AxS] = true; + valid[E][RulC3] = true; + valid[E][AxX] = true; + valid[E][AxW] = true; + valid[E][AxW2] = true; + valid[E][AxWB] = true; + valid[E][AxRED] = true; + valid[E][RulPref] = true; + valid[E][RulSuff] = true; + valid[E][AxKcomp] = true; + valid[E][AxAcomp] = true; + valid[E][AxNW] = true; +} + + +void init_R() +{ + valid[R][AxFN] = true; + valid[R][AxB] = true; + valid[R][AxB2] = true; + valid[T][AxS] = true; + valid[R][RulC3] = true; + valid[R][AxCt] = true; + valid[R][AxTF] = true; + valid[R][AxC2] = true; + valid[R][AxC] = true; + valid[R][AxX] = true; + valid[R][AxW] = true; + valid[R][AxW2] = true; + valid[R][AxWB] = true; + valid[R][AxRED] = true; + valid[R][RulPref] = true; + valid[R][RulSuff] = true; + valid[R][AxKcomp] = true; + valid[R][AxAcomp] = true; + valid[R][AxNW] = true; +} + + +void init_CK() +{ + valid[CK][AxFN] = true; + valid[CK][AxB] = true; + valid[CK][AxB2] = true; + valid[CK][RulC3] = true; + valid[CK][AxCt] = true; + valid[CK][AxTF] = true; + valid[CK][AxC2] = true; + valid[CK][AxC] = true; + valid[CK][AxK] = true; + valid[CK][AxK2] = true; + valid[CK][AxM] = true; + valid[CK][RulPref] = true; + valid[CK][RulSuff] = true; + valid[CK][AxKcomp] = true; + valid[CK][AxAcomp] = true; + valid[CK][AxNKI] = true; + valid[CK][AxNK] = true; +} + + +void init_S4() +{ + valid[S4][AxFN] = true; + valid[S4][AxB] = true; + valid[S4][AxB2] = true; + valid[T][AxS] = true; + valid[S4][RulC3] = true; + valid[S4][AxX] = true; + valid[S4][AxW] = true; + valid[S4][AxW2] = true; + valid[S4][AxWB] = true; + valid[S4][AxRED] = true; + valid[S4][AxBA] = true; + valid[S4][AxSBA] = true; + valid[S4][AxK2] = true; + valid[S4][AxM] = true; + valid[S4][RulPref] = true; + valid[S4][RulSuff] = true; + valid[S4][AxKcomp] = true; + valid[S4][AxAcomp] = true; + valid[CK][AxNKI] = true; +} diff --git a/src/axioms.h b/src/axioms.h new file mode 100644 index 0000000..1c3a987 --- /dev/null +++ b/src/axioms.h @@ -0,0 +1,125 @@ +/* +* axioms.h May 1993 +* +*/ + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + + +/* +* First define the logics and axioms +*/ + +typedef enum { + Null_logic, + FD, + B, + DW, + TW, + EW, + RW, + LIN, + T, + E, + R, + CK, + S4, + LOGMAX +} LOGIC; + + + +typedef enum { + AxNull, + RulPref, + RulSuff, + AxKcomp, + AxAcomp, + AxX, + AxBA, + AxSBA, + AxW2, + AxK, + AxK2, + AxM, + AxRED, + RulC3, + AxCt, + Axat, + AxTF, + AxB, + AxB2, + AxS, + AxC2, + AxW, + AxC, + AxWB, + AxFN, + RulNec, + AxNec, + AxD, + Ax4, + Ax5, + AxNID, + AxNand, + AxNW, + AxNK, + AxNKI, + AXMAX +} AXIOM; + + +/* +* There now follow the maximum sizes for the various +* logics and fragments. +*/ + +typedef struct { + boolean (*zero_test)(); + void (*one_test)(); + void (*two_test)(); + void (*three_test)(); + boolean (*many_test)(); +} testlist; + + + +GLOBAL char + logic_name[LOGMAX][8], /* Text names of known logics */ + ax_string[AXMAX][64]; /* Text forms of known axioms */ + +GLOBAL boolean + taxiom[AXMAX], /* Tested axioms */ + valid[LOGMAX][AXMAX]; /* Axioms valid in each logic */ + +GLOBAL testlist + TL[AXMAX]; /* Test functions for axioms */ diff --git a/src/dialog.c b/src/dialog.c new file mode 100644 index 0000000..d62e63b --- /dev/null +++ b/src/dialog.c @@ -0,0 +1,568 @@ +/* +* dialog.c V2.1 (May 1993) +* +* The main interactive part of MaGIC.c. This module contains +* some of the procedures to be executed in response to menu +* selections, most of the others being in getjob.c. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + + + +/* +* The dialog function gets the job specifications from the +* user, setting up theJob accordingly. It returns 1 if +* option 'g' is selected from the menu, 0 if 'e' or 'q' is +* selected and n > 1 to change (in the parallel case) to n +* processes. +*/ + +int dialog(boolean batch, char *batchfile) +{ + if ( batch ) return read_job( batchfile ); + + for(;;) switch( menu() ) { + case 'a': add_axioms(); break; + case 'b': bad_guy(); break; + case 'c': connective(); break; + case 'd': deletion(); break; + case 'e': + case 'q': return 0; + case 'f': fragment(); break; + case 'g': return 1; + case 'h': help(0); paws(); break; + case 'i': input_direct(); break; + case 'j': jump_condition(); break; + case 'k': job_defaults(batch); break; + case 'l': logic_choice(); break; + case 'm': print_version(); break; + case 'n': + case '#': return n_of_procs(); + case 'o': order_change(); break; + case 'p': print_options(); break; + case 'r': read_job( 0 ); break; + case 's': store_job(); + } +} + + + + +/* +* Menu first ensures that the fragment is consistent with the +* selected logic, axioms and badguy. Then it displays the +* currently selected job and the menu. Finally it takes the +* next user choice of menu selection and returns the character +* code for the next action. +*/ + +char menu() +{ + char readin(); + + set_frag( true ); + if ( xdialog ) { + printf("J\n"); + outjob( stdout ); + } + else display(); + return readin("abcdefghijklmnopqrst#"); +} + + + + +/* +* Readin returns the first character from stdin which matches +* one in its parameter, converting to lower case as required. +* It then discards all characters up to and including the next +* line feed. It is used mainly to get menu selections. +*/ + +char readin(char *str) +{ + char ch; + int i; + + fflush(stdout); + for(;;) { + fgets(answer,SLEN,stdin); + ch = 0; + for ( i = 0; answer[i]; i++ ) { + if ( isupper(ch = answer[i]) ) ch = tolower(ch); + if ( strchr(str,ch) ) return ch; + } + } +} + + + + +/* +* "Pause". +*/ + +void paws() +{ + if ( xdialog ) fflush(stdout); + else { + printf("\n\n Now type RETURN to continue........."); + READLN; + } +} + + + + +/* +* Job_defaults is the initialisation procedure. It gets the +* initial choice of system and sets theJob to its defaults. +*/ + +void job_defaults( boolean batch ) +{ + int i, j; + + zero = 0; + + for ( i = 0; i < SYMBOLMAX; i++ ) { + strcpy( theJob->symtable[i].s, "" ); + theJob->symtable[i].last = theJob->symtable[i].next = 0; + } + for ( i = 0; i < 3; i++ ) + for ( j = 0; j < CMAX+8; j++ ) + theJob->symbol[i][j] = 0; + theJob->symtab = 0; + + newsymbol( "(", 0, 0 ); + newsymbol( ")", 0, 0 ); + newsymbol( ".", 0, 0 ); + newsymbol( "a", 0, 0 ); + newsymbol( "b", 0, 0 ); + newsymbol( "t", theJob->symbol[0], 0 ); + newsymbol( "f", theJob->symbol[0], 0 ); + newsymbol( "T", theJob->symbol[0], 0 ); + newsymbol( "F", theJob->symbol[0], 0 ); + newsymbol( "~", theJob->symbol[1], 0 ); + newsymbol( "!", theJob->symbol[1], 0 ); + newsymbol( "?", theJob->symbol[1], 0 ); + newsymbol( "o", theJob->symbol[2], 0 ); + newsymbol( "&", theJob->symbol[2], 0 ); + newsymbol( "v", theJob->symbol[2], 0 ); + newsymbol( "->", theJob->symbol[2], 0 ); + + strcpy( theJob->data_dir, DATA_DIR ); + + for ( i = 0; i <= CMAX; i++ ) { + theJob->dcs[i] = 0; + if ( i < CMAX ) { + theJob->defcon[i] = 0; + theJob->concut[i] = false; + } + } + + for ( i = 0; i < TMAX; i++ ) + for ( j = 0; j < RTMAX; j++ ) + theJob->croot[i][j] = theJob->proot[i][j] = 0; + theJob->failure = 0; + + for ( i = 1; i < AXMAX; i++ ) + theJob->axiom[i] = 0; + + theJob->tty_out = PRETTY; + theJob->fil_out = NONE; + theJob->outfil_name[0] = '\0'; + + theJob->maxmat = 0; + theJob->maxtime = 0; + + Sizmax = theJob->sizmax = SZ; + theJob->sizmax_ismax = true; + + wff_initial(); + + if ( !batch ) { + if ( !noclear ) { +#ifdef __CYGWIN__ + puts("\033[2J"); +#else + system("clear"); +#endif + } + theJob->logic = FD; + if ( !xdialog ) { + printf("\n This is MaGIC %s, finding matrices for", VERSION); + printf(" your favourite logic.\n"); + printf(" Matrices come in all sizes up to %dx%d.\n", + SZ,SZ); + logic_choice(); + } + } +} + + + + +/* +* Set_frag makes the selected fragment include all the used +* connectives and sets Sizmax accordingly. +*/ + +void set_frag( boolean set_sizmax ) +{ + int i, j; + + for ( i = 0; i < AXMAX; i++ ) if ( theJob->axiom[i] ) { + if ( strchr(ax_string[i],'~') || strchr(ax_string[i],'f') + || strchr(ax_string[i],'?') ) + theJob->f_n = 1; + if ( strchr(ax_string[i],'t') || strchr(ax_string[i],'f') ) + theJob->f_t = 1; + if ( strchr(ax_string[i],'&') || strchr(ax_string[i],'v') ) + theJob->f_lat = 1; + if ( strchr(ax_string[i],'!') || strchr(ax_string[i],'?') ) + theJob->f_nec = 1; + if ( i == AxK || i == AxK2 || i == AxTF ) + theJob->f_T = theJob->f_t = 1; + } + if ( theJob->f_n && valid[theJob->logic][AxC] ) + theJob->f_fus = 1; + + for ( i = 0; theJob->croot[i][0]; i++ ) { + for ( j = 0; theJob->croot[i][j]; j++ ) + if ( theJob->croot[i][j] != ABSURD ) + check_frag(theJob->croot[i][j]); + for ( j = 0; theJob->proot[i][j]; j++ ) + if ( theJob->proot[i][j] != TRIVIAL ) + check_frag(theJob->proot[i][j]); + } + if ( theJob->failure ) + check_frag(theJob->failure); + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] != PRIMITIVE ) + check_frag(theJob->defcon[i]); + + if ( theJob->totord ) + theJob->f_lat = 1; + if ( theJob->f_lat ) + theJob->f_t = theJob->f_T = theJob->f_F = 1; + if (( theJob->f_F || theJob->f_T ) && theJob->f_n ) + theJob->f_F = theJob->f_T = 1; + + F_N = + ((theJob->f_n && valid[theJob->logic][AxFN]) + || theJob->axiom[AxFN]); + afx = + ((valid[theJob->logic][RulPref] && valid[theJob->logic][RulSuff]) + || (theJob->axiom[RulPref] && theJob->axiom[RulSuff])); + + if ( set_sizmax ) { + if ( !(theJob->f_lat || theJob->f_n) ) + Sizmax = theJob->f_t? S_pot: S_pO; + else if ( !theJob->f_lat ) + Sizmax = theJob->f_t? S_pont: S_poN; + else if ( !theJob->distrib ) + Sizmax = theJob->f_n? S_ln: S_lat; + else if ( !theJob->totord ) + Sizmax = ((theJob->f_n && theJob->logic==S4) + || theJob->axiom[AxBA])? S_ba: + (theJob->f_n? S_dln: S_dlat); + else Sizmax = theJob->f_n? S_Ton: S_to; + + if ( theJob->sizmax > Sizmax || theJob->sizmax_ismax ) + theJob->sizmax = Sizmax; + } +} + + + + +/* +* This function adds to the fragment any connectives which +* occur in the given formula. It attends to the main +* connective only, calling itself recursively to deal with +* the subformulae. +*/ + +void check_frag(int x) +{ + if (!x) return; + if (!theJob->form[x].sym->s[1]) { + switch(theJob->form[x].sym->s[0]) { + case '~': theJob->f_n = 1; break; + case 'f': theJob->f_n = 1; + case 't': theJob->f_t = 1; break; + case 'T': theJob->f_T = 1; break; + case 'F': theJob->f_F = 1; break; + case 'o': theJob->f_fus = 1; break; + case '?': theJob->f_n = 1; + case '!': theJob->f_nec = 1; break; + case '&': + case 'v': theJob->f_lat = 1; + } + } + check_frag(theJob->form[x].lsub); + check_frag(theJob->form[x].rsub); +} + + + + +/* +* Print_axiom just writes out axiom #x on stream f. +*/ + +void print_axiom(FILE *f, AXIOM x) +{ + fprintf(f, "%s", ax_string[x]); +} + + + + +/* +* Display shows the current choices and the menu. +*/ + +void display() +{ + if ( !noclear ) { +#ifdef __CYGWIN__ + puts("\033[2J"); +#else + system("clear"); +#endif + } + disp(stdout); + printf(" A)xiom B)adguy C)onnective D)elete\n"); + printf(" E)xit F)ragment G)enerate H)elp\n"); + printf(" I)O J)ump K)ill L)ogic\n"); + printf(" M)aGIC N)o. Procs O)rder P)rint Opts\n"); + printf(" Q)uit R)ead S)tore "); +} + + + + +/* +* Disp is the function to print an account of the current +* contents of theJob. It is to have verbose and terse modes. +*/ + +void disp(FILE *f) +{ + AXIOM ax; + int i, j; + boolean b; + +#ifdef PARALLEL + fprintf(f, "\n Parallel MaGIC running %d out of %d processes\n", + gm->nprocs, PARALLEL); +#endif + + if ( theJob->totord && default_orders[theJob->logic] != total_orders ) + fprintf(f, "\n Logic:%8cT%s", ' ', logic_name[theJob->logic]); + else if ( theJob->distrib && + default_orders[theJob->logic] != distributive_lattices ) + fprintf(f, "\n Logic:%8cD%s", ' ', logic_name[theJob->logic]); + else if ( !theJob->distrib && + default_orders[theJob->logic] != lattices ) + fprintf(f, "\n Logic:%8cL%s", ' ', logic_name[theJob->logic]); + else fprintf(f, "\n Logic:%8c%s", ' ', logic_name[theJob->logic]); + + b = false; + for ( ax = AxNull+1; ax < AXMAX; ax++ ) if ( theJob->axiom[ax] ) { + if ( !b ) { + b = true; + fprintf(f, "\n\n Plus: "); + } + else fprintf(f, "\n "); + print_axiom(f,ax); + } + + for ( i = 0; theJob->croot[i][0]; i++ ) { + if ( i ) fprintf(f, "\n "); + else fprintf(f, "\n\n Extra: "); + for ( j = 0; theJob->proot[i][j]; j++ ) { + if ( j ) fprintf(f, ", "); + if ( theJob->proot[i][j] != TRIVIAL ) + outfml(theJob->proot[i][j], + theJob->proot[i][j],f); + } + if ( theJob->proot[i][0] != TRIVIAL ) fprintf(f, " / "); + for ( j = 0; theJob->croot[i][j]; j++ ) { + if ( j ) fprintf(f, ", "); + if ( theJob->croot[i][j] != ABSURD ) + outfml(theJob->croot[i][j], + theJob->croot[i][j],f); + } + } + + fprintf(f, "\n\n Fragment: ->"); + if ( theJob->f_lat ) fprintf(f, ", &, v"); + if ( theJob->f_n ) fprintf(f, ", ~"); + if ( theJob->f_fus ) fprintf(f, ", o"); + if ( theJob->f_nec ) { + fprintf(f, ", !"); + if ( theJob->f_n ) fprintf(f, ", ?"); + } + if ( theJob->f_t ) { + fprintf(f, ", t"); + if ( theJob->f_n ) fprintf(f, ", f"); + } + if ( theJob->f_T ) fprintf(f, ", T"); + if ( theJob->f_F ) fprintf(f, ", F"); + + for ( i = 0; theJob->defcon[i]; i++ ) { + if ( i ) fprintf(f, "\n "); + else if ( !theJob->defcon[1] ) fprintf(f, "\n\n Definition: "); + else fprintf(f, "\n\n Definitions:"); + if ( !theJob->adicity[i] ) + fprintf(f, " %s ", theJob->dcs[i]->s); + else if ( theJob->adicity[i] == 1 ) { + if ( theJob->dcs[i]->s[1] ) + fprintf(f, " %s a ", theJob->dcs[i]->s); + else fprintf(f, " %sa ", theJob->dcs[i]->s); + } + else fprintf(f, " a %s b ", theJob->dcs[i]->s); + if ( theJob->defcon[i] == PRIMITIVE ) { + fprintf(f, "Primitive"); + if ( theJob->concut[i] ) fprintf(f, " (cut)"); + } + else outfml(theJob->defcon[i], theJob->defcon[i],f); + } + + if ( theJob->failure ) { + fprintf(f, "\n\n Fail: "); + outfml(theJob->failure, theJob->failure, f); + } + + fprintf(f, "\n\n TTY output: %s\n File output: %s", + (theJob->tty_out==NONE? "none": + (theJob->tty_out==UGLY? "ugly": + (theJob->tty_out==PRETTY? "pretty": "summary"))), + (theJob->fil_out==NONE? "none": + (theJob->fil_out==UGLY? "ugly": + (theJob->fil_out==PRETTY? "pretty": "summary")))); + if ( theJob->fil_out ) + fprintf(f,"\n Output file: \"%s\"", theJob->outfil_name); + + fprintf(f, "\n\n Search concludes "); + if (theJob->maxtime) + fprintf(f, "after %d seconds\n or ", theJob->maxtime); + if (theJob->maxmat) + fprintf(f, "when %d matri%s found\n or ", + theJob->maxmat, (theJob->maxmat==1? "x": "ces")); + fprintf(f, "when size %d finished.\n\n\n", theJob->sizmax); +} + + + + + +/* +* The Help facility is extremely primitive. It simply transfers +* the contents of a short file to stdout. The parameter codes +* the place in the program from which the Help call was made. +*/ + +void help(helpcode x) +{ + switch( x ) { + case MEN: + put_out("MEN"); + break; + case WFF1: + case WFF2: + put_out("WFF"); + break; + case FDL: + put_out("FDL"); + break; + case BTW: + put_out("BTW"); + break; + case LOG: + put_out("LOG"); + break; + case OUT: + put_out("OUT"); + break; + case HELPMAX: + break; + } +} + + + + +/* +* This is essentially a macro for Help (above). +* It prepends the data directory name, appends ".show" +* and does the file transfer. +* +* If we are in terse mode, nothing happens because the +* front end xmagic has its own Help routine. +*/ + +void put_out(char *f_nm) +{ + FILE *f1; + char s[100]; + + if ( !noclear ) { +#ifdef __CYGWIN__ + puts( "\033[2J" ); + strcpy(s,DATA_DIR); + strcat(s,f_nm); + strcat(s,".show"); + if ( (f1 = fopen(s,"r")) == NULL ) { + printf("Cannot open %s.",s); + } + else { + while (fgets(s,80,f1) != NULL) { + printf("%s",s); + } + fclose(f1); + } +#else + sprintf(s,"clear; more %s%s.show", DATA_DIR, f_nm); + system( s ); +#endif + } +} diff --git a/src/embedded.c b/src/embedded.c new file mode 100644 index 0000000..f125d85 --- /dev/null +++ b/src/embedded.c @@ -0,0 +1,11 @@ +#include "RM.h" +#include "hmi.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) + Abort("synopsis: embedding ",-1); + relatemats(embedding, argv[1], "subalgebra"); + + return 0; +} diff --git a/src/getjob.c b/src/getjob.c new file mode 100644 index 0000000..5d7282f --- /dev/null +++ b/src/getjob.c @@ -0,0 +1,1298 @@ +/* +* getjob.c V2.1 (May 1993) +* +* This file contains most of the functions called from dialog.c +* which fill in the various fields of theJob for MaGICal +* purposes. The functions called immediately following menu +* selections come first, then the subsidiary ones. Note that +* most of the formula-handling functions are in separate files. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + + + +/* +* Response to menu option (a). This causes the axioms to be +* listed on the screen with numbers and asks for a selection. +* Note that "user-defined axiom" gets a number bigger than +* any of the listed axioms. +* +* Listed axioms are marked as selected just by setting the +* appropriate flags. User defined ones have to be read in by +* the function got_formula. +*/ + +void add_axioms() +{ + int i, j; + int axint; + AXIOM ax; + AXIOM local_ax[AXMAX+2]; + + j = 1; + if ( !xdialog ) { + for ( ax = AxNull+1; ax < AXMAX; ax++ ) + if ( !valid[theJob->logic][ax] && !theJob->axiom[ax] ) { + local_ax[j] = ax; + printf("\n %-5d%s", j++, ax_string[ax]); + } + printf("\n\n %-5d< User-defined axiom >\n", j); + printf(" %-5d< User-defined rule >", j+1); + local_ax[j] = AXMAX; + local_ax[j+1] = AXMAX+1; + printf("\n\n Select from the above by typing the numbers: "); + fflush(stdout); + } + else for ( ax = AxNull; ax < AXMAX+2; ax++ ) local_ax[ax] = ax; + fgets(answer,SLEN,stdin); + for ( i = 0; answer[i]; i++ ) + if ( !isdigit(answer[i]) ) answer[i] = ' '; + + for ( i = 0; answer[i]; i++ ) + if ( !i || (answer[i-1] == ' ' && answer[i] != ' ') ) { + sscanf( answer+i, "%d", &axint ); + ax = (AXIOM)axint; + if ( ax > AxNull && ax < AXMAX+2 ) + add_one_axiom( local_ax[ax] ); + } +} + + + +void add_one_axiom( AXIOM select ) +{ + int i, j; + + if ( select < AXMAX ) theJob->axiom[select] = true; + else { + for ( i = 0; theJob->croot[i][0] && iproot[i][0] = TRIVIAL; + } + else { + if ( !xdialog ) { + printf("\n Type the premise(s) of the "); + printf("rule, one to a line.\n When there"); + printf(" are no more to come, just hit "); + printf("RETURN.\n"); + } + for ( j = 0; j < RTMAX; j++ ) { + if ( !got_formula( 1, i, j, "premise" )) + break; + } + if ( !xdialog ) { + printf("\n\n Now the conclusion(s) of the"); + printf(" rule, one to a line.\n When there"); + printf(" are no more to come, just hit "); + printf("RETURN.\n"); + } + for ( j = 0; j < RTMAX; j++ ) { + if ( !got_formula( 0, i, j, "conclusion" )) + break; + } + if ( !theJob->croot[i][0] ) + theJob->croot[i][0] = ABSURD; + if ( !theJob->proot[i][0] ) + theJob->proot[i][0] = TRIVIAL; + if ( theJob->proot[i][0] == TRIVIAL + && theJob->croot[i][0] == ABSURD ) + theJob->proot[i][0] = theJob->croot[i][0] = 0; + } + } +} + + + + + +/* +* Response to menu option (b). +* Take the user definition of a formula to go in failure. +*/ + +void bad_guy() +{ + if ( !xdialog ) puts(""); + got_formula( 2, 0, 0, "formula" ); +} + + + + + +/* +* Response to menu option (c). +* Take the user's specification of a new operator, find its +* adicity and parse its definition if any. Undefined +* connectives are allowed: their notional definitions are +* set to the constant PRIMITIVE. +*/ + +void connective() +{ + int offset; + char newcon[SLEN]; + symb sy; + + for ( offset = 0; theJob->defcon[offset] && offsetsymtab; sy; sy = sy->next ) + if ( !strcmp(newcon,sy->s) ) { + EP; printf("\n No fair: \"%s\" already means something.",newcon); + paws(); + return; + } + + if ( !xdialog ) { + printf("\n What is the adicity of %s? ", newcon); + fflush(stdout); scanf("%d", theJob->adicity+offset); READLN; + } + if ( theJob->adicity[offset]<0 || theJob->adicity[offset]>2 ) { + printf("\n No fair: adicity must be 0, 1 or 2."); + paws(); + return; + } + + if ( !xdialog ) + printf("\n D)efined or u)ndefined? "); + if ( readin("du") == 'u' ) { + theJob->defcon[offset] = PRIMITIVE; + if ( !xdialog ) + printf("\n Place a \"cut\" on %s? (y/n) ", newcon); + theJob->concut[offset] = (readin("yn") == 'y'); + } + else { + if ( !xdialog ) { + if ( !theJob->adicity[offset] ) + printf("\n Define %s ", newcon); + else if ( theJob->adicity[offset] == 1 ) + printf("\n Define %s(a) ", newcon); + else printf("\n Define a %s b ", newcon); + fflush(stdout); + } + theJob->defcon[offset] = 0; + if ( !got_formula( IN_DEFN, offset, 0, "definition" )) return; + } + + sy = newsymbol( newcon, + theJob->symbol[theJob->adicity[offset]], + theJob->dcs ); + if ( !theJob->adicity[offset] ) + theJob->form[VMAX+4+offset].sym = sy; +} + + + + + +/* +* Response to menu option (d). +* The user is offered a choice of deleting an axiom from the +* list, a user-defined axiom, a connective or the badguy. +* Appropriate action is taken to scrub the unwanted item +* from the job specification. +*/ + +void deletion() +{ + if ( !xdialog ) { + printf("\n Delete (a) pre-defined axiom?"); + printf("\n (b) bad guy?"); + printf("\n (c) connective?"); + printf("\n (d) user-defined axiom? "); + fflush(stdout); + } + switch( readin("abcd") ) { + case 'a': delete_saxiom(); break; + case 'b': theJob->failure = 0; break; + case 'c': delete_connective(); break; + case 'd': delete_uaxiom(); + } +} + + + + + +/* +* Menu option (e) is for "exit", so there is no response to +* it in this file. Fragment is the response to option (f). +* +* There is a certain amount of detail involved in settling +* the fragment, since some combinations of connectives allow +* others to be defined. The calculation of these forced +* extensions to the fragment is done here on the fly in +* order to avoid unnecessary questions. +* +* In terse mode, all such fiddling is bypassed. +*/ + +void fragment() +{ + boolean old_nec; + + old_nec = theJob->f_nec; + if ( noclear ) { + if ( !xdialog ) { + printf("\n\n Select which connectives you want"); + printf("\n\n ~ (y/n) "); + } + theJob->f_n = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" & and v (y/n) "); + theJob->f_lat = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" t (y/n) "); + theJob->f_t = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" T (y/n) "); + theJob->f_T = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" F (y/n) "); + theJob->f_F = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" o (y/n) "); + theJob->f_fus = ( readin("yn") == 'y' ); + if ( !xdialog ) printf(" ! (y/n) "); + theJob->f_nec = ( readin("yn") == 'y' ); + } + else { + theJob->f_n = theJob->f_lat = theJob->f_t = 0; + theJob->f_T = theJob->f_F = theJob->f_fus = 0; + theJob->f_nec = 0; + set_frag( false ); + if ( !theJob->f_n ) { + printf( + "\n\n Do you want negation defined? (y/n) "); + theJob->f_n = ( readin("yn") == 'y' ); + } + if ( !theJob->f_lat ) { + printf("\n Do you want & and v defined? (y/n) "); + theJob->f_lat = ( readin("yn") == 'y' ); + if ( theJob->f_lat ) theJob->f_t = 1; + else { + printf("\n Do you want constant "); + printf("t defined? (y/n) "); + theJob->f_t = ( readin("yn") == 'y' ); + } + } + if ( theJob->f_lat ) + theJob->f_T = theJob->f_F = 1; + if ( !theJob->f_T ) { + printf("\n Do you want constant "); + printf("T defined? (y/n) "); + theJob->f_T = ( readin("yn") == 'y' ); + if ( theJob->f_T && theJob->f_n ) theJob->f_F = 1; + } + if ( !theJob->f_F ) { + printf("\n Do you want constant "); + printf("F defined? (y/n) "); + theJob->f_F = ( readin("yn") == 'y' ); + } + if ( theJob->f_n && valid[theJob->logic][AxC] ) + theJob->f_fus = 1; + if ( !theJob->f_fus ) { + printf("\n Do you want fusion "); + printf("o defined? (y/n) "); + theJob->f_fus = ( readin("yn") == 'y' ); + } + if ( !theJob->f_nec ) { + printf("\n Do you want necessity "); + printf("! defined? (y/n) "); + theJob->f_nec = (readin("yn") == 'y'); + } + } + if ( theJob->f_nec && !old_nec ) { + theJob->axiom[RulNec] = + !valid[theJob->logic][RulNec]; + theJob->axiom[AxNID] = + !valid[theJob->logic][AxNID]; + if ( theJob->f_lat && valid[theJob->logic][AxW] ) + theJob->axiom[AxNand] = + !valid[theJob->logic][AxNand]; + } +} + + + + +/* +* Menu options (g) for "generate" and (h) for "help" do not +* cause any action in this file. +* +* Response to menu option (i). +*/ + +void input_direct() +{ + char dd[SLEN]; + + if ( !xdialog ) { + printf(" \n Current input directory: %s\n", + theJob->data_dir); + printf(" \n New input directory: "); + fflush(stdout); + } + fgets(dd,SLEN,stdin); + nospace(dd); + if ( !*(dd) ) return; + + if ( dd[strlen(dd)-1] != '/' ) strcat( dd, "/" ); + strcpy( theJob->data_dir, dd ); +} + + + + +/* +* Response to menu option (j). +* This passage of dialogue gets the conditions on which +* the search is to be terminated. There is nothing to +* explain here. +*/ + +void jump_condition() +{ + char oc; + + if ( xdialog ) { + scanf("%d", &(theJob->maxmat)); + scanf("%d", &(theJob->sizmax)); + scanf("%d", &(theJob->maxtime)); + return; + } + printf("\n Shall I stop when:"); + printf(" (a) I'm exhausted?\n"); + printf("%20c(b) time's up?\n", ' '); + printf("%20c(c) I've found enough matrices?\n", ' '); + printf("%20c(d) the matrices get too big?\n", ' '); + printf("%20c(e) a combination of the above? ", ' '); + oc = readin("abcde"); + theJob->sizmax = Sizmax; + theJob->maxtime = theJob->maxmat = 0; + theJob->sizmax_ismax = 1; + switch( oc ) { + case 'c': + printf("\n How many is enough? "); + fflush(stdout); + scanf("%d", &(theJob->maxmat)); + if ( theJob->maxmat < 0 ) + theJob->maxmat = 0; + break; + case 'd': + printf("\n How big is big enough? "); + fflush(stdout); + scanf("%d", &(theJob->sizmax)); + if ( theJob->sizmax > 1 ) + theJob->sizmax_ismax = 0; + break; + case 'e': + printf("\n How many matrices are enough? "); + fflush(stdout); + scanf("%d", &(theJob->maxmat)); + if ( theJob->maxmat < 0 ) + theJob->maxmat = 0; + printf("\n How big can they get? "); + fflush(stdout); + scanf("%d", &(theJob->sizmax)); + if ( theJob->sizmax > 1 ) + theJob->sizmax_ismax = 0; + case 'b': + printf("\n How many seconds have I got? "); + fflush(stdout); + scanf("%d", &(theJob->maxtime)); + if ( theJob->maxtime <= 0 ) + theJob->maxtime = 0; + else if ( theJob->maxtime < 5 ) + theJob->maxtime = 5; + } +} + + + + +/* +* Initially and in response to menu option (l) the user is +* offered the choice of logics pre-defined in MaGIC.h. +* +* Any logic name may be prefixed with 'T' if total orders are +* required, or 'L' if non-distributive lattices will do. +*/ + +void logic_choice() +{ + LOGIC lptr; + + if ( !xdialog ) { + printf("\n\n What is your favourite logic? "); + fflush(stdout); + } + fgets(answer,SLEN,stdin); + trim(); + + for ( lptr = Null_logic+1; lptr < LOGMAX; lptr++ ) + if ( !strcmp(logic_name[lptr],answer) ) { + set_logic( lptr ); + return; + } + + if ( *(answer)=='L' || *(answer)=='T' || *(answer)=='D' ) + for ( lptr = Null_logic+1; lptr < LOGMAX; lptr++ ) + if ( !strcmp(logic_name[lptr],answer+1) ) { + set_logic( lptr ); + theJob->totord = (*(answer)=='T'); + theJob->distrib = ((*(answer)=='T') || (*(answer)=='D')); + return; + } + + if ( *answer ) + printf("\n\n There is no such logic as \"%s\".", answer); + printf("\n\n Logics are"); + for ( lptr = 1; lptr < LOGMAX; lptr++ ) + printf("%s%s",(lptr==1? " ":(lptr+1==LOGMAX? " and ": ", ")), + logic_name[lptr]); + printf("\n Prefix \"D\" for distribution, "); + printf("\"L\" for no distribution, "); + printf("\"T\" for total order."); + printf("\n\n\n H)elp, R)eselect or Q)uit? "); + switch( readin("hrq") ) { + case 'q': exit(0); + case 'h': + help(FDL); paws(); + help(BTW); paws(); + help(LOG); + } + logic_choice(); +} + + + + +/* +* Set the currently selected logic to lptr. +* Set the default order type and fragment for that logic. +*/ + +void set_logic( LOGIC lptr ) +{ + theJob->logic = lptr; + switch( default_orders[lptr] ) { + case lattices: + theJob->distrib = false; + theJob->totord = false; + break; + case distributive_lattices: + theJob->distrib = true; + theJob->totord = false; + break; + case total_orders: + theJob->distrib = true; + theJob->totord = true; + } + theJob->f_n = default_fragment[lptr][n_exists]; + theJob->f_lat = default_fragment[lptr][lat_exists]; + theJob->f_fus = default_fragment[lptr][fus_exists]; + theJob->f_nec = default_fragment[lptr][nec_exists]; + theJob->f_t = theJob->f_T = theJob->f_F = theJob->f_lat; +} + + + + +/* +* Response to menu option (m). +* Print the version number, release date and author's ID. +*/ + +void print_version() +{ + printf("\n\n\n This is MaGIC Version %s, released %s.\n\n", + VERSION, RELEASE_DATE); + printf(" Author and target for bug reports:\n"); + printf("\t J. Slaney\n\t Australian National University\n"); + printf("\t John.Slaney@anu.edu.au\n\n\n\n"); + paws(); +} + + + + + +/* +* The sequential MaGIC makes no response to menu option (n). +* The parallel version, however, allows the user to change +* the number of processes by means of this option. +* To be honest, this feature was included mainly to provide +* a working toy for the xmagic display panel. +* +* The value returned is the new number of processes unless +* we are sequential, in which case the return value is -1. +* Note that this function does not actually create or destroy +* processes, but merely returns the desired number. +* +* The old convention for changing the number of processes +* was to type '#' followed by the number. This is still +* supported, although the normal call will be via menu (n). +*/ + +int n_of_procs() +{ +#ifdef PARALLEL + if (!(npcp = strchr(answer,'#'))) { + printf("\n\n How many processes do you want? "); + fgets(npcp,10,stdin); + nospace(npcp); + } + while ( *npcp && ( *npcp < '0' || *npcp > '9' )) npcp++; + if ( !*npcp ) return(gm->nprocs); + i = 0; + do { + i = i*10 + *npcp - '0'; + npcp++; + } + while ( *npcp >= '0' && *npcp <= '9' ); + if ( i < 2 || i > PARALLEL ) { + printf("\n Number must be in range 2 - %d\n\n", PARALLEL); + paws(); + return(gm->nprocs); + } + return(i); +#endif + return(-1); +} + + + + + +/* +* Response to menu option (o). +* Re-order symbols[2] to determine scope conventions, or +* re-order theJob->dcs to determine change order. +*/ + +void order_change() +{ + symb primitives[CMAX]; + symb dyadics[CMAX+8]; + int i, j; + int neworder[CMAX+8]; + + j = 0; + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + primitives[j++] = theJob->dcs[i]; + primitives[j] = 0; + if ( j < 2 ) { + user_order( theJob->symbol[2], + "order of dyadic connectives", neworder ); + for ( i = 0; theJob->symbol[2][i]; i++ ) + dyadics[i] = theJob->symbol[2][neworder[i]]; + dyadics[i] = 0; + for ( i = 0; dyadics[i]; i++ ) + theJob->symbol[2][i] = dyadics[i]; + return; + } + if ( !xdialog ) { + printf("\n\n You may alter (\"s\") the scope ordering of"); + printf(" the\n dyadic connectives, or (\"c\") the change"); + printf(" order of\n the user-defined connectives."); + printf("\n\n Which do you want to do? (s/c): "); + fflush(stdout); + } + switch( readin("cs") ) { + case 's': + user_order( theJob->symbol[2], + "order of dyadic connectives", neworder ); + for ( i = 0; theJob->symbol[2][i]; i++ ) + dyadics[i] = theJob->symbol[2][neworder[i]]; + dyadics[i] = 0; + for ( i = 0; dyadics[i]; i++ ) + theJob->symbol[2][i] = dyadics[i]; + break; + case 'c': + user_order( primitives, "change order", neworder ); + j = 0; + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + theJob->dcs[i] = primitives[neworder[i]]; + break; + } +} + + + + +/* +* Shuffle symbols according to instructions. +* The second parameter describes the order in question. +*/ + +void user_order(symb oldsymbols[], char *string, int neworder[]) +{ + int i, j, mxs; + + for ( mxs = 0; oldsymbols[mxs]; mxs++ ) ; + + if ( !xdialog ) { + printf("\n\n Current %s:\n\n", string); + for ( i = 0; i < mxs; i++ ) { + printf(" %-3d", i+1); + for ( j = 1; j < strlen(oldsymbols[i]->s); j++ ) + putchar(' '); + } + puts(""); + for ( i = 0; i < mxs; i++ ) + printf(" %s ", oldsymbols[i]->s); + printf("\n\n Type the new order (e.g. 2 1 3 etc)\n"); + } + for ( i = 0; i < mxs; i++ ) + scanf("%d", neworder+i); + if ( xdialog ) return; + + for ( i = 0; i < mxs; i++ ) { + if ( neworder[i] < 1 || neworder[i] > mxs ) neworder[i] = 0; + for ( j = 0; j < i; j++ ) + if ( neworder[j] == neworder[i] ) neworder[i] = 0; + } + for ( i = 0; i < mxs; i++ ) if ( !neworder[i] ) { + for ( j = i; j < mxs-1; j++ ) neworder[j] = neworder[j+1]; + neworder[mxs-1] = 0; + } + for ( i = 1; i <= mxs; i++ ) { + for ( j = 0; j < mxs; j++ ) if ( neworder[j] == i ) break; + if ( j == mxs ) { + for ( j = 0; neworder[j]; j++ ) ; + neworder[j] = i; + } + } + for ( i = 0; i < mxs; i++ ) neworder[i]--; +} + + + + +/* +* Response to menu option (p). +* The print settings for tty and file are set independently. +* If file output is requested, the output file name is set. +* All of this is perfectly straightforward. +*/ + +void print_options() +{ + char oc; + + if ( !xdialog ) printf("\n\n"); + do { + if ( !xdialog ) + printf( + "\n Tty: P)retty U)gly S)ummary N)one H)elp "); + oc = readin("pusnh"); + theJob->tty_out = oc=='u'? UGLY: (oc=='p'? PRETTY: + (oc=='s'? SUMMARY: NONE)); + if (oc=='h') help(OUT); + } + while ( oc == 'h' ); + do { + if ( !xdialog ) + printf( + "\n File: P)retty U)gly S)ummary N)one H)elp "); + oc = readin("pusnh"); + theJob->fil_out = oc=='u'? UGLY: (oc=='p'? PRETTY: + (oc=='s'? SUMMARY: NONE)); + if ( oc == 'h' ) help(OUT); + } + while ( oc == 'h' ); + + if (theJob->fil_out) { + if ( !xdialog && *(theJob->outfil_name) ) { + printf("\n New output file? (y/n) "); + if ( readin("yn") == 'n' ) return; + } + if ( filing ) { + fclose(outfil); + filing = 0; + } + do { + if ( !xdialog ) { + printf(" \n Name output file: "); + fflush(stdout); + } + fgets(theJob->outfil_name,SLEN,stdin); + nospace(theJob->outfil_name); + } + while ( !*(theJob->outfil_name) ); + if ( !xdialog && (outfil = fopen(theJob->outfil_name,"r")) ) { + fclose(outfil); + printf("\n File \"%s\" ",theJob->outfil_name); + printf("already exists. Overwrite it? (y/n) "); + fflush(stdout); + if ( (oc = readin("yn")) == 'n' ) { + strcpy(theJob->outfil_name,""); + theJob->fil_out = false; + } + } + } + else strcpy(theJob->outfil_name,""); +} + + + + +/* +* Menu option (q) for "quit" does not require action here. +* +* Response to menu option (r). +* This function prompts the user for a filename, opens the +* file and reads the specification of theJob, expected to +* have been placed there previously by function store_job. +*/ + +boolean read_job(char *batchfile) +{ + int i; + char fname[SLEN]; + FILE *f; + + if ( batchfile ) { + i = 0; + while ( (fname[i] = batchfile[i]) ) i++; + } + else { + if ( !xdialog ) { + printf("\n\n Name the file containing the job"); + printf(" you want: "); + fflush(stdout); + } + fgets(fname,SLEN,stdin); + nospace(fname); + } + if ( !(f = fopen( fname, "r" ))) { + EP + fprintf(stderr,"\n Can't find file %s (sorry).\n\n", fname); + if ( !batchfile ) paws(); + return false; + } + injob(f); + if ( batchfile ) set_frag( true ); + return true; +} + + + + +/* +* Response to menu option (s). +* Get the name of a file in which to store the job, +* then do it. +*/ + +void store_job() +{ + FILE *f; + char fname[SLEN]; + + if ( !xdialog ) + printf("\n\n Name the file where you want the job stored: "); + fgets(fname,SLEN,stdin); + nospace(fname); + if ( !(f = fopen( fname, "w" ))) { + EP printf("\n Can't open that file (sorry).\n\n"); + paws(); + return; + } + outjob(f); + fclose(f); +} + + + + + +/* +* This grabs the contents of theJob in the format in which they +* are dumped by outjob. It is called by MaGIC in response to +* menu option "r" and by xmagic to get the job description after +* each update. +*/ + +int nint(FILE *f) +{ + int ni; + + fscanf(f, "%d", &ni); + return ni; +} + + +void injob(FILE *f) +{ + int i, j, k; + int nint(); + + for ( i = 0; i < AXMAX; i++ ) + theJob->axiom[i] = (nint(f)? true: false); + + for ( i = 0; i < CMAX; i++ ) + theJob->adicity[i] = nint(f); + + for ( i = 0; i < TMAX; i++ ) + for ( j = 0; j < RTMAX; j++ ) + theJob->croot[i][j] = nint(f); + + for ( i = 0; i < TMAX; i++ ) + for ( j = 0; j < RTMAX; j++ ) + theJob->proot[i][j] = nint(f); + + for ( i = 0; i < CMAX; i++ ) + theJob->defcon[i] = nint(f); + + for ( i = 0; i < CMAX; i++ ) + theJob->concut[i] = (nint(f)? true: false); + + theJob->failure = nint(f); + theJob->logic = nint(f); + + theJob->f_n = (nint(f)? true: false); + theJob->f_lat = (nint(f)? true: false); + theJob->f_t = (nint(f)? true: false); + theJob->f_T = (nint(f)? true: false); + theJob->f_F = (nint(f)? true: false); + theJob->f_fus = (nint(f)? true: false); + theJob->f_nec = (nint(f)? true: false); + + theJob->maxtime = nint(f); + theJob->maxmat = nint(f); + theJob->sizmax = nint(f); + + theJob->sizmax_ismax = (nint(f)? true: false); + theJob->totord= (nint(f)? true: false); + theJob->distrib = (nint(f)? true: false); + + theJob->tty_out = nint(f); + theJob->fil_out = nint(f); + + i = 0; + do theJob->data_dir[i] = nint(f); + while ( theJob->data_dir[i++] ); + + i = 0; + do theJob->outfil_name[i] = nint(f); + while ( theJob->outfil_name[i++] ); + + i = 0; + do logic_name[theJob->logic][i] = nint(f); + while ( logic_name[theJob->logic][i++] ); + + for ( i = 0; i < SYMBOLMAX; i++ ) { + for ( j = 0; j < 16; j++ ) + theJob->symtable[i].s[j] = nint(f); + if ( (k = nint(f)) >= 0 ) + theJob->symtable[i].last = theJob->symtable + k; + else theJob->symtable[i].last = 0; + if ( (k = nint(f)) >= 0 ) + theJob->symtable[i].next = theJob->symtable + k; + else theJob->symtable[i].next = 0; + } + + for ( i = 0; i < 3; i++ ) + for ( j = 0; j < CMAX+8; j++ ) + if ( (k = nint(f)) < 0 ) theJob->symbol[i][j] = 0; + else theJob->symbol[i][j] = theJob->symtable + k; + + for ( i = 0; i < CMAX; i++ ) + if ( (j = nint(f)) < 0 ) theJob->dcs[i] = 0; + else theJob->dcs[i] = theJob->symtable + j; + + if ( (i = nint(f)) < 0 ) theJob->symtab = 0; + else theJob->symtab = theJob->symtable + i; + + for ( i = 0; i < FMAX; i++ ) { + theJob->form[i].lsub = nint(f); + theJob->form[i].rsub = nint(f); + if ( (j = nint(f)) < 0 ) theJob->form[i].sym = 0; + else theJob->form[i].sym = theJob->symtable + j; + } + fclose(f); + + F_N = + ((theJob->f_n && valid[theJob->logic][AxFN]) + || theJob->axiom[AxFN]); + afx = + ((valid[theJob->logic][RulPref] && valid[theJob->logic][RulSuff]) + || (theJob->axiom[RulPref] && theJob->axiom[RulSuff])); +} + + + +/* +* This dumps the current contents of theJob in a just-about-human- +* readable form to a file specified by the user. +* The stored job can be recovered by means of read_job. +* Outjob replaces the job display in xdialog mode. +*/ + +void outjob(FILE *f) +{ + int i, j, k; + + for ( i = 0; i < AXMAX; i++ ) { + j = (int) theJob->axiom[i]; + fprintf(f, " %d", j); + } + + fprintf(f,"\n"); + for ( i = 0; i < CMAX; i++ ) fprintf(f, " %d", theJob->adicity[i]); + + for ( i = 0; i < TMAX; i++ ) + for ( j = 0; j < RTMAX; j++ ) + fprintf(f, " %d", theJob->croot[i][j]); + + for ( i = 0; i < TMAX; i++ ) + for ( j = 0; j < RTMAX; j++ ) + fprintf(f, " %d", theJob->proot[i][j]); + + fprintf(f,"\n"); + for ( i = 0; i < CMAX; i++ ) fprintf(f, " %d", theJob->defcon[i]); + + for ( i = 0; i < CMAX; i++ ) fprintf(f, " %d", theJob->concut[i]? 1: 0); + + fprintf(f, " %d", theJob->failure); + fprintf(f, " %d", theJob->logic); + + fprintf(f, " %d %d %d %d %d %d %d", + theJob->f_n, theJob->f_lat, theJob->f_t, + theJob->f_T, theJob->f_F, theJob->f_fus, theJob->f_nec); + + fprintf(f, " %d %d %d", + theJob->maxtime, theJob->maxmat, theJob->sizmax); + fprintf(f, " %d %d %d", + theJob->sizmax_ismax, theJob->totord, theJob->distrib); + fprintf(f, " %d %d", + theJob->tty_out, theJob->fil_out); + + fprintf(f,"\n"); + for ( i = 0; theJob->data_dir[i]; i++ ) { + j = theJob->data_dir[i]; + fprintf(f, " %d", j); + } + fprintf(f, " 0 "); + + for ( i = 0; theJob->outfil_name[i]; i++ ) { + j = theJob->outfil_name[i]; + fprintf(f, " %d", j); + } + fprintf(f, " 0 "); + + for ( i = 0; logic_name[theJob->logic][i]; i++ ) { + j = logic_name[theJob->logic][i]; + fprintf(f, " %d", j); + } + fprintf(f, " 0 "); + + for ( i = 0; i < SYMBOLMAX; i++ ) { + for ( j = 0; j < 16; j++ ) { + k = theJob->symtable[i].s[j]; + fprintf(f, " %d", k); + } + if ( theJob->symtable[i].last ) + k = theJob->symtable[i].last - theJob->symtable; + else k = -1; + fprintf(f, " %d", k); + if ( theJob->symtable[i].next ) + k = theJob->symtable[i].next - theJob->symtable; + else k = -1; + fprintf(f, " %d", k); + } + + for ( i = 0; i < 3; i++ ) + for ( j = 0; j < CMAX+8; j++ ) { + if ( theJob->symbol[i][j] ) + k = theJob->symbol[i][j] - theJob->symtable; + else k = -1; + fprintf(f, " %d", k); + } + + for ( i = 0; i < CMAX; i++ ) { + if ( theJob->dcs[i] ) + j = theJob->dcs[i] - theJob->symtable; + else j = -1; + fprintf(f, " %d", j); + } + if ( theJob->symtab ) + i = theJob->symtab - theJob->symtable; + else i = -1; + fprintf(f, " %d", i); + + fprintf(f,"\n"); + for ( i = 0; i < FMAX; i++ ) { + fprintf(f, " %d", theJob->form[i].lsub); + fprintf(f, " %d", theJob->form[i].rsub); + if ( theJob->form[i].sym ) + j = theJob->form[i].sym - theJob->symtable; + else j = -1; + fprintf(f, " %d", j); + } + fprintf(f,"\n"); +} + + + + +/* +* Trim converts answer to upper case and removes +* any non-alphanumeric characters. +*/ + +void trim() +{ + int i, j = 0; + + for ( i = 0; answer[i]; i++ ) { + if ( islower(answer[i]) ) + answer[i] = toupper(answer[i]); + if ( isupper(answer[i]) || isdigit(answer[i]) ) + answer[j++] = answer[i]; + } + while ( j < i ) + answer[j++] = '\0'; +} + + +/* + * By contrast, nospace just removes any white space characters, + * including the line feed if any. The parameter is assumed to be + * a null-terminated character string. The method is maximally + * crude, but the strings are very short so it's OK. + */ + +void nospace(char *s) +{ + int i,j; + + for ( i = 0; s[i]; i++ ) if ( isspace(s[i]) ) + for ( j = i;; j++ ) + if ( !(s[j] = s[j+1]) ) break; +} + + + + +/* +* Delete_saxiom is just a subroutine of delete, taking +* care of case (a). +*/ + +void delete_saxiom() +{ + int i, j = 0; + + for ( i = 1; i < AXMAX; i++ ) + if ( theJob->axiom[i] ) { + if ( j ) j = AXMAX; + else j = i; + } + if ( !j ) return; + if ( j < AXMAX ) theJob->axiom[j] = 0; + else { + if ( !xdialog ) { + for ( i = 1; i < AXMAX; i++ ) + if ( theJob->axiom[i] ) { + printf("\n %2d ", i); + print_axiom(stdout,i); + } + printf("\n\n Delete which one? (if none type \"0\") "); + fflush(stdout); + } + scanf("%d", &i); READLN; + if ( i > 0 && i < AXMAX ) theJob->axiom[i] = 0; + } +} + + + + +/* +* Delete_uaxiom is just a subroutine of delete, taking +* care of case (d). +*/ + +void delete_uaxiom() +{ + int i=0, j=0, k=0; + + if ( !**(theJob->croot) ) return; + if ( theJob->croot[1][0] && !xdialog ) { + for ( i = 0; theJob->croot[i][0]; i++ ) { + printf("\n %d: ", i+1); + for ( j = 0; theJob->proot[i][j]; j++ ) { + if ( j ) printf(", "); + if ( theJob->proot[i][j] != TRIVIAL ) + outfml(theJob->proot[i][j], + theJob->proot[i][j],stdout); + } + if ( theJob->proot[i][0] != TRIVIAL + || theJob->croot[i][1] ) + printf(" / "); + for ( j = 0; theJob->croot[i][j]; j++ ) { + if ( j ) printf(","); + if ( theJob->croot[i][j] != ABSURD ) + outfml(theJob->croot[i][j], + theJob->croot[i][j],stdout); + } + } + printf("\n\n Delete which one? (if none type \"0\") "); + fflush(stdout); scanf("%d", &j); READLN; + if ( j < 1 || j > i ) return; + j--; + } + else if ( xdialog ) scanf("%d", &j); + else { + i = 1; + j = 0; + } + while ( ++j <= i ) + for ( k = 0; k < RTMAX; k++ ) { + theJob->proot[j-1][k] = theJob->proot[j][k]; + theJob->croot[j-1][k] = theJob->croot[j][k]; + } +} + + + +/* +* Delete_connective is just a subroutine of delete, taking +* care of case (c). +*/ + +void delete_connective() +{ + int i, j; + int badplace = 0; + int badadic; + char badstr[SLEN]; + symb badcon; + + if ( !theJob->dcs[0] ) return; + if ( xdialog ) { + fgets(badstr,SLEN,stdin); + nospace(badstr); + } + else if ( !theJob->dcs[1] ) strcpy(badstr,theJob->dcs[0]->s); + else { + printf("\n\n Delete which connective? "); + fflush(stdout); + for (;;) { + fgets(badstr,SLEN,stdin); + nospace(badstr); + if ( !*badstr ) return; + for ( badplace = 0; theJob->dcs[badplace]; badplace++ ) + if ( !strcmp(badstr,theJob->dcs[badplace]->s) ) break; + if ( theJob->dcs[badplace] ) break; + } + } + badadic = theJob->adicity[badplace]; + badcon = theJob->dcs[badplace]; + + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] != PRIMITIVE && + is_in( badcon, theJob->defcon[i] )) + return(nodelcon("connective",theJob->dcs[i], + theJob->defcon[i])); + for ( i = 0; theJob->croot[i][0]; i++ ) { + for ( j = 0; theJob->croot[i][j]; j++ ) + if ( is_in( badcon, theJob->croot[i][j] )) + return(nodelcon("an axiom",'\0', + theJob->croot[i][j])); + for ( j = 0; theJob->proot[i][j]; j++ ) + if ( is_in( badcon, theJob->proot[i][j] )) + return(nodelcon("a rule premise",'\0', + theJob->proot[i][j])); + } + if ( theJob->failure && + is_in( badcon, theJob->failure )) + return(nodelcon("the badguy",'\0',theJob->failure)); + + for ( i = badplace; theJob->dcs[i]; i++ ) { + theJob->adicity[i] = theJob->adicity[i+1]; + theJob->dcs[i] = theJob->dcs[i+1]; + theJob->defcon[i] = theJob->defcon[i+1]; + theJob->concut[i] = theJob->concut[i+1]; + } + + for ( i = 0; theJob->symbol[badadic][i] != badcon; i++ ) ; + for ( j = i; theJob->symbol[badadic][j]; j++ ) + theJob->symbol[badadic][j] = theJob->symbol[badadic][j+1]; + purge( badcon ); + *(badcon->s) = 0; + if ( badcon->last ) badcon->last->next = badcon->next; + if ( badcon->next ) badcon->next->last = badcon->last; + if ( badcon == theJob->symtab ) theJob->symtab = badcon->next; + badcon->last = badcon->next = 0; +} + + + +/* +* If we bump out of delete_connective, it is for a reason, +* communicated to the user thus: +*/ + +void nodelcon(char *s, symb spec, int formula) +{ + EP printf("\n\n Sorry, it's needed to specify %s", s); + if ( spec ) printf(" %s", spec->s); + printf(": "); + outfml(formula,formula,stdout); + puts(""); + paws(); + return; +} diff --git a/src/hmi.c b/src/hmi.c new file mode 100644 index 0000000..d7f56e7 --- /dev/null +++ b/src/hmi.c @@ -0,0 +1,329 @@ +#include "RM.h" +#include "hmi.h" + + +/* + * Is there a homomorphic image of m1 in m2? + */ + +int homo(MATRIX *m1, MATRIX *m2) +{ + int h[SZ]; + int x,y; + + /* + * First check that the fragments agree + */ + for (x=NEG; xfragment[x] != m2->fragment[x]) + return 0; + FORALLCON(m1,x); + FORALLCON(m2,y); + if (x != y ) + return 0; + + /* + * Now initialise to "undefined" and ask for a solution + */ + for (x=0; xfragment[x] != m2->fragment[x]) + return 0; + FORALLCON(m1,x); + FORALLCON(m2,y); + if (x != y ) + return 0; + + /* + * Now initialise to "undefined" and ask for a solution + */ + for (x=0; xfragment[x] != m2->fragment[x]) + return 0; + FORALLCON(m1,x); + FORALLCON(m2,y); + if (x != y ) + return 0; + if (m1->siz > m2->siz) + return 0; + + /* + * Now initialise to "undefined" and ask for a solution + */ + for (x=0; xfragment[x] != m2->fragment[x]) + return 0; + FORALLCON(m1,x); + FORALLCON(m2,y); + if (x != y ) + return 0; + if (m1->siz < m2->siz) + return 0; + + /* + * Now initialise to "undefined" and ask for a solution + */ + for (x=0; xfragment[NEG] && + !monprop(m1,m1->neg,m2->neg,localh)) + return 0; + if (m1->fragment[BOX] && + !monprop(m1,m1->box,m2->box,localh)) + return 0; + FORALLCON(m1,x) + if (m1->adicity[x] == 1 && + !monprop(m1,m1->monadic[x],m2->monadic[x],localh)) + return 0; + if (!dyprop(m1,m1->C,m2->C,localh)) + return 0; + if (m1->fragment[FUS] && + !dyprop(m1,m1->fus,m2->fus,localh)) + return 0; + if (m1->fragment[LAT]) { + if (!dyprop(m1,m1->K,m2->K,localh)) + return 0; + if (!dyprop(m1,m1->A,m2->A,localh)) + return 0; + } + FORALLCON(m1,x) + if (m1->adicity[x] == 2 && + !dyprop(m1,m1->dyadic[x],m2->dyadic[x],localh)) + return 0; + FORALL(m1,x) + h[x] = localh[x]; + return 1; +} + + +int monprop(MATRIX *m1, int a[], int b[], int lh[]) +{ + int x; + + FORALL(m1,x) + if (lh[x] >= 0 && lh[a[x]] != b[lh[x]]) { + if (lh[a[x]] < 0) + lh[a[x]] = b[lh[x]]; + else + return 0; + } + return 1; +} + + +int dyprop(MATRIX *m1, int a[][SZ], int b[][SZ], int lh[]) +{ + int x,y; + + FORALL(m1,x) + if (lh[x] >= 0) + FORALL(m1,y) + if (lh[y] >= 0 && lh[a[x][y]] != b[lh[x]][lh[y]]) { + if (lh[a[x][x]] < 0) + lh[a[x][y]] = b[lh[x]][lh[y]]; + else return 0; + } + return 1; +} diff --git a/src/hmi.h b/src/hmi.h new file mode 100644 index 0000000..049f620 --- /dev/null +++ b/src/hmi.h @@ -0,0 +1,17 @@ +/* + * Function prototypes for homomorphism testers + */ + +int homo(MATRIX *m1, MATRIX *m2); +int homomorphism(MATRIX *m1, MATRIX *m2, int h[]); +int nt_homo(MATRIX *m1, MATRIX *m2); +int nt_homomorphism(MATRIX *m1, MATRIX *m2, int h[]); +int embedding(MATRIX *m1, MATRIX *m2); +int injected_into(MATRIX *m1, MATRIX *m2, int h[]); +int epimorphic_image(MATRIX *m1, MATRIX *m2); +int mapped_onto(MATRIX *m1, MATRIX *m2, int h[]); +void copy_h(MATRIX *m, int source[], int dest[]); +int propagated(MATRIX *m1, MATRIX *m2, int h[]); +int monprop(MATRIX *m1, int a[], int b[], int lh[]); +int dyprop(MATRIX *m1, int a[][SZ], int b[][SZ], int lh[]); + diff --git a/src/homomorphic.c b/src/homomorphic.c new file mode 100644 index 0000000..afacca4 --- /dev/null +++ b/src/homomorphic.c @@ -0,0 +1,11 @@ +#include "RM.h" +#include "hmi.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) + Abort("synopsis: homomorphic ",-1); + relatemats(homo, argv[1], "Homomorphism"); + + return 0; +} diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..a89e989 --- /dev/null +++ b/src/image.c @@ -0,0 +1,9 @@ +#include "RM.h" +#include "hmi.h" + +main(int argc, char *argv[]) +{ + if (argc != 2) + Abort("synopsis: embedding ",-1); + relatemats(epimorphic_image, argv[1], "MapsOnto"); +} diff --git a/src/isom.c b/src/isom.c new file mode 100644 index 0000000..349898e --- /dev/null +++ b/src/isom.c @@ -0,0 +1,528 @@ +/* +* isom.c V2.1 (May 1993) +* +* This contains all the procedures pertaining to the +* elimination of isomorphic copies from the output of +* matrix-generating programs such as MaGIC.c. +* +* It is assumed that the external variables siz, neg[], +* ord[][], des, C[][] and box[] are available and that +* this module is called, presumably by the accept() +* function, every time a new matrix is found. If there +* are any isomorphic copies, the integer isoms is +* incremented. +* +* There are two parts to all this. First come some +* procedures to find the acceptable permutations on the +* current siz, neg[], ord[][] and des. Then there are +* the routines to be executed on discovery of a new +* matrix. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + +#define firstopen (tr_par.vlength - tr_par.vlength) + +static boolean + manyisoms; /* Not all isoms have been stacked */ + +static ism + nextisom; /* First free isom in the list */ + +static int + lastisovect; /* Last unfree segment of isolist */ + +static int + isoused; /* Number of isoms used this case */ + +static char + shvector[V_LENGTH]; /* The variable part of thisvector */ + + + +/* +* This initialisation function just sets up perm. +*/ + +void perm_initial() +{ + perm = (PRM*) malloc(sizeof(PRM)); + perm->h[0] = -1; + perm->pup = 0; + isoused = ISOMAX; +} + + + + +/* +* Setperm finds the automorphisms which do not disturb +* ord, N or des. The bulk of it is a crude test-and-change. +*/ + +void setperm() +{ + int i, j; + int p_poss[SZ][SZ], + p_used[SZ], + sh[SZ], + single[SZ]; + PRM *prmptr; + + for (i=0; ih[0] >= 0; prmptr = prmptr->pup ) + prmptr->h[0] = -1; + + FORALL(i) FORALL(j) { + p_poss[i][j] = (lower_than(i)==lower_than(j)) && + (higher_than(i)==higher_than(j)); + if ( theJob->f_n && + (( N[i]==i && N[j]!=j ) || ( N[j]==j && N[i]!=i ))) + p_poss[i][j] = 0; + } + + FORALL(i) { + if ( theJob->f_t ) { + if ( i != des ) + p_poss[des][i] = p_poss[i][des] = 0; + if ( theJob->f_n && i != N[des] ) + p_poss[N[des]][i] = p_poss[i][N[des]] = 0; + } + else if ( desig[i] ) + FORALL(j) if ( !desig[j] ) + p_poss[i][j] = p_poss[j][i] = 0; + p_used[i] = 1; sh[i] = i; + single[i] = 1; + FORALL(j) + if ( i != j && p_poss[i][j] ) + single[i] = 0; + } + goto CHANGE; /* !!! */ + +PTEST: FORALL(i) FORALL(j) + if ( ord[i][j] != ord[sh[i]][sh[j]] ) + goto CHANGE; + newperm(sh); + +CHANGE: FORALL(i) + if ( !(( theJob->f_n && i < N[i] ) || single[i] )) { + p_used[sh[i]] = 0; + if ( theJob->f_n ) + p_used[sh[N[i]]] = 0; + for ( j = sh[i]-1; j >= 0; j-- ) + if ( p_poss[i][j] && !p_used[j] ) { + sh[i] = j; + if ( theJob->f_n ) + sh[N[i]] = N[j]; + p_used[j] = 1; + if ( theJob->f_n ) + p_used[N[j]] = 1; + for (i--; i >= 0 && !(theJob->f_n && i < N[i]); i--) + if ( !single[i] ) { + for (j = siz; j; j-- ) + if ( p_poss[i][j] && !p_used[j] ) { + sh[i] = j; + if ( theJob->f_n ) + sh[N[i]] = N[j]; + p_used[j] = 1; + if ( theJob->f_n) + p_used[N[j]] = 1; + break; + } + } + goto PTEST; + } + } +} + + + + +/* +* How many elements are below x? +*/ + +int lower_than(int x) +{ + int i; + int j = 0; + + for ( i=0; i < x; i++ ) j += ord[i][x]; + return j; +} + + +/* +* Similarly, how many are above? +*/ + +int higher_than(int x) +{ + int i; + int j = 0; + + for ( i = siz; i > x; i-- ) j += ord[x][i]; + return j; +} + + + + + +/* +* Add a permutation to the perm list. +*/ + +void newperm(int *vec) +{ + int i; + PRM *p; + + for ( p = perm; p->pup && p->h[0] >= 0; p = p->pup) ; + if ( !p->pup ) { + p->pup = (PRM*) malloc(sizeof(PRM)); + p->pup->pup = 0; + p->pup->h[0] = -1; + } + FORALL(i) p->h[i] = *(vec+i); +} + + + + + +/* +* That completes the first half, concerned with generating +* the list of allowable permutations. Now, assuming that +* list given, deal with isomorphisms between good matrices. +* +* Isomorphic usually returns 1 if the matrix was already in the +* isomorphism tree below ptr, snipping it out as it does so +* (since it is not going to be generated twice). If not, +* all the isomorphic copies which lie within the search space +* are generated and added to the tree. +* +* If the flag "manyisoms" is set, indicating that some isomorphic +* copies of earlier things have not been recorded because of lack +* of space, then the copies of the current matrix, instead of +* being added directly to the tree, are compared with the current +* matrix to see whether one of them preceeds it. If the current +* matrix is the first in its isomorphism class, its copies are +* inserted in the tree. If not, the temporary store is emptied +* and 1 returned. +* +* The flag "manyisoms" is raised if the number of stored copies +* gets to equal ISOMAX, and lowered at the start of each search. +*/ + +boolean isomorphic(ism ptr, trs T) +{ + int i, j; + boolean do_add = false; + + if ( !tr_par.vlength ) return false; + j = 0; + for ( i = 0; i < tr_par.vlength; i++ ) + if ( T->coinorder[i] >= firstopen ) + shvector[j++] = thisvector[i]; + + for ( i = 0; i < tr_par.vlength; i++ ) { + if ( shvector[i] < ptr->icv[i] ) { + if ( ptr->left ) + return isomorphic(ptr->left,T); + do_add = true; + break; + } + if ( shvector[i] > ptr->icv[i] ) { + if ( ptr->right ) + return isomorphic(ptr->right,T); + do_add = true; + break; + } + } + if ( do_add ) { + if ( isomorphic_anyhow(T) ) { + isoms2++; + return true; + } + add_isoms( T ); + return false; + } + snip(ptr); + isoms++; + return true; +} + + + + + +/* +* Snip removes a node from the isom tree. +* The action is slightly different if the node is the root. +*/ + +void snip(ism p) +{ + ism q; + int i; + + if ( p == istak ) { + if ( !p->left && !p->right ) { + *(p->icv) = SZ; + } + else { + if ( p->right ) + for ( q = p->right; q->left; q = q->left ) ; + else for ( q = p->left; q->right; q = q->right ) ; + for ( i = 0; i < tr_par.vlength; i++ ) + p->icv[i] = q->icv[i]; + snip(q); + } + } + else { + if ( !p->left ) + subst(p,p->right); + else if ( !p->right ) + subst(p,p->left); + else { + for ( q = p->right; q->left; q = q->left ) ; + for ( i = 0; i < tr_par.vlength; i++ ) + p->icv[i] = q->icv[i]; + subst(q,q->right); + } + } +} + + + + + +/* +* A subroutine of the above, this moves p2 into the place +* of p1, marking p1 as defunct so that it can be re-used. +*/ + +void subst(ism p1, ism p2) +{ + if ( p2 ) + p2->parent = p1->parent; + if ( p1->parent->left == p1 ) + p1->parent->left = p2; + else p1->parent->right = p2; + *(p1->icv) = SZ; + p1->parent = nextisom; + nextisom = p1; +} + + + + +/* +* Isomorphic_anyhow if manyisoms and this is not the first +* in its isomorphism class. This function and add_isoms are +* very similar and should probably be amalgamated at some stage. +*/ + +boolean isomorphic_anyhow(trs T) +{ + int i, j, k; + char ac[V_LENGTH]; + PRM *aptr; + + if ( !manyisoms ) return false; + for ( aptr = perm; aptr->h[0] >= 0; aptr = aptr->pup ) { + FORALL(i) FORALL(j) + ac[impindex[(int)aptr->h[i]][(int)aptr->h[j]]] = aptr->h[C[i][j]]; + FORALL(i) + ac[boxindex[(int)aptr->h[i]]] = aptr->h[box[i]]; + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + switch ( theJob->adicity[i] ) { + case 0: + ac[ucc0[i]] = aptr->h[nulladic[i]]; + break; + case 1: + FORALL(j) + ac[ucc1[i][(int)aptr->h[j]]] = + aptr->h[monadic[i][j]]; + break; + case 2: + FORALL(j) FORALL(k) + ac[ucc2[i][(int)aptr->h[j]][(int)aptr->h[k]]] = + aptr->h[dyadic[i][j][k]]; + } + for ( i = firstopen; i < tr_par.vlength; i++ ) + if ( thisvector[T->inorder[i]] != ac[T->inorder[i]] ) { + if ( thisvector[T->inorder[i]] > ac[T->inorder[i]] ) + return true; + break; + } + } + return false; +} + + + + + + +/* +* Add_isoms, called from isomorphic, generates all the non- +* trivial automorphic copies of the matrix got by applying +* allowable permutations and puts them into the "isom" tree. +*/ + +void add_isoms(trs T) +{ + int i, j, k, same; + char ac[V_LENGTH], shac[V_LENGTH]; + PRM *aptr; + + for ( aptr = perm; + aptr->h[0] >= 0 && nextisom && + lastisovect < ISLMAX - tr_par.vlength; + aptr = aptr->pup ) { + FORALL(i) FORALL(j) + ac[impindex[(int)aptr->h[i]][(int)aptr->h[j]]] = aptr->h[C[i][j]]; + if ( theJob->f_nec ) + FORALL(i) + ac[boxindex[(int)aptr->h[i]]] = aptr->h[box[i]]; + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + switch ( theJob->adicity[i] ) { + case 0: ac[ucc0[i]] = aptr->h[nulladic[i]]; + break; + case 1: + FORALL(j) + ac[ucc1[i][(int)aptr->h[j]]] = + aptr->h[monadic[i][j]]; + break; + case 2: + FORALL(j) FORALL(k) + ac[ucc2[i][(int)aptr->h[j]][(int)aptr->h[k]]] = + aptr->h[dyadic[i][j][k]]; + } + same = true; + j = 0; + for ( i = 0; i < tr_par.vlength; i++ ) + if ( T->coinorder[i] >= firstopen ) { + shac[j] = ac[i]; + if ( shac[j] != shvector[j] ) same = false; + j++; + } + if ( !same ) { + if ( *((*(istak)).icv) != SZ ) { + add_this(shac,istak); + if ( !nextisom || lastisovect >= ISLMAX - tr_par.vlength ) + manyisoms = true; + } + else + for ( i = 0; i < tr_par.vlength; i++ ) + (*(istak)).icv[i] = shac[i]; + } + } +} + + + + + +/* +* If the matrix mat is not in the i_tree, put it in and return true. +* If it is there already, return false. +*/ + +boolean add_this(char mat[], ism i_tree) +{ + int i; + + for ( i = 0; i < tr_par.vlength; i++ ) + if ( mat[i] < i_tree->icv[i] ) { + if ( i_tree->left ) + return add_this(mat,i_tree->left); + i_tree->left = tack_on(i_tree,mat); + return true; + } + else if ( mat[i] > i_tree->icv[i] ) { + if ( i_tree->right ) + return add_this(mat,i_tree->right); + i_tree->right = tack_on(i_tree,mat); + return true; + } + return false; +} + + + + + +/* +* Extend the isom tree with a new entry. Its parent is +* p and its matrix is mat. +* If there is no free ism in the stack, crash gracefully. +*/ + +ism tack_on(ism p, char mat[]) +{ + ism pi; + int i; + + pi = nextisom; + if ( !pi ) skipout("Isomorphism stack overflow",SKIP); + if ( pi >= istak+isoused ) isoused++; + nextisom = pi->parent; + pi->left = pi->right = 0; + pi->parent = p; + lastisovect += tr_par.vlength; + pi->icv = isolist+lastisovect; + for ( i = 0; i < tr_par.vlength; i++ ) + pi->icv[i] = mat[i]; + return pi; +} diff --git a/src/logic_io.c b/src/logic_io.c new file mode 100644 index 0000000..d477d65 --- /dev/null +++ b/src/logic_io.c @@ -0,0 +1,835 @@ +/* +* logic_io.c V2.1 (May 1993) +* +* This file contains the routines for reading in setups +* and for making the appropriate initialisations. It +* also contains the procedures for printing any matrices +* found. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1992 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + +/* +* First the semi-trivial function cluster to control the loop +*/ + +int newsiz() +{ + N[0] = 0; + return( got_siz()? (theJob->f_n? newneg(): neword()): 0 ); +} + +int newneg() +{ + return( got_neg()? neword(): newsiz() ); +} + +int neword() +{ + return( got_ord()? newdes(): (theJob->f_n? newneg(): newsiz()) ); +} + +int newdes() +{ + return( got_des()? 1: neword() ); +} + + + + +/* +* Sep is a subroutine to print the separator "-1" if the +* chosen format is UGLY. The parameter is the counter for +* the appropriate level. +*/ + +void sep(int *x) +{ + if ( *x ) { + if ( theJob->tty_out==UGLY ) printf(" -1\n"); + if ( theJob->fil_out==UGLY ) fprintf(outfil, " -1\n"); + *x = 0; + } +} + + + +/* +* Next_bit returns 1 or 0 depending on the next bit in the +* input file. This is to allow very compressed data to be used. +*/ + +int next_bit() +{ + if ( input_bit == 8 ) { + if ( read(infil,&buffa,1) < 1 ) return 0; + input_bit = 0; + } + if ( buffa & ((char)1 << input_bit++) ) return 1; + return 0; +} + + + + + +/* +* Return the new size, or 0 if there isn't one. +* The values of T and F are incidentally set at this stage. +*/ + +int got_siz() +{ + if ( theJob->f_n ) + sep( &negno ); + else sep( &ordno ); + if ( next_bit() ) siz++; else siz = 0; + if ( tr_par.done || siz>=theJob->sizmax ) siz = 0; + if ( theJob->f_T ) theJob->form[VMAX+2].val = siz; + if ( theJob->f_F ) theJob->form[VMAX+3].val = 0; + return siz; +} + + + + +/* +* Set the next negation table. +* Return 1 if successful, 0 if not. +*/ + +boolean got_neg() +{ + int i; + + sep(&ordno); + if ( tr_par.done || !next_bit() ) return false; + if ( N[0] ) { + FORaLL(i) + if ( N[i] > i ) break; + N[N[i]] = N[i]; + N[i] = i; + } + else FORALL(i) N[i] = siz-i; + return true; +} + + + + + +/* +* Read the next order table. +* Return 1 if successful, 0 if not. +* At this stage, decide which elements are maximal, define +* lattice meet and join if they exist, and set up cc, the +* index to the communication vector of transref. +*/ + +boolean got_ord() +{ + int i, j, k; + + sep( &desno ); + if ( tr_par.done || !next_bit() ) return false; + + FORALL(i) FORALL(j) + if ( i < j ) ord[i][j] = next_bit(); + else ord[i][j] = (i == j); + + FORALL(i) { + maximal[i] = 1; + for ( j = i+1; j <=siz; j++ ) maximal[i] *= !ord[i][j]; + } + if ( theJob->f_lat ) { + FORALL(i) { + for ( j = i; j <= siz; j++ ) { + for (k = j; !(ord[i][k] && ord[j][k]); k++) ; + A[i][j] = A[j][i] = k; + for (k = i; !(ord[k][i] && ord[k][j]); k--) ; + K[i][j] = K[j][i] = k; + } + } + } + + set_up_cc(); + return true; +} + + + + +/* +* Read the next choice of designated values, or of least +* designated value if t is defined. +* Return 1 on success, 0 on failure. +* +* The set of designated (true) values can be calculated +* here, and the values of t and f filled in. +*/ + +boolean got_des() +{ + int i; + + if ( matsum ) { + if ( theJob->tty_out == SUMMARY ) { + printf(" %d matri%s", + matsum, (matsum>1? "ces": "x")); + fflush(stdout); + } + if ( theJob->fil_out == SUMMARY ) + fprintf(outfil, " %d matri%s", + matsum, (matsum>1? "ces": "x")); + matsum = 0; + } + sep( &matno ); + + if ( tr_par.done || !next_bit() ) return false; + FORALL(i) desig[i] = next_bit(); + + for ( des = 0; !desig[des]; des++ ) ; + for ( undes = siz; desig[undes]; undes-- ) ; + if ( theJob->f_t ) { + theJob->form[VMAX].val = des; + if ( theJob->f_n ) theJob->form[VMAX+1].val = N[des]; + } + return true; +} + + + + +/* +====================================================================== +*/ + + +/* +* That's the end of the input routines. Now for the output ones. +* +* Mat_print is called from the tester with each good matrix. +*/ + +void mat_print() +{ + int i, j; + boolean b; + + firstchange = -1; + for ( i = 0; i < tr_par.vlength; i++ ) { + if ( thisvector[i] != thatvector[i] ) firstchange = i; + thatvector[i] = thisvector[i]; + } + if ( !matno ) firstchange = tr_par.vlength; + if ( firstchange < 0 ) skipout("Same matrix found twice",SKIP); + + j = -1; + for ( i = 0; theJob->dcs[i]; i++ ); + for (--i; i >= 0; i--) + if ( theJob->defcon[i] == PRIMITIVE ) { + switch( theJob->adicity[i] ) { + case 0: + newmatplus( j, ucc0[i] ); + break; + case 1: + newmatplus( j, ucc1[i][0] ); + break; + case 2: + newmatplus( j, ucc2[i][0][0] ); + } + j = i; + } + if ( theJob->f_nec ) { + newmatplus( j, firstbox ); + if ( firstchange >= firstarrow ) sep(&boxno); + } + else newmatplus( j, firstarrow ); + + b = true; + for ( i = 0; theJob->dcs[i]; i++ ); + for ( --i; i>=0; i-- ) + if ( theJob->defcon[i] == PRIMITIVE ) + if ( matplus[i]++ ) { + b = false; + break; + } + if ( b && theJob->f_nec ) + if ( boxno++ ) b = false; + if ( b ) + if ( !matno++ ) + if ( !desno++ ) + if ( !ordno++ ) + if ( theJob->f_n ) + negno++; + + good++; + matsum++; + if ( theJob->tty_out ) printup(stdout,theJob->tty_out); + if ( theJob->fil_out ) printup(outfil,theJob->fil_out); +} + + + + +/* +* Essentially a macro for the above: +*/ + +void newmatplus(int x, int y) +{ + if ( firstchange >= y && x >= 0 ) sep(matplus+x); +} + + + + + +/* +* Printing the new size is part of printing the new negation +* table or, if negation is not defined, the new order table. +* The parameter x is the output mode, and f the destination. +*/ + +void siz_print(FILE *f, output_style x) +{ + switch(x) { + case UGLY: + fprintf(f, " %d\n", siz); + break; + case PRETTY: + fprintf(f, "\n\n\n\n Size: %d\n", siz+1); + break; + case SUMMARY: + fprintf(f, "\n\n\n Size: %d", siz+1); + case NONE: + break; + } +} + + + + +/* +* Printing the new negation table is part of printing the new +* order table. +* The parameter x is the output mode, and f the destination. +*/ + +void neg_print(FILE *f, output_style x) +{ + int i; + + if ( negno == 1 ) siz_print(f,x); + switch(x) { + case UGLY: + FORALL(i) fprintf(f, " %d", N[i]); + fprintf(f, "\n"); + break; + case PRETTY: + fprintf(f, "\n\n Negation table "); + pretty_negno(f); + fprintf(f, "\n\n a |"); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n ---+"); + FORALL(i) fprintf(f, "--"); + fprintf(f, "\n ~a |"); + FORALL(i) fprintf(f, " %x", N[i]); + fprintf(f, "\n"); + break; + case SUMMARY: + fprintf(f, "\n\n Negation "); + pretty_negno(f); + case NONE: + break; + } +} + + + + +/* +* Printing the new order table is part of printing the new +* choice of designated values. +* The parameter x is the output mode, and f the destination. +*/ + +void ord_print(FILE *f, output_style x) +{ + int i, j; + + if ( ordno == 1 ) { + if ( theJob->f_n ) neg_print(f,x); + else siz_print(f,x); + } + switch(x) { + case NONE: + break; + case UGLY: + FORALL(i) FORALL(j) + fprintf(f, " %d", ord[i][j]); + fprintf(f, "\n"); + break; + case PRETTY: + fprintf(f, "\n\n Order "); + pretty_ordno(f); + fprintf(f, "\n\n < |"); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n --+"); + FORALL(i) fprintf(f, "--"); + FORALL(i) + { fprintf(f, "\n%10x |", i); + FORALL(j) + fprintf(f, " %c", (ord[i][j]? '+': '-')); + } + fprintf(f, "\n"); + break; + case SUMMARY: + fprintf(f, "\n\n Order "); + pretty_ordno(f); + } +} + + + +/* +* Printing the new choice of designated values is part of +* printing the new implication matrix. +* The parameter x is the output mode, and f the destination. +* +* If t is defined only it is printed. Otherwise the true +* values are listed. In ugly format, the boolean vector +* true (1 if value is designated, 0 if not) is printed. +* Note that this convention for ugly output is different +* from that of MaGIC version 1.1 and causes incompatibility +* with post-processing software. +*/ + +void des_print(FILE *f, output_style x) +{ + int i; + + if ( desno == 1 ) ord_print(f,x); + switch(x) { + case NONE: + break; + case UGLY: + FORALL(i) fprintf(f, " %d", desig[i]); + fprintf(f, "\n" ); + break; + case PRETTY: + if ( theJob->f_t ) { + fprintf(f, "\n\n Choice "); + pretty_desno(f); + fprintf(f, " of t: %x\n", des); + } + else { + fprintf(f, "\n\n Choice "); + pretty_desno(f); + fprintf(f, " of truths: "); + FORALL(des) if ( desig[des] ) + fprintf(f, " %x", des); + fprintf(f, "\n"); + } + break; + case SUMMARY: + if ( theJob->f_t ) + fprintf(f, "\n\n t = %x", des); + else { + fprintf(f, "\n\n truth ="); + FORALL(des) if ( desig[des] ) + fprintf(f, " %x", des); + } + } +} + + + + +/* +* Printing the new implication matrix is part of printing +* the new algebra. +* The parameter x is the output mode, and f the destination. +*/ + +void C_print(FILE *f, output_style x) +{ + int i, j; + + if ( matno == 1 ) des_print(f,x); + switch(x) { + case SUMMARY: + case NONE: + return; + case UGLY: + FORALL(i) FORALL(j) fprintf(f, " %d", C[i][j]); + break; + case PRETTY: + fprintf(f, "\n\n Implication matrix "); + pretty_matno(f); + + fprintf(f, "\n\n -> |"); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n ---+"); + FORALL(i) fprintf(f, "--"); + FORALL(i) { + fprintf(f, "\n%10x |", i); + FORALL(j) fprintf(f, " %x", C[i][j]); + } + fprintf(f, "\n"); + } +} + + + + +/* +* Printing a new ! table may be part of printup. +* The parameter x is the output mode, and f the destination. +*/ + +void box_print(FILE *f, output_style x) +{ + int i; + + switch(x) { + case UGLY: + FORALL(i) fprintf(f, " %d", box[i]); + fprintf(f, "\n"); + break; + case PRETTY: + fprintf(f, "\n ! matrix "); + pretty_boxno( f ); + fprintf(f, "\n\n a |"); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n ---+"); + FORALL(i) fprintf(f, "--"); + fprintf(f, "\n !a |"); + FORALL(i) fprintf(f, " %x", box[i]); + if ( theJob->f_n ) { + fprintf(f, "\n ?a |"); + FORALL(i) fprintf(f, " %x", diamond[i]); + } + fprintf(f, "\n"); + break; + case SUMMARY: + case NONE: + return; + } +} + + + + +/* +* Printing a nulladic user connective may be part of printup. +* The parameter x is the output mode, and f the destination. +*/ + +void u_print0(FILE *f, output_style x, int y) +{ + switch(x) { + case UGLY: + fprintf(f, " %d\n", nulladic[y]); + break; + case PRETTY: + fprintf(f, "\n Choice "); + pretty_umat( f, y ); + fprintf(f, " of %s: %x\n", + theJob->dcs[y]->s, nulladic[y]); + break; + case SUMMARY: + case NONE: + return; + } +} + + + + + +/* +* Printing a monadic user connective may be part of printup. +* The parameter x is the output mode, and f the destination. +*/ + +void u_print1(FILE *f, output_style x, int y) +{ + int i; + + switch(x) { + case UGLY: + FORALL(i) fprintf(f, " %d", monadic[y][i]); + fprintf(f, "\n"); + break; + case PRETTY: + fprintf(f, "\n %s matrix ", theJob->dcs[y]->s); + pretty_umat( f, y ); + fprintf(f, "\n\n "); + for ( i = 0; i < strlen(theJob->dcs[y]->s); i++ ) + printf(" "); + fprintf(f, "a |"); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n ----"); + for ( i = 0; i < strlen(theJob->dcs[y]->s); i++ ) + printf("-"); + fprintf(f, "+"); + FORALL(i) fprintf(f, "--"); + fprintf(f, "\n %s(a) |", theJob->dcs[y]->s); + FORALL(i) fprintf(f, " %x", monadic[y][i]); + fprintf(f, "\n"); + break; + case SUMMARY: + case NONE: + return; + } +} + + + +/* +* Printing a dyadic user connective may be part of printup. +* The parameter x is the output mode, and f the destination. +*/ + +void u_print2(FILE *f, output_style x, int y) +{ + int i, j, k; + + switch(x) { + case NONE: + case SUMMARY: + return; + case UGLY: + FORALL(i) FORALL(j) + fprintf(f, " %d", dyadic[y][i][j]); + break; + case PRETTY: + fprintf(f, "\n %s matrix ", theJob->dcs[y]->s); + pretty_umat( f, y ); + fprintf(f, "\n\n %s |", + theJob->dcs[y]->s); + FORALL(i) fprintf(f, " %x", i); + fprintf(f, "\n --"); + for ( i = 0; i < strlen(theJob->dcs[y]->s); i++ ) + printf("-"); + fprintf(f, "+"); + FORALL(i) fprintf(f, "--"); + FORALL(i) { + fprintf(f, "\n"); + for ( k = 0; k < strlen(theJob->dcs[y]->s); k++ ) + printf(" "); + fprintf(f, "%19x |", i); + FORALL(j) + fprintf(f, " %x", dyadic[y][i][j]); + } + fprintf(f, "\n"); + } +} + + + + + +/* +* This is the printup routine called with every good matrix. +* The parameter x is the output mode, and f the destination. +*/ + +void printup(FILE *f, output_style x) +{ + int i; + + if ( firstchange >= firstarrow ) C_print(f,x); + if ( theJob->f_nec && firstchange >= firstbox ) box_print(f,x); + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + switch( theJob->adicity[i] ) { + case 0: + if ( firstchange >= ucc0[i] ) + u_print0(f,x,i); + break; + case 1: + if (firstchange >= ucc1[i][0] ) + u_print1(f,x,i); + break; + case 2: + if (firstchange >= ucc2[i][0][0] ) + u_print2(f,x,i); + } + if ( x == PRETTY ) { + if ( theJob->failure ) fail_print(f); + fprintf(f, "\n"); + fflush(f); + } +} + + + + + +/* +* This is the series of functions for pretty-printing the +* matrix numbers. +*/ + +void pretty_size(FILE *f) +{ + fprintf( f, "%d", siz+1 ); +} + + +void pretty_negno(FILE *f) +{ + pretty_size(f); + fprintf( f, ".%d", negno ); +} + + +void pretty_ordno(FILE *f) +{ + if ( theJob->f_n ) + pretty_negno(f); + else pretty_size(f); + fprintf( f, ".%d", ordno ); +} + + +void pretty_desno(FILE *f) +{ + pretty_ordno(f); + fprintf( f, ".%d", desno ); +} + + + +void pretty_matno(FILE *f) +{ + pretty_desno(f); + fprintf( f, ".%d", matno ); +} + + + +void pretty_boxno(FILE *f) +{ + pretty_matno(f); + if ( theJob->f_nec ) fprintf( f, ".%d", boxno ); +} + + +/* + * DOES NOT LOOK RIGHT AT ALL + * +void pretty_umat(FILE *f, int x) +{ + int i; + + for ( i = x-1; i >= 0; i-- ) + if ( theJob->defcon[i] == PRIMITIVE ) { + pretty_umat( f, i ); + break; + } + if ( i < 0 ) pretty_matno(f); + fprintf( f, ".%d", matplus[x] ); +} + * +*/ + +void pretty_umat(FILE *f, int x) +{ + if ( x < 0 ) pretty_boxno(f); + else { + pretty_umat(f,x-1); + if ( theJob->defcon[x] == PRIMITIVE ) + fprintf( f, ".%d", matplus[x] ); + } +} + + + +/* +* The assignment of values failing the bad guy is appended to +* each structure recorded in pretty format. +*/ + +void fail_print(FILE *f) +{ + insert_badvalues( theJob->failure ); + fprintf(f,"\n Failure: "); + outformula( theJob->failure, theJob->failure, f, VALS ); + fprintf(f,"\n"); +} + + +void insert_badvalues( int offset ) +{ + if ( !offset ) return; + insert_badvalues( theJob->form[offset].lsub ); + insert_badvalues( theJob->form[offset].rsub ); + if ( offset <= Vmax ) theJob->form[offset].val = badvalue[offset]; +} + + + + + +/* +* At the end of a job some runtime statistics are sent to +* stdout. Note that this is not done in batch mode. +*/ + +void stats_print() +{ + int tim; + + CLoCK(&tim); + printf("\n\n\n\n\n Matrices generated by MaGIC: "); + fflush(stdout); + system("date"); + printf("\n\n Good ones found: %d\n", good); + printf(" Bad ones tested: %d\n", tot-(good+isoms+isoms2)); + printf(" Isomorphs omitted: %d", isoms); + if ( isoms2 ) printf(" + %d", isoms2); +#ifdef HASTIMES + end_timer = time_buffer.tms_utime; + printf("\n Time (cpu): %1.2f seconds", + (end_timer - begin_timer)/(1.0*CLK_TCK)); +#endif + printf("\n Time (wall clock): %1.2f seconds\n\n\n", + (tim - start_time)/(TICK*1.0)); +} diff --git a/src/logic_pretest.c b/src/logic_pretest.c new file mode 100644 index 0000000..554712d --- /dev/null +++ b/src/logic_pretest.c @@ -0,0 +1,184 @@ +/* +* logic_pretest.c V2.1 (May 1993) +* +* Most 2-refutations can be found before the search for matrices +* starts. The function find_twos does this. As in the cases of +* the test and setup routines, this process is very case-ridden. +* +* When a 2-refutation is found, it is reported to transref by +* means of the call new_two_ref. Transref takes care of all the +* processing, including making sure it is not a repeat of one +* already reported or subsumed by a 1-refutation. +* +* As for set_poss, the parameter info is the vector of sets of +* possible values. It must not be changed by anything in this +* file, as is is needed (and sometimes updated) by transref. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + +static unsigned possval[SZ][SZ]; + +static trs Tpt; + + + +void new_two_ref( int a, int x, int b, int y ) +{ + int cells[3], vals[3]; + boolean valency[3]; + + cells[0] = a; vals[0] = x; valency[0] = false; + cells[1] = b; vals[1] = y; valency[1] = false; + cells[2] = -1; vals[2] = 0; valency[2] = false; + new_small_ref(cells, vals, valency, Tpt); +} + + + +boolean find_twos(unsigned info[], trs Tr) +{ + int i, j; + + FORALL(i) FORALL(j) possval[i][j] = info[impindex[i][j]]; + Tpt = Tr; + + if ( theJob->f_fus ) pretest_fus(); + + for ( i = 1; i < AXMAX; i++ ) if ( taxiom[i] && TL[i].two_test ) + (*(TL[i].two_test))(); + return true; +} + + + +boolean find_twos_and_threes(unsigned info[], trs Tr) +{ + int i, j; + + FORALL(i) FORALL(j) possval[i][j] = info[impindex[i][j]]; + Tpt = Tr; + + if ( theJob->f_fus ) pretest_fus(); + + for ( i = 1; i < AXMAX; i++ ) if ( taxiom[i] && TL[i].two_test ) + (*(TL[i].two_test))(); + + for ( i = 1; i < AXMAX; i++ ) if ( taxiom[i] && TL[i].three_test ) + (*(TL[i].three_test))(); + + return true; +} + + + +void new_three_ref( int a, int x, int b, int y, int c, int z ) +{ + int cells[4], vals[4]; + boolean valency[4]; + + cells[0] = a; vals[0] = x; valency[0] = false; + cells[1] = b; vals[1] = y; valency[1] = false; + cells[2] = c; vals[2] = z; valency[2] = false; + cells[3] = -1; vals[3] = 0; valency[3] = false; + new_small_ref(cells, vals, valency, Tpt); +} + + + +/* +* Where m is maximal, a->m is maximal for all a. Also, if m1 and +* m2 are maximal and different then a->m1 and a->m2 are different. +*/ + + + +void pretest_fus() +{ + int m1, m2, a, x; + + FORALL(m1) if ( maximal[m1] ) + for ( m2 = m1+1; m2 <= siz; m2++ ) if ( maximal[m2] ) + FORALL(x) if ( maximal[x] ) + FORALL(a) + new_two_ref( impindex[a][m1], x, impindex[a][m2], x ); +} + + +/* +* For affixing, we need to look at possval, which is why this is here. +* Report the incompatible pairs of values for a->b, c->d. +*/ + +void affix_case(int a, int b, int c, int d) +{ + int i, j; + + FORALL(i) if ( IN(i,possval[a][b]) ) + FORALL(j) if ( IN(j,possval[c][d]) ) + if ( !ord[i][j] ) + new_two_ref( impindex[a][b], i, impindex[c][d], j ); +} + + + + +void test_assertion() +{ + int i, j; + int a, b; + + FORALL(a) FORALL(j) if ( !ord[a][j] ) + FORALL(i) if ( a != i || j == i ) + FORALL(b) if ( IN(i,possval[a][b]) && IN(j,possval[i][b]) ) + new_two_ref( impindex[a][b], i, impindex[i][b], j ); +} + + + + +void test_contraction() +{ + int i, j; + int a, b; + + FORALL(i) FORALL(j) if ( !ord[j][i] ) + FORALL(b) if ( i != b ) + FORALL(a) if ( IN(i,possval[a][b]) ) + if ( IN(j,possval[a][i]) ) + new_two_ref( impindex[a][b], i, impindex[a][i], j ); +} diff --git a/src/logic_set.c b/src/logic_set.c new file mode 100644 index 0000000..2d9b238 --- /dev/null +++ b/src/logic_set.c @@ -0,0 +1,530 @@ +/* +* logic_set.c V2.1 (May 1993) +* +* The procedures going with set_poss() called from the +* search. Also pre_set() called from job_initial(). +*/ + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + +static int vuloc[VMAX]; /* Local version of vu */ +static int phi; /* The changeable subformula */ +static int phicc; /* Vector cell of phi */ +static int phival; /* The value of formula phi */ + + + + +/* +* Pre_set checks that such axioms as Excluded Middle are +* satisfied, returning 0 (false) if not. These principles +* are the ones that can be checked without generating any +* implication matrix, or even setting up a search space, +* so the search is avoided if they fail. +*/ + +boolean pre_set() +{ + int i,j; + + FORALL(i) { + greater_than[i] = less_than[i] = 0; + FORALL(j) { + if ( ord[i][j] ) ADDTO(j,greater_than[i]); + if ( ord[j][i] ) ADDTO(j,less_than[i]); + } + Greater_than[i] = (greater_than[i] & ~(1 << i)); + Less_than[i] = (less_than[i] & ~(1 << i)); + } + + for ( i = 1; i < AXMAX; i++ ) + if ( theJob->axiom[i] && TL[i].zero_test ) + if (!(*(TL[i].zero_test))()) return false; + + if ( theJob->f_T ) + FORALL(i) + if ( !ord[i][siz] ) return false; + if ( theJob->f_F ) + FORALL(i) + if ( !ord[0][i] ) return false; + + for ( i = 0; theJob->croot[i][0]; i++ ) + if ( ! kost[i] ) + if ( ! utest(i,0) ) return false; + + return true; +} + + + + + +/* +* Utest returns true if the user-defined axiom or rule #x +* is valid (preserves designation for all assignments to the +* variables it involves). It is assumed that changeable +* connectives are not involved. +*/ + +boolean utest(int x, unsigned info[]) +{ + int i, j; + + if ( kost[x] > 1 ) return true; + + for ( i = 1; i <= Vmax; i++ ) { + theJob->form[i].val = 0; + vuloc[i] = 0; + } + phi = 0; + phival = 0; + FORALL(i) FORALL(j) C[i][j] = (ord[i][j]? des: undes); + for ( i = 0; theJob->croot[x][i]; i++ ) + set_vuloc( theJob->croot[x][i], theJob->croot[x][i] ); + for ( i = 0; theJob->proot[x][i]; i++ ) + set_vuloc( theJob->proot[x][i], theJob->proot[x][i] ); + do if ( badcase( x, info )) { + if ( kost[x] ) REMOVE(phival,info[phicc]); + else return false; + } + while ( anothercase(x) ); + return true; +} + + + + + +/* +* The following few functions implement the testing of zero +* and one refutations in a rather crude but acceptable way. +* First we record the variables used locally. +*/ + +void set_vuloc(int r, int rr) +{ + int i; + + if ( r < 0 ) return; + if ( r < VMAX ) vuloc[r] = 1; + else { + set_vuloc(theJob->form[r].lsub,rr); + set_vuloc(theJob->form[r].rsub,rr); + if ( r != rr && !strcmp(theJob->form[r].sym->s,"->") ) + phi = r; + else if ( r != rr && !strcmp(theJob->form[r].sym->s,"o") ) + phi = r; + else if ( !strcmp(theJob->form[r].sym->s,"!") ) + phi = r; + else for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->form[r].sym == theJob->dcs[i] ) { + if ( theJob->defcon[i] == PRIMITIVE ) + phi = r; + else set_vuloc(theJob->defcon[i],rr); + } + } +} + + +/* +* This tests the rule for one assignment of values to its +* constituent atoms. Note the returned value is 1 for a +* failure of the rule and 0 for success. +*/ + +boolean badcase(int x, unsigned info[]) +{ + int i; + + for ( i = 0; theJob->proot[x][i] > 0; i++ ) + if ( !desig[eval( theJob->proot[x][i], info )] ) return false; + for ( i = 0; theJob->croot[x][i] > 0; i++ ) + if ( desig[eval( theJob->croot[x][i], info )] ) return false; + return true; +} + + +/* +* Eval returns the current value of the subformula rooted +* at r. This is not a fast way of testing, but it will do. +* Note that C is set up so that true cases get designated +* values and false cases undesignated ones. +*/ + +int eval(int r, unsigned z[]) +{ + int dr, Ls, Rs; + symb c; + + if ( r < 1 ) skipout("Attempt to test rubbish",SKIP); + c = theJob->form[r].sym; + if ( is_var(c) ) return theJob->form[r].val; + + Ls = theJob->form[r].lsub; + Rs = theJob->form[r].rsub; + if ( !strcmp(c->s,"->") ) { + if ( r == phi ) { + phicc = impindex[eval(Ls,z)][eval(Rs,z)]; + while ( phival < siz && !IN(phival,z[phicc]) ) + phival++; + return phival; + } + return C[eval(Ls,z)][eval(Rs,z)]; + } + if ( !c->s[1] ) switch( c->s[0] ) { + case 'T': + return siz; + case 'F': + return 0; + case 't': + return des; + case 'f': + return N[des]; + case '&': + return K[eval(Ls,z)][eval(Rs,z)]; + case 'v': + return A[eval(Ls,z)][eval(Rs,z)]; + case 'o': + if ( r == phi ) { + phicc = impindex[eval(Ls,z)][N[eval(Rs,z)]]; + while ( phival < siz && !IN(phival,z[phicc]) ) + phival++; + return N[phival]; + } + return N[C[eval(Ls,z)][N[eval(Rs,z)]]]; + case '~': + return N[eval(Rs,z)]; + case '!': + if ( r == phi ) { + phicc = boxindex[eval(Rs,z)]; + while ( phival < siz && + !IN(phival,z[phicc]) ) + phival++; + return phival; + } + return box[eval(Rs,z)]; + } + for ( dr = 0; theJob->dcs[dr] != theJob->form[r].sym; dr++ ) ; + if ( r == phi ) { + switch( theJob->adicity[dr] ) { + case 0: + phicc = ucc0[dr]; + break; + case 1: + phicc = ucc1[dr][eval(Rs,z)]; + break; + case 2: + phicc = ucc2[dr][eval(Ls,z)][eval(Rs,z)]; + } + while ( phival < siz && !IN(phival,z[phicc]) ) + phival++; + return phival; + } + else { + if ( theJob->adicity[dr] == 1 ) + theJob->form[atom[0][dr]].val = eval(Rs,z); + else if ( theJob->adicity[dr] == 2 ) { + theJob->form[atom[0][dr]].val = eval(Ls,z); + theJob->form[atom[1][dr]].val = eval(Rs,z); + } + return eval( theJob->defcon[dr], z ); + } +} + + + +int anothercase(int x) +{ + int i; + + if ( kost[x] ) { + if ( phival == siz ) phival = 0; + else return ++phival; + } + for ( i = 1; i <= Vmax; i++ ) if ( vuloc[i] ) { + if ( theJob->form[i].val == siz ) theJob->form[i].val = 0; + else return ++(theJob->form[i].val); + } + return 0; +} + + + + + + +/* +* Set_poss is called from transref when a search space is +* being initialised. The vector info will contain (as bit +* vectors) supersets of the possible values for each cell. +* Set_poss may take values out but should on no account +* put values in. Since transref is allowed to split up +* the seasrch space and search bits of it separately, this +* function should not be based on any assumption about what +* values will be present as possibilities. +* +* The first values to be pruned away are those which are +* larger than siz, the maximum value. The arrow matrix C +* should also have only designated (true) values in cells +* a -> b where ord[a][b], and only undesignated values in +* cells a -> b where not ord[a][b]. +* +* Logic_poss trims off more values according to the +* postulates of the chosen logic and any pre-defined axioms +* which have been selected. +* +* In affixing logics (all except FD), affixing and the +* existence of fusion force more values out. +*/ + +boolean set_poss(unsigned info[], trs T) +{ + int i, j, k, pr; + unsigned mask; + + pr = 0; + for ( i = 0; theJob->dcs[i]; i++ ) ; + while ( i ) { + if ( theJob->defcon[--i] == PRIMITIVE ) { + switch ( theJob->adicity[i] ) { + case 0: co_priority( ucc0[i], pr, T ); + break; + case 1: + FORALL(j) + co_priority( ucc1[i][j], pr, T ); + break; + case 2: + FORALL(j) FORALL(k) + co_priority( ucc2[i][j][k], pr, T ); + } + pr++; + } + } + if ( theJob->f_nec ) + FORALL(i) co_priority( boxindex[i], pr, T ); + FORALL(i) FORALL(j) co_priority( impindex[i][j], pr+1, T ); + + mask = 0; + FORALL(i) ADDTO(i,mask); + for ( i = 0; i < tr_par.vlength; i++ ) info[i] &= mask; + + FORALL(i) FORALL(j) FORALL(k) + if IFF( (ord[i][j]),(!desig[k]) ) + REMOVE(k,info[impindex[i][j]]); + + if ( !logic_poss(info) ) return 0; + if ( theJob->f_fus ) fusion(info); + + for ( i = 0; theJob->croot[i][0]; i++ ) + if ( kost[i] == 1 ) utest( i, info ); + + return 1; +} + + + + + + +/* +* Fusion requires that if m is any maximal element then any +* a->m is also maximal. +*/ + +void fusion(unsigned info[]) +{ + int m, x, a; + + FORALL(m) if ( maximal[m] ) + FORALL(x) if ( !maximal[x] ) + FORALL(a) + REMOVE(x,info[impindex[a][m]]); +} + + + + + + +/* +* If permutation is selected, wherever ord[a][ b->c ] we must +* have ord[b][ a->c ]. Hence if ord[a][x] for ALL or NONE of +* the values for b->c, then we can remove from the values for +* a->c any y such that !ord[b][y] or ord[b][y] respectively. +*/ + +boolean permutable(int a, int b, int c, unsigned info[]) +{ + int k, m; + + k = impindex[a][c]; m = impindex[b][c]; + + if ( (greater_than[a] & info[m]) == info[m] ) + info[k] &= greater_than[b]; + if ( !(greater_than[a] & info[m]) ) + info[k] &= ~greater_than[b]; + if ( (greater_than[b] & info[k]) == info[k] ) + info[m] &= greater_than[a]; + if ( !(greater_than[b] & info[k]) ) + info[m] &= ~greater_than[a]; + + return( info[k] != 0 && info[m] != 0 ); +} + + + + + + + + + +/* +* This function calls the above cases as required, according +* to the axioms selected. Some additional slightly messy +* features of C-type logics are additionally implemented. +*/ + +boolean logic_poss(unsigned info[]) +{ + int i, j, k; + + if ( theJob->logic==R ) + TaT(info); + if ( (theJob->logic==RW || theJob->logic==LIN) + && theJob->f_n && theJob->f_lat ) + RWX(info); + + for ( i = 1; i < AXMAX; i++) + if ( theJob->axiom[i] && TL[i].one_test ) + (*(TL[i].one_test))(info); + + if ( afx && ( theJob->axiom[AxC] || theJob->axiom[AxC2] )) + FORALL(i) FORALL(j) if ( i < j ) + FORALL(k) + if ( !permutable( i, j, k, info ) ) + return false; + return (*info != 0); +} + + + + + +/* +* The axioms valid in the chosen logic are set or unset +* here according to whether the parameter x is 1 or 0. +*/ + +#define TUNSET(axm) taxiom[axm] = 0 + +void logic_axioms(boolean x) +{ + AXIOM ax; + + if ( theJob->f_n && theJob->logic != FD ) + theJob->axiom[AxFN] = x; + + for ( ax = AxNull; ax < AXMAX; ax++ ) + if ( valid[theJob->logic][ax] ) + theJob->axiom[ax] = x; + + for ( ax = AxNull; ax < AXMAX; ax++ ) { + if ( strchr(ax_string[ax],'~') && !theJob->f_n ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'&') && !theJob->f_lat ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'v') && !theJob->f_lat ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'o') && !theJob->f_fus ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'!') && !theJob->f_nec ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'t') && !theJob->f_t ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'f') && !(theJob->f_t && theJob->f_n) ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'T') && !theJob->f_T ) + theJob->axiom[ax] = 0; + if ( strchr(ax_string[ax],'F') && !theJob->f_F ) + theJob->axiom[ax] = 0; + } + + if (x) efficient_logic_set(); +} + + + + +/* +* The above sets all the axioms valid in a particular logic +* - for example, so that they are not printed. For the search +* however, it is much more efficient only to test a few of them +* as selected by this function. +*/ + +void efficient_logic_set() +{ + AXIOM ax; + + for ( ax = AxNull; ax < AXMAX; ax++ ) + taxiom[ax] = theJob->axiom[ax]; + + switch(theJob->logic) { + case Null_logic: + case FD: + case B: + case DW: + case TW: + case EW: + case T: + case LOGMAX: + break; + case R: + case RW: + case LIN: + case CK: + taxiom[AxB2] = false; + case S4: + case E: + taxiom[AxWB] = false; + taxiom[AxB] = false; + break; + } + + if ( theJob->axiom[AxC] ) + taxiom[AxC2] = true; +} diff --git a/src/logic_test.c b/src/logic_test.c new file mode 100644 index 0000000..518e773 --- /dev/null +++ b/src/logic_test.c @@ -0,0 +1,362 @@ +/* +* logic_test.c V2.1 (May 1993) +* +* This is the "test" module for MaGIC. Good_matrix is +* called from transref, and the rest of this file contains +* only what depends on it (with the exception of functions +* for removing isomorphs and for printing matrices, which +* are in other files). +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + + + +/* +* The logic of Good_matrix, schematically, is as follows. +* +* Translate the communication vector info into +* testable data structures. +* If these fail to satisfy some postulate +* Report the refutation just found to the +* search controller. +* return FALSE. +* endif +* If the badguy (if any) fails +* If the structure is not isomorphic +* to any already generated +* Print the matrix. +* endif +* endif +* return TRUE. +* +* The pre-defined axioms, including those defining the logic, +* are tested first. Then any user-defined ones are tested. +* +* Before that, however, check to see that the break conditions +* are not met. +*/ + +boolean Good_matrix(unsigned info[], trs T) +{ + int ti; + + if ( theJob->maxmat && good == theJob->maxmat) tr_par.done = true; + if ( tr_par.done ) return true; + + tot++; + vect_into_C(info); + + for ( ti = AxNull; ti < AXMAX; ti++ ) + if ( taxiom[ti] && TL[ti].many_test ) + if ( !(*(TL[ti].many_test))(T) ) return false; + if ( !fus_test(T) ) return false; + if ( !axtest(T) ) return false; + if ( theJob->failure && !got_a_fail ) { + no_ref( T ); return false; + } + if ( isomorphic(istak,T) ) return true; + mat_print(); + return true; +} + + + + + +/* +* Translate the communication vector into testable data structures. +*/ + +void vect_into_C(unsigned info[]) +{ + register int + i, j; + int k; + + FORALL(i) FORALL(j) C[i][j] = info[impindex[i][j]]; + if ( theJob->f_nec ) + FORALL(i) box[i] = info[boxindex[i]]; + for ( k = 0; theJob->dcs[k]; k++ ) + if ( theJob->defcon[k] == PRIMITIVE ) + switch( theJob->adicity[k] ) { + case 0: nulladic[k] = theJob->form[VMAX+4+k].val + = info[ucc0[k]]; + break; + case 1: FORALL(i) monadic[k][i] = info[ucc1[k][i]]; + break; + case 2: FORALL(i) FORALL(j) + dyadic[k][i][j] = info[ucc2[k][i][j]]; + } + for (i = 0; i < tr_par.vlength; i++) thisvector[i] = info[i]; + if ( theJob->f_n && theJob->f_nec ) + FORALL(i) diamond[i] = N[box[N[i]]]; +} + + + + + +/* +* Where fusion is stipulated to exist but is not definable, +* it is necessary to test for it. This tends to give large +* refutations, so it is done as late as possible. +* +* Defining fus[a][b] as the least x such that ord[a][C[b][x]], +* check that this exists and is also least in the relation ord. +*/ + +boolean fus_test(trs T) +{ int i,j,k,m; + + if ( !theJob->f_fus ) return true; + if ( theJob->axiom[AxC] && theJob->f_n ) { + FORALL(i) FORALL(j) fus[i][j] = N[C[i][N[j]]]; + return true; + } + + FORaLL(i) FORaLL(j) { + FORALL(fus[i][j]) + if ( ord[i][C[j][fus[i][j]]] ) break; + if ( fus[i][j] > siz ) { + FORALL(k) Ref( impindex[j][k], T ); + return false; + } + for ( k = fus[i][j]+1; k <= siz; k++ ) + if ( ord[i][C[j][k]] && !ord[fus[i][j]][k] ) { + for ( m = 0; m <= fus[i][j]; m++ ) + Ref( impindex[j][m], T ); + Ref( impindex[j][k], T ); + return false; + } + } + return true; +} + + + + + +/* +* Eventually it is necessary to test any user-defined axioms +* and rules there may be. This is much slower than testing +* pre-defined ones. +* +* The test given here is the stupidest imaginable, simply +* making all assignments of values to variables and working +* out the consequent values of all subformulas in each case. +* It works fairly well, however, except when there are many +* variables in use. Then its stupidity trips it up badly, so +* there is a case for using a more sophisticated algorithm +* where the task is large. +*/ + +boolean axtest(trs T) +{ + WFF *w; + int i, j; + + got_a_fail = 0; + if ( !**(theJob->croot) && !theJob->failure ) return true; + if ( *(theJob->defcon) ) setcon(); + for ( i = 0; i < CMAX; i++ ) { + theJob->form[atom[0][i]].val = 0; + theJob->form[atom[1][i]].val = 0; + } + theJob->form[0].val = 0; + for ( i = 1; i <= Vmax; i++ ) theJob->form[i].val = siz; + +WORK: for ( w = theJob->form+VMAX+4+(CMAX*3); w != tx; w++ ) + w->val = *(w->mtx + *(w->lv)*SZ + *(w->rv)); + + for ( i = 0; theJob->croot[i][0]; i++ ) if ( kost[i] > 1 ) { + if ( theJob->proot[i][0] != TRIVIAL ) { + for ( j = 0; theJob->proot[i][j]; j++ ) + if ( !desig[theJob->form[theJob->proot[i][j]].val] ) + goto AX_OK; + } + if ( theJob->croot[i][0] != ABSURD ) { + for ( j = 0; theJob->croot[i][j]; j++ ) + if ( desig[theJob->form[theJob->croot[i][j]].val] ) + goto AX_OK; + } + for ( j = 0; theJob->proot[i][j]; j++ ) + set_used( theJob->proot[i][j], T, true ); + for ( j = 0; theJob->croot[i][j]; j++ ) + set_used( theJob->croot[i][j], T, true ); + return false; +AX_OK: ; + } + if ( theJob->failure && !desig[theJob->form[theJob->failure].val] ) { + got_a_fail = 1; + for ( i = 1; i <= Vmax; i++ ) + badvalue[i] = rvu[i]? theJob->form[i].val: SZ; + } + + for ( i = 1; i <= Vmax; i++ ) + if ( vu[i] ) { + if ( theJob->form[i].val ) { + theJob->form[i].val--; + goto WORK; + } + theJob->form[i].val = siz; + } + + return true; +} + + + + + +/* +* Find the appeals to changeable values in subformula #x. +* Topper is a flag saying whether this is the outermost +* level (so that implications at that level can be read +* as appeals to ord). +*/ + +void set_used(int x, trs T, boolean topper) +{ + int i; + WFF *wf; + + if ( x <= 0 ) return; + wf = theJob->form+x; + + set_used( wf->lsub, T, false ); + set_used( wf->rsub, T, false ); + + if ( !strcmp(wf->sym->s,"->") && !topper ) + Ref( impindex[*(wf->lv)][*(wf->rv)], T ); + else if ( !strcmp(wf->sym->s,"!") ) + Ref( boxindex[*(wf->rv)], T ); + else if ( !strcmp(wf->sym->s,"?") ) + Ref( boxindex[N[*(wf->rv)]], T ); + else if ( !strcmp(wf->sym->s,"o") ) { + if ( theJob->f_n && valid[theJob->logic][AxC] ) + Ref( impindex[*(wf->lv)][N[*(wf->rv)]], T ); + else for ( i = 0; i <= wf->val; i++ ) + Ref( impindex[*(wf->rv)][i], T ); + } + else for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->dcs[i] == wf->sym ) { + if ( theJob->defcon[i] == PRIMITIVE ) + switch( theJob->adicity[i] ) { + case 0: Ref( ucc0[i], T ); + break; + case 1: Ref( ucc1[i][*(wf->rv)], T ); + break; + case 2: Ref( ucc2[i][*(wf->lv)][*(wf->rv)], T ); + break; + } + else { + if ( theJob->adicity[i] == 2 ) { + theJob->form[atom[0][i]].val + = *(wf->lv); + theJob->form[atom[1][i]].val + = *(wf->rv); + } + else if ( theJob->adicity[i] == 1 ) + theJob->form[atom[0][i]].val = *(wf->rv); + set_used(theJob->defcon[i], T, false); + } + break; + } + if ( wf->rsub ) + wf->val = *(wf->mtx + ((*(wf->lv))*SZ + *(wf->rv))); +} + + + + + +/* +* The routine to derive matrices for defined connectives +* calls the recursive getval as required. +*/ + +void setcon() +{ + int i, j, k, m; + + for ( i = 0; theJob->defcon[i]; i++ ) + if ( theJob->defcon[i] != PRIMITIVE ) + switch(theJob->adicity[i]) { + case 0: + nulladic[i] = theJob->form[VMAX+4+i].val = + getval(theJob->defcon[i]); + break; + case 1: + FORALL(j) { + for ( m = 0; m < CMAX; m++ ) + theJob->form[atom[0][m]].val = j; + monadic[i][j] = getval(theJob->defcon[i]); + } + break; + case 2: + FORALL(j) { + for (m = 0; m < CMAX; m++ ) + theJob->form[atom[0][m]].val = j; + FORALL(k) { + for ( m = 0; m < CMAX; m++ ) + theJob->form[atom[1][m]].val = k; + dyadic[i][j][k] = getval(theJob->defcon[i]); + } + } + } +} + + + + + +/* +* Recursively generate the value of a subformula. +*/ + +int getval(int y) +{ + WFF *x; + + x = theJob->form + y; + + if ( x->rsub < 1 ) + return x->val; + if ( x->lsub < 1 ) + return *(x->mtx + getval(x->rsub)); + return *(x->mtx + SZ*getval(x->lsub) + getval(x->rsub)); +} diff --git a/src/mp_parse.c b/src/mp_parse.c new file mode 100644 index 0000000..96db3c8 --- /dev/null +++ b/src/mp_parse.c @@ -0,0 +1,266 @@ +/* +* mp_parse.c V2.1 (May 1993) +* +* The parser for MaGIC and like programs. This accepts +* formulas in I Block normal form (that is, with our usual +* conventions about scope, association to the left, dots +* in place of parentheses etc. See works of R.K. Meyer or +* the author for details. Actual scope ordering is passed +* by means of the string cn which records the available +* connectives with the dyadic ones in scope order. +* +* Programs such as minlog also use this parser, so although +* for example WFF is defined in MaGIC.h it is defined again +* here to make life easier at some points. The WFF structure +* must have at least the three fields specified here, though +* of course it may have others if it is externally defined. +* +* Note that formula #0 is regarded as the dummy, to be +* returned in case of failure. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "MaGIC.h" + +#define P_OK (c && c->s[0] != ')') +#define ERROR(sy,ws) { strcpy(P_ERR[0],(sy)->s); strcpy(P_ERR[1],ws); return 0; } +#define ERROR1(ws) { strcpy(P_ERR[0],""); strcpy(P_ERR[1],ws); return 0; } + +static char P_ERR[2][32]; + + + + + +/* +* Now for the parser. It returns the offset of the successfully +* parsed formula, or 0 if the parse attempt was unsuccessful. +* +* s_parse (parse a subformula) is called to get the result. +* Finally, if an error was recorded, it is output. +* +* The parameters are the text to be parsed, the destination +* array of subformulas and the list of available connectives. +*/ + +int parse( symb fla[] ) +{ + int rc; + + rc = s_parse( fla ); + if ( !rc ) { + if ( xdialog ) printf("E"); + printf("\n ERROR: \"%s\" %s",P_ERR[0],P_ERR[1]); + } + return rc; +} + + + + + +/* +* S_parse parses successive subformulas which, separated by +* dyadic connectives, make up the string. It then calls +* Finish to sort out the binary parsing according to the +* scope conventions. +* +* This is a one-pass parser. +*/ + +int s_parse( symb fla[] ) +{ + int sbf[20]; /* Dyadic-separated subformulas */ + int pptr=0; /* Offset of current symbol */ + int oldptr; /* Previous value of pptr */ + int ns = 0; /* "Next subformula" */ + int i,j; + symb dcn[19]; /* Separating dyadics */ + symb c; + + c = *fla; + while ( P_OK ) { + oldptr = pptr; + while ( symbol_listed(1,(c=fla[pptr++])) ) ; + i = pptr-1; + if ( is_var(c) || symbol_listed(0,c) ) + sbf[ns] = Loc(c, 0, 0); + else if ( c->s[0] == '(' || c->s[0] == '.' ) { + if ( !(sbf[ns] = s_parse( fla+pptr)) ) return 0; + j = Match(fla+pptr-1); + if ( !j ) return 0; + pptr += j-1; + } + else ERROR(fla[pptr-1]," can't begin a subformula"); + if ( symbol_listed(1,fla[oldptr]) ) + while ( i > oldptr ) + sbf[ns] = Loc(fla[--i], 0, sbf[ns]); + c = fla[pptr]; + if ( P_OK ) { + if ( symbol_listed(2,c) ) + dcn[ns++] = c; + else ERROR(fla[pptr]," found where dyadic connective expected"); + c = fla[++pptr]; + if ( !P_OK) + ERROR(fla[pptr]," found where subformula expected to start"); + } + } + return Finish(sbf, dcn, ns); +} + + + + + + +/* +* Return the offset of the right parenthesis matching the +* left one at start. Remember a dot is a left parenthesis +* whose imaginary right mate is as far to the right as is +* reasonable. +*/ + +int Match( symb start[] ) +{ + int i; + + for ( i = 1; start[i] && start[i]->s[0] != ')'; i++ ) + if ( start[i]->s[0] == '(' ) + i += Match(start+i)-1; + if ( start[0]->s[0] == '.' ) + return i; + if ( !start[i] ) + ERROR1("Unmatched left parenthesis"); + return i+1; +} + + + + + +/* +* Given a formula A1 * .... * An where each Ai is a formula +* and each * is some dyadic connective, turn it into a binary +* wff tree as advertised in the definition of a formula. +* +* Array subf holds the offsets of the main subformulas. The +* corresponding connectives are in conn, while totl is the +* number of subformulas. +*/ + +int Finish( int subf[], symb conn[], int totl ) +{ + int i, m = 0; + int sk; + + if ( !totl ) return *subf; + sk = 0; + for (i = 0; i < totl; i++) + if ( symbol_position(2,conn[i]) >= sk) { + m = i; + sk = symbol_position(2,conn[i]); + } + i = m+1; + return Loc(conn[m], + Finish(subf, conn, m), + Finish(subf+i, conn+i, totl-i) + ); +} + + + + + +/* +* Locate the given formula in the stack of those already +* parsed and return its offset. If it is not already there +* then add it and return its offset. +* +* Note that in order to force variables to the bottom of the +* stack of subformulas, there may be a section reserved for +* them with symbols initialised to '?'. If not, never mind. +*/ + +int Loc( symb mn, int lft, int rgt ) +{ + int i; + + for (i = 0; theJob->form[i].sym; i++) + if ( theJob->form[i].sym == mn && theJob->form[i].lsub == lft + && theJob->form[i].rsub == rgt ) + return i; + + else if ( is_var(mn) && theJob->form[i].sym->s[0] == '.' ) + break; + + theJob->form[i].sym = mn; + theJob->form[i].lsub = lft; + theJob->form[i].rsub = rgt; + return i; +} + + + + + +/* +* A variable is any lower-case letter that is not a connective. +*/ + +boolean is_var(symb x) +{ + int i; + + for ( i = 0; i < 3; i++ ) + if ( symbol_listed(i,x) ) return 0; + return (isalpha(x->s[0]) && !(x->s[1])); +} + + + + +/* +* Return the offset of symbol s in array theJob->symbol[x]. +*/ + +int symbol_position( int x, symb s ) +{ + + int i; + + for ( i = 0; theJob->symbol[x][i]; i++ ) + if ( theJob->symbol[x][i] == s ) return i; + return -1; +} diff --git a/src/nt_homomorphic.c b/src/nt_homomorphic.c new file mode 100644 index 0000000..df97769 --- /dev/null +++ b/src/nt_homomorphic.c @@ -0,0 +1,11 @@ +#include "RM.h" +#include "hmi.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) + Abort("synopsis: nt_homomorphic ",-1); + relatemats(nt_homo, argv[1], "NontrivialHomomorphism"); + + return 0; +} diff --git a/src/one_plus_t_gen.c b/src/one_plus_t_gen.c new file mode 100644 index 0000000..d5b954d --- /dev/null +++ b/src/one_plus_t_gen.c @@ -0,0 +1,94 @@ + +#include "RM.h" + + +static int tot_got; /* Total of elements generated */ +static int got[SZ]; /* Elements generated so far */ + + + +int main( argc, argv ) +int argc; +char *argv[]; +{ + int option; + PRINTMODE p = UGLY; + int g2(); + int getopt(); + + while ((option = getopt (argc, argv, "upt")) != -1) + switch( option ) { + case 'p': p = PRETTY; break; + case 't': p = TeX; + } + selectmats( g2, p ); + return 0; +} + + + + +int g2(m) +MATRIX *m; +{ + int i; + int generator; + int position = 0; + void try(); + + FORALL(m,generator) { + FORALL(m,i) got[i] = 0; + if (m->tee_exists) { + got[m->tee] = 1; + tot_got = 1; + } + else tot_got = 0; + FORALLCON(m,i) + if ( m->adicity[i] == 0 ) try( m, m->nulladic[i] ); + try( m, generator ); + if ( tot_got > m->siz ) { + m->my_values[position] = generator; + if ( position++ ) + strcpy( m->my_string, "Possible generators: "); + else strcpy( m->my_string, "Generator: "); + } + } + return( position > 0 ); +} + + + + + +void try(m,x) +MATRIX *m; +int x; +{ + int i, j; + + if ( got[x] ) return; + + got[x] = 1; + tot_got++; + if ( m->fragment[NEG] ) try( m, m->neg[x] ); + if ( m->fragment[BOX] ) try( m, m->box[x] ); + FORALLCON(m,i) + if ( m->adicity[i] == 1 ) try( m, m->monadic[i][x] ); + FORALL(m,i) if ( got[i] ) { + try( m, m->C[i][x] ); + try( m, m->C[x][i] ); + if ( m->fragment[LAT] ) { + try( m, m->K[i][x] ); + try( m, m->A[i][x] ); + } + if ( m->fragment[FUS] ) { + try( m, m->fus[i][x] ); + try( m, m->fus[x][i] ); + } + FORALLCON(m,j) { + if ( m->adicity[j] == 2 ) + try( m, m->dyadic[j][i][x] ); + try( m, m->dyadic[j][x][i] ); + } + } +} diff --git a/src/onegen.c b/src/onegen.c new file mode 100644 index 0000000..a62b3a4 --- /dev/null +++ b/src/onegen.c @@ -0,0 +1,152 @@ +/* One.c +* +* This uses RM (Read Matrices) to read ugly output from +* MaGIC and calls a function "g1" to choose those matrices +* generated by a single element using only the connectives +* present in the fragment, with the exception of the normal +* sentential constants. The one-generated matrices are +* printed out. The print format may be changed from the +* default (UGLY) to PRETTY or TeX by means of the command +* line switches "-p" or "-t". +* +* For details of the matrix structures and other goodies +* defined in the header, see RM.h. The following should be +* fairly self-explanatory, however. Note that my_values and +* my_string are optional extras used by printmat. These are +* re-initialised to <-1,-1,-1,....> and "" by mat_malloc. +* Use strcpy to put a message in m->my_string. +* +* Function g1 finds all the elements whose singletons generate +* the algebra, records them in my_values and returns 1 to print +* the matrix or 0 to suppress printing. +* +* Try is the recursive procedure for adding new elements +* to the array got[]. It is beside the point if element +* x has already been generated. +* +* Command line option -u (ugly) is allowed but has no effect +* since UGLY is the default print mode. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include "RM.h" + + +static int tot_got; /* Total of elements generated */ +static int got[SZ]; /* Elements generated so far */ + + + +int main( argc, argv ) +int argc; +char *argv[]; +{ + int option; + PRINTMODE p = UGLY; + int g1(); + int getopt(); + + while ((option = getopt (argc, argv, "upt")) != -1) + switch( option ) { + case 'p': p = PRETTY; break; + case 't': p = TeX; + } + selectmats( g1, p ); + return 0; +} + + + + +int g1(m) +MATRIX *m; +{ + int i; + int generator; + int position = 0; + void try(); + + FORALL(m,generator) { + FORALL(m,i) got[i] = 0; + tot_got = 0; + FORALLCON(m,i) + if ( m->adicity[i] == 0 ) try( m, m->nulladic[i] ); + try( m, generator ); + if ( tot_got > m->siz ) { + m->my_values[position] = generator; + if ( position++ ) + strcpy( m->my_string, "Possible generators: "); + else strcpy( m->my_string, "Generator: "); + } + } + return( position > 0 ); +} + + + + + +void try(m,x) +MATRIX *m; +int x; +{ + int i, j; + + if ( got[x] ) return; + + got[x] = 1; + tot_got++; + if ( m->fragment[NEG] ) try( m, m->neg[x] ); + if ( m->fragment[BOX] ) try( m, m->box[x] ); + FORALLCON(m,i) + if ( m->adicity[i] == 1 ) try( m, m->monadic[i][x] ); + FORALL(m,i) if ( got[i] ) { + try( m, m->C[i][x] ); + try( m, m->C[x][i] ); + if ( m->fragment[LAT] ) { + try( m, m->K[i][x] ); + try( m, m->A[i][x] ); + } + if ( m->fragment[FUS] ) { + try( m, m->fus[i][x] ); + try( m, m->fus[x][i] ); + } + FORALLCON(m,j) { + if ( m->adicity[j] == 2 ) + try( m, m->dyadic[j][i][x] ); + try( m, m->dyadic[j][x][i] ); + } + } +} diff --git a/src/setup.c b/src/setup.c new file mode 100644 index 0000000..b6126cc --- /dev/null +++ b/src/setup.c @@ -0,0 +1,464 @@ +/* +* setup.c V2.1 (May 1993) +* +* These functions have nothing in common except that they +* are called near the outer levels of MaGIC and don't really +* fit into any other category. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + + +#include // needed for gettimeofday data types +#include // because we use gettimeofday for timing +#include // nobody knows why this is included +#include // because we use "open" to read data file + +#include "MaGIC.h" + + + + +/* +* Subf_set completes the contents of theJob->form. +* It also sets up vu[] and kost[]. +*/ + +void subf_set() +{ + int i, j; + + for ( i = 0; theJob->form[i].sym; i++ ) { + theJob->form[i].val = 0; + theJob->form[i].mtx = 0; + theJob->form[i].lv = + &(theJob->form[theJob->form[i].lsub].val); + theJob->form[i].rv = + &(theJob->form[theJob->form[i].rsub].val); + for ( j = 0; theJob->dcs[j]; j++ ) + if ( theJob->dcs[j] == theJob->form[i].sym ) { + switch( theJob->adicity[j] ) { + case 0: + theJob->form[i].mtx = nulladic+j; + break; + case 1: + theJob->form[i].mtx = monadic[j]; + break; + case 2: + theJob->form[i].mtx = *(dyadic[j]); + } + break; + } + if ( !theJob->form[i].mtx && + !strcmp(theJob->form[i].sym->s,"->") ) + theJob->form[i].mtx = *C; + else if ( !theJob->form[i].sym->s[1] && !theJob->form[i].mtx ) + switch( theJob->form[i].sym->s[0] ) { + case '~': + theJob->form[i].mtx = N; + break; + case '!': + theJob->form[i].mtx = box; + break; + case '?': + theJob->form[i].mtx = diamond; + break; + case 'v': + theJob->form[i].mtx = *A; + break; + case '&': + theJob->form[i].mtx = *K; + break; + case 'o': + theJob->form[i].mtx = *fus; + } + } + tx = theJob->form+i; + + for ( i = 0; i < VMAX; i++ ) vu[i] = rvu[i] = 0; + Vmax = 0; + if ( theJob->failure ) { + set_u(vu,theJob->failure); + set_u(rvu,theJob->failure); + } + for ( i = 0; theJob->croot[i][0]; i++ ) { + kost[i] = 0; + for ( j = 0; theJob->croot[i][j]; j++ ) { + set_u(vu,theJob->croot[i][j]); + kost[i] += worstcase(theJob->croot[i][j],0); + } + for ( j = 0; theJob->proot[i][j]; j++ ) { + set_u(vu,theJob->proot[i][j]); + kost[i] += worstcase(theJob->proot[i][j],0); + } + } +} + + + + + +/* +* Worstcase calculates the maximum number of lookups of +* changeable matrices in the assignment of a value to the +* formula rooted at x. This is 0 if that formula is a variable, +* a pre-defined constant or null. Otherwise it is the worstcases +* of its left and right subformulas plus the cost of its main +* connective. This cost is 0 for ~, & and v, 1 for -> and any +* user-given primitives, the worstcase of the definition for any +* user-defined connective and a number depending on the logic and +* the matrix size in the case of o. +*/ + +int worstcase(int x, int ntop) +{ + int i, wcsum; + WFF *wf; + + if ( x < 1 ) return 0; + + wf = theJob->form+x; + + if ( is_var(wf->sym) ) return 0; + + wcsum = worstcase(wf->lsub,1) + worstcase(wf->rsub,1); + + if ( !strcmp(wf->sym->s,"->") ) return (wcsum + ntop); + if ( !wf->sym->s[1] ) switch( wf->sym->s[0] ) { + case '&': + case 'v': + case '~': + case 't': + case 'f': + case 'T': + case 'F': return wcsum; + case '!': return (wcsum + 1); + case '?': return (wcsum + 1); + case 'o': + if ( F_N && theJob->axiom[AxC] ) + return (wcsum + 1); + return (wcsum + siz + 1); + } + + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->dcs[i] == wf->sym ) { + if ( theJob->defcon[i] == PRIMITIVE ) + return (wcsum + 1); + return (wcsum + worstcase(theJob->defcon[i],1)); + } + /* + This case should never happen! + */ + skipout("Unrecognised symbol in a formula",SKIP); + return 0; +} + + + + +/* +* Record the variables used in the formula rooted at x by +* setting the array arr. +*/ + +void set_u(int arr[], int x) +{ + if ( x < 1 ) return; + if ( x < VMAX ) { + *(arr+x) = 1; + if ( x > Vmax ) Vmax = x; + } + else { + set_u(arr,theJob->form[x].lsub); + set_u(arr,theJob->form[x].rsub); + } +} + + + + +/* +* Here is a clock function which ought to work even in +* the worst of times. +*/ + +void CLoCK(int *timer) +{ + struct timeval tp; + struct timezone tzp; + +#ifdef HASTIMES + times(&time_buffer); +#endif + gettimeofday(&tp,&tzp); + *timer = tp.tv_sec & 0xffff; + *timer *= TICK; + *timer += (tp.tv_usec / (1000000/TICK)); +} + + + +/* +* This is called from got_ord in logic_io.c to set up the +* index from the structures representing the models to the +* vector to be used by transref. At the same time, it labels +* `forbidden' any cells at which backtracking is not allowed. +*/ + +void set_up_cc() +{ + int i, j, k; + boolean fb; + + tr_par.vlength = 0; + for ( i = 0; theJob->dcs[i]; i++ ) ; + while ( i ) { + if ( theJob->defcon[--i] == PRIMITIVE ) { + fb = theJob->concut[i]; + switch ( theJob->adicity[i] ) { + case 0: + ucc0[i] = tr_par.vlength++; + tr_par.forbidden[ucc0[i]] = fb; + break; + case 1: + FORALL(j) { + ucc1[i][j] = tr_par.vlength++; + tr_par.forbidden[ucc1[i][j]] = fb; + } + break; + case 2: + FORALL(j) FORALL(k) { + ucc2[i][j][k] = tr_par.vlength++; + tr_par.forbidden[ucc2[i][j][k]] = fb; + } + } + } + } + if ( theJob->f_nec ) { + firstbox = tr_par.vlength; + FORALL(i) { + boxindex[i] = tr_par.vlength++; + tr_par.forbidden[boxindex[i]] = false; + } + } + firstarrow = tr_par.vlength; + FORALL(i) FORALL(j) + if ( !( F_N && i < N[j] )) { + impindex[i][j] = tr_par.vlength++; + tr_par.forbidden[impindex[i][j]] = false; + if ( F_N ) impindex[N[j]][N[i]] = impindex[i][j]; + } + if ( tr_par.vlength > V_LENGTH ) { + printf(" %d > %d\n", tr_par.vlength, V_LENGTH); + skipout( "V_LENGTH is too small for this job", SKIP ); + } +} + + + +/* +* This is called immediately before transref. +*/ + +void job_start() +{ + char infil_name[100]; + + set_orders(infil_name); + if ( (infil = open(infil_name,0)) < 0 ) + skipout("Failed to open the data file",SKIP); + input_bit = 8; + if ( *(theJob->outfil_name) && !filing ) { + outfil = fopen(theJob->outfil_name,"w"); + filing = 1; + } + if ( theJob->fil_out == PRETTY || theJob->fil_out == SUMMARY ) + disp(outfil); + else if ( theJob->fil_out == UGLY ) + uglydisp(outfil); + if ( theJob->tty_out == UGLY ) + uglydisp(stdout); + + logic_axioms(1); + + CLoCK(&start_time); +#ifdef HASTIMES + begin_timer = time_buffer.tms_utime; +#endif + + good = tot = isoms = isoms2 = 0; + siz = 0; + set_up_trin(); +} + + + + +/* +* This, on the other hand, is not. +*/ + +void job_stop(boolean batch) +{ + int i; + + CLoCK( &stop_time ); +#ifdef HASTIMES + end_timer = time_buffer.tms_utime; +#endif + + if ( theJob->tty_out == UGLY ) { + printf(" -1\n"); + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) printf(" -1\n"); + } + if (theJob->fil_out == UGLY ) { + fprintf(outfil, " -1\n"); + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + fprintf(outfil, " -1\n"); + } + if ( !batch ) { + stats_print(); + paws(); + } + if ( filing ) { + fclose(outfil); + filing = 0; + } + + if ( infil ) { + close(infil); + infil = 0; + } + tr_par.done = true; + logic_axioms(false); +} + + + + + +/* +* Set_orders works out the input file name and extends the +* string passed to it with that name. +*/ + +void set_orders(char s[]) +{ + strcpy( s, theJob->data_dir ); + + if ( theJob->totord ) + strcat(s,(theJob->f_n? "tn": "to")); + else if ( theJob->f_lat ) { + if ( !theJob->distrib ) + strcat(s,(theJob->f_n? "ln": "l")); + else if ( theJob->axiom[AxBA] || + ( theJob->logic==S4 && theJob->f_n )) + strcat(s,"ba"); + else strcat(s,(theJob->f_n? "dln": "dl")); + } + else { + if ( theJob->f_n && theJob->f_t ) + strcat(s,"pont"); + else if ( theJob->f_n ) + strcat(s,"pon"); + else if ( theJob->f_t ) + strcat(s,"pot"); + else strcat(s,"po"); + } + + sprintf( s+strlen(s), ".%d", Sizmax ); +} + + + + +/* +* This routine just prints the header for ugly output. +*/ + +void uglydisp(FILE *f) +{ + int i, j; + + fprintf( f, " %d", theJob->f_n ); + fprintf( f, " %d", theJob->f_t ); + fprintf( f, " %d", theJob->f_T ); + fprintf( f, " %d", theJob->f_F ); + fprintf( f, " %d", theJob->f_fus ); + fprintf( f, " %d", theJob->f_lat ); + fprintf( f, " %d", theJob->f_nec ); + j = 0; + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) j++; + fprintf( f, " %d", j ); + for ( i = 0; theJob->dcs[i]; i++ ) + if ( theJob->defcon[i] == PRIMITIVE ) + fprintf( f, " %d %s", theJob->adicity[i], theJob->dcs[i]->s ); + fprintf( f, "\n" ); +} + + +/* +* The job parameters are communicated to transref via a structure +* tr_par which must be set up before each call. Note that +* tr_par.forbidden which records functions on which there is a +* "cut" is set elsewhere. +*/ + +void set_up_trin() +{ + tr_par.Test = Good_matrix; + tr_par.Onerefs = set_poss; + tr_par.Smallrefs = find_twos; + tr_par.batches = 10; + tr_par.extra_batches = EXTRA_DEFAULT; + tr_par.maxref = 6; + tr_par.maxbak = 0; + tr_par.verb = 0; + tr_par.Nosecs = false; + tr_par.topsoff = true; + tr_par.report_breaks = false; + tr_par.done = false; +} + + +/* +* These stubs are just to maintain compatibility with the vntr used +* by FINDER. They are not called. +*/ + +void report_branches(trin T) {} +void ref_size_report() {} + diff --git a/src/sub_irr.c b/src/sub_irr.c new file mode 100644 index 0000000..758b247 --- /dev/null +++ b/src/sub_irr.c @@ -0,0 +1,202 @@ +/* One.c +* +* This uses RM (Read Matrices) to read ugly output from MaGIC and calls +* a function "si" to choose those matrices which are subdirectly +* irreducible under the connectives present in the fragment. The +* matrices are printed out. The print format may be changed from the +* default (UGLY) to PRETTY or TeX by means of the command line switches +* "-p" or "-t". +* +* For details of the matrix structures and other goodies defined in the +* header, see RM.h. The following should be fairly self-explanatory, +* however. Note that my_values and my_string are optional extras used +* by printmat. These are re-initialised to <-1,-1,-1,....> and "" by +* mat_malloc. Use strcpy to put a message in m->my_string. +* +* Command line option -u (ugly) is allowed but has no effect since UGLY +* is the default print mode. +*/ + + + +#include "RM.h" + + +int si(MATRIX *m); +int non_degenerate(MATRIX* m, int g1, int g2, int congruence[]); +void adjust(MATRIX *m, int congruence[], int from, int to); +int extended(MATRIX *m, int congruence[], int a, int b); +int monex(MATRIX *m, int arr[], int a, int b, int congruence[]); +int dyex(MATRIX *m, int arr[][SZ], int a, int b, int congruence[]); +int orthogonal(MATRIX *m, int theCongruences[][SZ]); + + + +int main( argc, argv ) +int argc; +char *argv[]; +{ + int option; + PRINTMODE p = UGLY; + int g1(); + int getopt(); + + while ((option = getopt (argc, argv, "upt")) != -1) + switch( option ) { + case 'p': p = PRETTY; break; + case 't': p = TeX; + } + selectmats(si,p); + return 0; +} + + + + +int si(MATRIX *m) +{ + int theCongruences[2][SZ]; + int gen1a, gen1b, gen2a, gen2b; + + FORALL(m,gen1a) FORALL(m,gen1b) if (gen1b < gen1a) { + if (non_degenerate(m,gen1a,gen1b,theCongruences[0])) { + FORALL(m,gen2a) FORALL(m,gen2b) if (gen2b < gen2a) { + if (non_degenerate(m,gen2a,gen2b,theCongruences[1]) && + orthogonal(m,theCongruences)) + return 0; + } + } + } + return(1); +} + + +int non_degenerate(MATRIX* m, int g1, int g2, int congruence[]) +{ + int x, y, temp; + + for (x=0; x= 0) { + temp--; + adjust(m,congruence,congruence[x],temp); + } + for (x=-2,y=0; x>=temp; x--,y++) + adjust(m,congruence,x,y); + if (y == 0) + return 0; + return 1; +} + + +void adjust(MATRIX *m, int congruence[], int from, int to) +{ + int x; + + FORALL(m,x) + if (congruence[x] == from) + congruence[x] = to; +} + + + +int extended(MATRIX *m, int congruence[], int a, int b) +{ + int x; + int ex = 0; + + if (m->fragment[NEG] && + monex(m,m->neg,a,b,congruence)) + ex = 1; + if (m->fragment[BOX] && + monex(m,m->box,a,b,congruence)) + ex = 1; + FORALLCON(m,x) + if (m->adicity[x] == 1 && + monex(m,m->monadic[x],a,b,congruence)) + ex = 1; + if (dyex(m,m->C,a,b,congruence)) + ex = 1; + if (m->fragment[LAT] && + dyex(m,m->K,a,b,congruence)) + ex = 1; + if (m->fragment[LAT] && + dyex(m,m->A,a,b,congruence)) + ex = 1; + if ( m->fragment[FUS] && + dyex(m,m->fus,a,b,congruence)) + ex = 1; + FORALLCON(m,x) + if ( m->adicity[x] == 2 && + dyex(m,m->dyadic[x],a,b,congruence)) + ex = 1; + return ex; +} + + +int monex(MATRIX *m, int arr[], int a, int b, int congruence[]) +{ + if (congruence[arr[a]] == congruence[arr[b]]) + return 0; + adjust(m,congruence,congruence[arr[b]],congruence[arr[a]]); + return 1; +} + + +int dyex(MATRIX *m, int arr[][SZ], int a, int b, int congruence[]) +{ + int x; + int ext = 0; + + FORALL(m,x) { + if (congruence[arr[x][a]] != congruence[arr[x][b]]) { + adjust(m,congruence,congruence[arr[x][b]],congruence[arr[x][a]]); + ext = 1; + } + if (congruence[arr[a][x]] != congruence[arr[b][x]]) { + adjust(m,congruence,congruence[arr[b][x]],congruence[arr[a][x]]); + ext = 1; + } + } + return ext; +} + + + +int orthogonal(MATRIX *m, int theCongruences[][SZ]) +{ + int x,y; + + FORALL(m,x) + FORALL(m,y) + if (xcomvec and sent to be tested for goodness by a function +* Job->test which is independent of this module. If the vector is not +* good, a "refutation" is constructed by means of calls to the +* function Ref. This is a subset of the vector cell/value pairs +* which should not be incorporated in any candidate vector. The +* refutations, or negative constraints, are kept in a database +* indexed by linked lists starting from the various +* T->vector[_].cvals[_].cvrefs for fast lookup. Impossible values +* may be removed from the sets of possibilities before the search +* starts. This is done by the function Job->Onerefs which is also +* independent of any details in this file. Additionally, small +* refutations may be detected in advance and stored in the database +* before the search begins. This is done by Job->Smallrefs which +* calls the function new_small_ref in this file with each +* refutation it wants to add. "Small" is not defined. +* +* The basic action is to pick a cell to be treated next and to insert +* each of its possible values in turn, each time visiting the +* constraint database to remove possible values inconsistent with +* the chosen one and so forth, and each time recursively searching +* the space constituted by the remaining cells. Cells with only +* one possible value are chosen before any others, then ones with +* higher priority before those with lower, and within each priority +* ones with the smallest number of remaining possible values first. +* +* If the search space is too big to be handled in one hit, as +* represented by a limit on the number of batches of 1024 +* refutations allowed for the database, a "cut cell" is chosen (the +* forkpoint, or first one on which the search forks) and we divide +* on that cell in order to conquer, first making its current value +* the only one possible for it, and then making just the larger +* values possible. +* +* There are too many details of the implementation to be described in +* this short comment. For an account, see the chapter "How It Works" +* in the User's Guide. +*/ + + + + +#define TRFILE + +#include +#include +#include + +#include "vntr.h" + + + +/* +* This is the top level function for this file. The "Job" contains a +* succinct description of the problem, consisting of the length of +* the vectors required, the addresses of the test function etc. and +* some miscellaneous settings. The search is performed on the whole +* space and then on each subspace created by "cutting" (see above) +* until there is no "cut" in the stack. */ + +void transref( trin Job ) +{ + trs T; + int local_time[3]; + void dump_cuts(); + + if ( Job->done ) return; + T = tr_initial( Job ); + do { + local_time[0] = timestamp(); + if ( Job->verb > 1 ) dump_cuts(T); + T->local_memory = 0; + if ( prepared( T ) + && !T->novect + && pre_test( T )) { + local_time[1] = timestamp(); + Job->reps[PRE_TIME].f_report += + ((local_time[1]-local_time[0]) * 1.0) / CLK_TCK; + search( T ); + Job->reps[SEARCHES].i_report++; + local_time[2] = timestamp(); + Job->reps[SEARCH_TIME].f_report += + ((local_time[2]-local_time[1]) * 1.0) / CLK_TCK; + } + clean_up( T ); + } + while ( T->cutptr && !T->job->done ); + free(T); +} + + + +/* +* This is not part of the search. It just initialises a job description. +*/ + +void init_trin( trin tr ) +{ + int i, j; + + tr->Test = 0; + tr->Onerefs = 0; + tr->Smallrefs = 0; + tr->batches = REF_DEFAULT; + tr->extra_batches = EXTRA_DEFAULT; + tr->maxref = MAX_REF_SIZE; + + for ( i = 0; i < MAX_REPORT; i++ ) { + tr->reps[i].report_type = 0; + tr->reps[i].i_report = 0; + tr->reps[i].f_report = 0; + tr->reps[i].do_report = 0; + } + + for ( i = 0; i < RRA; i++ ) + for ( j = 0; j < RRB; j++ ) + tr->ref_record[i][j] = 0; + + for ( i = 0; i < SZ; i++ ) + tr->branches[i] = 0; +} + + + +/* +* Clean up after a loop through the search by deleting the database +* and freeing the memory it used. This is mostly just garbage +* collection. +* +* After a search, there may be a dangling active cut, or the need for +* a cut may have been signalled but not acted upon. This function +* puts all such things back into their proper state, and activates +* the cut for the next loop if there is one. */ + +void clean_up( trs T ) +{ + int i; + + for ( i = 0; T->stak[i] && i < T->job->batches; i++ ) { + free( T->stak[i] ); + T->stak[i] = 0; + } + for ( i = 0; T->il_stak[i] && i < T->stakmax; i++ ) { + free( T->il_stak[i] ); + T->il_stak[i] = 0; + } + for ( i = 0; T->rl_stak[i] && i < T->job->batches+T->stakmax; i++ ) { + free( T->rl_stak[i] ); + T->rl_stak[i] = 0; + } + if ( !T->cutptr ) return; + if ( T->cutptr->active ) delete_cut( T ); + T->cut_flag = NOCUT; + if ( T->cutptr ) T->cutptr->active = true; + if ( T->local_memory > T->job->reps[LOC_MEMORY].i_report ) + T->job->reps[LOC_MEMORY].i_report = T->local_memory; +} + + + +/* +* It is possible to report many statistics on the execution. The routine +* for printing these out is somewhere else in the code, but the definitions +* of the printable quantities are here. Each report has a type (integer, +* floating point or function) and am identifying string. +*/ + +void report_initial( trs T ) +{ + int i; + + switch( T->job->verb ) { + case 0: + T->job->reps[0].report_type = 0; + return; + + case 1: + for ( i = 0; i < SOLUTIONS; i++ ) + T->job->reps[i].report_type = NO_REPORT; + strcpy(T->job->reps[SOLUTIONS].s_report, "Solutions found: "); + T->job->reps[SOLUTIONS].report_type = INT_REPORT; + T->job->reps[SOLUTIONS+1].report_type = 0; + return; + + case 2: + strcpy(T->job->reps[SOLUTIONS].s_report, "Solutions found: "); + T->job->reps[SOLUTIONS].report_type = INT_REPORT; + + strcpy(T->job->reps[SEARCHES].s_report, "Subspaces searched: "); + T->job->reps[SEARCHES].report_type = INT_REPORT; + + strcpy(T->job->reps[SETPOSSES].s_report, "Subspaces prepared: "); + T->job->reps[SETPOSSES].report_type = INT_REPORT; + + strcpy(T->job->reps[DOSMALLS].s_report, "Subspaces preprocessed:"); + T->job->reps[DOSMALLS].report_type = INT_REPORT; + + strcpy(T->job->reps[BAD_TESTS].s_report, "Bad candidates tested: "); + T->job->reps[BAD_TESTS].report_type = INT_REPORT; + + strcpy(T->job->reps[SURJ_BACKTRACKS].s_report, + "Surjection backtracks: "); + T->job->reps[SURJ_BACKTRACKS].report_type = INT_REPORT; + + strcpy(T->job->reps[NULL_BACKTRACKS].s_report, + "Other backtracks: "); + T->job->reps[NULL_BACKTRACKS].report_type = INT_REPORT; + + strcpy(T->job->reps[LOC_MEMORY].s_report, "Maximum database size: "); + T->job->reps[LOC_MEMORY].report_type = INT_REPORT; + + strcpy(T->job->reps[MEMORY].s_report, "Total database entries:"); + T->job->reps[MEMORY].report_type = INT_REPORT; + + strcpy(T->job->reps[PRE_TIME].s_report, "Preprocess time: "); + T->job->reps[PRE_TIME].report_type = FLOAT_REPORT; + + strcpy(T->job->reps[SEARCH_TIME].s_report,"Search time: "); + T->job->reps[SEARCH_TIME].report_type = FLOAT_REPORT; + + strcpy(T->job->reps[PREREFS].s_report, "Pre-test refutations: "); + T->job->reps[PREREFS].report_type = INT_REPORT; + + strcpy(T->job->reps[F_SUBSUMED].s_report, "Forward subsumptions: "); + T->job->reps[F_SUBSUMED].report_type = INT_REPORT; + + strcpy(T->job->reps[B_SUBSUMED].s_report, "Back subsumptions: "); + T->job->reps[B_SUBSUMED].report_type = INT_REPORT; + + strcpy(T->job->reps[B1_SUBSUMED].s_report," Back subs by units: "); + T->job->reps[B1_SUBSUMED].report_type = INT_REPORT; + + strcpy(T->job->reps[TOPLESS].s_report, "No-top deletions: "); + T->job->reps[TOPLESS].report_type = INT_REPORT; + + strcpy(T->job->reps[BRANCHES].s_report, "Branches"); + T->job->reps[BRANCHES].report_type = DO_REPORT; + T->job->reps[BRANCHES].do_report = report_branches; + + strcpy(T->job->reps[REFSIZE].s_report, "Search refutations: "); + T->job->reps[REFSIZE].report_type = INT_REPORT | DO_REPORT; + T->job->reps[REFSIZE].do_report = ref_size_report; + + T->job->reps[MAX_REPORT].report_type = 0; + } +} + + + +/* +* This is the initialisation function for a TRS structure, which +* contains all the variables used by functions in this file to +* communicate with each other. It is really a subroutine of transref +* above. The job description becomes a field of the TRS so that every +* function can refer to it. +*/ + +trs tr_initial( trin Job ) +{ + trs T; + int i; + + if ( !(Job->vlength) ) + skipout( "Attempt to search null space", NULL_SPACE ); + if ( Job->vlength > V_LENGTH ) + skipout( "Search space too big to fit in vector length", BIG_SPACE ); + if ( Job->batches < 1 ) { + fprintf(stderr,"Setting stack size to 1\n"); + Job->batches = 1; + } + if ( Job->extra_batches < 1 ) { + fprintf(stderr,"Setting marginal stack size to 1\n"); + Job->extra_batches = 1; + } + if ( Job->batches >= REF_MAX ) { + fprintf(stderr,"Setting stack size to %d\n", REF_MAX-1); + Job->batches = REF_MAX-1; + } + if ( Job->maxref < 1 ) { + fprintf(stderr,"Setting ref-length to 1\n"); + Job->maxref = 1; + } + if ( Job->maxref > MAX_REF_SIZE ) + fprintf(stderr, "Warning: ref-length %d exceeds notional maximum %d\n", + Job->maxref, MAX_REF_SIZE); + + T = (trs) malloc(sizeof(TRS)); + T->job = Job; + T->max_batches = T->job->batches - 1; + T->stakmax = (Job->maxref < 4? 4: Job->maxref) * Job->batches; + report_initial( T ); + T->cutptr = 0; + T->noref = false; + T->cut_flag = NOCUT; + for ( i = 0; i <= T->job->vlength; i++ ) T->priority[i] = 0; + return T; +} + + + + +/********************************************************************* +* +* This is where the going gets serious. The search function calls +* itself recursively to search the subspaces determined by setting +* the value of the picked cell successively in each possible +* way. Value wy is inserted in cell ex. If the picked cell is -1 this +* means the vector is complete and should be tested. +* +* When the program is re-constituting its search after a "cut", it +* has to take account of the values that cells had before the +* cut. This is done by attending to the starting point for the loop +* of assignments to wy. +*/ + +void search( trs T ) +{ + cell c; + int ex, wy; + boolean inok, ccut, fch; + + if ( T->job->done || T->novect ) return; + + ex = pick_cell( T ); + if ( ex < 0 ) { + if ( T->job->Test ) { + T->noref = false; + if ( (*T->job->Test)(T->comvec,T) ) { + T->job->reps[SOLUTIONS].i_report++; + T->firstchange = V_LENGTH; + } + else { + if ( T->noref ) T->noref = false; + else setref( T ); + T->job->reps[BAD_TESTS].i_report++; + } + } + else T->job->done = true; + } + else { + c = T->vector + ex; + for ( wy = (T->cutptr && T->cutptr->active)? T->cutptr->comvec[ex] :0; + wy <= T->maxval && !T->novect; wy++ ) + if ( !(c->cvals[wy].blocks) ) { + if ( T->cutptr && T->cutptr->active + && wy != T->cutptr->comvec[ex] ) delete_cut( T ); + inok = invalue( T, c, wy ); + if ( T->cut_flag == FOUND && T->forkpoint >= 0 ) + record_cut( T ); + else if ( inok && !T->ts ) search( T ); + if ( T->job->done ) return; + if ( T->ts ) totsus( T ); + ccut = (T->cut_flag == RECORDED && ex == T->forkpoint); + if ( (fch = (T->coinorder[ex]) <= T->firstchange) ) + T->firstchange = T->coinorder[ex]; + outvalue( T, c ); + if ( T->ts && T->vector[T->susguy].poss ) T->ts = false; + if ( ccut ) complete_cut( ex, T ); + if ( T->newref ) { + if ( c->used ) { + addref( ex, T ); + if ( T->ts ) totsus( T ); + } + else break; + } + if ( fch && T->job->forbidden[ex] ) break; + } + } +} + + + +/* +* The next routine picks the next cell to have its value fixed. +* If there is a cell with only one possible value, that is picked. +* Otherwise, pick the cell with the smallest number of possible +* values among those with the highest priority. +* If there is no open cell, return -1. +* +* Things are a little different when there is an active cut, since +* the change order up to the cut point must be reproduced exactly +* in order to maintain integrity. +*/ + +int pick_cell( trs T ) +{ + int rc; /* Return code */ + int smallest; /* Cardinality of cell rc */ + int best; /* Priority of cell rc */ + int i; + + if ( T->cutptr && T->cutptr->active ) { + for ( i = 0; i < T->job->vlength && T->inorder[i] >= 0; i++ ); + if ( i < T->job->vlength && (rc = T->cutptr->inorder[i]) >= 0 ) { + if ( T->forkpoint < 0 && T->vector[rc].poss > 1 ) + T->forkpoint = rc; + return rc; + } + delete_cut( T ); + } + + smallest = SZ; + best = 0; + rc = -1; + while ( T->unfixt >= 0 && T->vector[T->unfixt].has_value ) T->unfixt--; + + for (i = T->unfixt; i >= 0; i-- ) + if ( !(T->vector[i].has_value) && T->vector[i].poss == 1 ) { + T->job->branches[1]++; + return i; + } + else if ( !(T->vector[i].has_value) && T->priority[i] >= best + && ( T->priority[i] > best || T->vector[i].poss < smallest )) { + rc = i; + smallest = T->vector[i].poss; + best = T->priority[i]; + } + + if (rc >= 0) { + T->job->branches[smallest]++; + if ( T->forkpoint < 0 && T->vector[rc].poss > 1 ) + T->forkpoint = rc; + } + return rc; +} + + + +/* +* Old cuts are trashed to stop them interfering with the search. +*/ + +void delete_cut( trs T ) +{ + cutt ftc; + + ftc = T->cutptr; + T->cutptr = ftc->lastone; + free(ftc); +} + + + +/* +* The next few functions have to do with allocation and de-allocation of +* refutations, refutation-list entries and integer-pairs. Some of this is +* a little intricate, but none of it is deep. +* +* First, to get a refutation list entry. +*/ + +reflist get_rl( trs T ) +{ + reflist r; + + if ( !T->rl_free->nextref ) more_rls( T ); + r = T->rl_free; + T->rl_free = T->rl_free->nextref; + return r; +} + + +/* +* Conversely, to put a dead one to rest. +*/ + +void put_rl( trs T, reflist dead ) +{ + if ( !dead ) return; + dead->rf = 0; + dead->nextref = T->rl_free; + T->rl_free = dead; +} + + +/* +* Removing a refutation is more than just putting the stake through its +* heart. It may occur in several lists. Here is the routine to delete +* the reference to it from a given list and to kill that rl. +*/ + +void remove_rl( trs T, ref r, reflist *source ) +{ + reflist rp, rs; + + if ( !*source ) return; + if ( (*source)->rf == r ) { + rp = (*source)->nextref; + put_rl( T, *source ); + *source = rp; + return; + } + + for ( rs = *source; rs->nextref; rs = rs->nextref ) + if ( rs->nextref->rf == r ) { + rp = rs->nextref->nextref; + put_rl( T, rs->nextref ); + rs->nextref = rp; + return; + } +} + + + +/* +* Now there is analogous stuff for integer-pair lists. When one of these +* is allocated, it has its initial values kindly filled in here. +*/ + +intlist get_il( trs T, boolean b, int x, int y, intlist nx ) +{ + intlist ip; + + if ( !T->il_free->p ) more_ils( T ); + ip = T->il_free; + T->il_free = T->il_free->p; + ip->positive = b; + ip->v = x; + ip->w = y; + ip->p = nx; + return ip; +} + + +/* +* This is how to put down a pair of integers. +*/ + +void put_il( trs T, intlist dead ) +{ + if ( !dead ) return; + dead->v = -1; + dead->p = T->il_free; + T->il_free = dead; +} + + + +/* +* Getting a ref involves filling it with default values. +*/ + +ref get_ref( trs T ) +{ + ref r; + + if ( !T->stak_ptr->nxtr ) more_refs( T ); + r = T->stak_ptr; + T->stak_ptr = T->stak_ptr->nxtr; + r->cardinality = 0; + r->notop = false; + r->cellvals = 0; + T->job->reps[MEMORY].i_report++; + T->local_memory++; + if ( batches_used(T) > T->max_batches ) + T->cut_flag = FOUND; + return r; +} + + + +/* +* And losing it again involves first getting rid of the cell-value pairs +* which constitute the refutation proper. For this, call recursive put_il. +*/ + +void put_ref( trs T, ref dead, boolean flag ) +{ + rec_put_il( T, dead->cellvals, dead, flag ); + dead->cardinality = -1; + dead->nxtr = T->stak_ptr; + T->stak_ptr = dead; + T->job->reps[MEMORY].i_report--; + T->local_memory--; +} + + +/* +* Here's the recursive put_il, which deletes a whole list of integer pairs. +*/ + +void rec_put_il( trs T, intlist ip, ref r, boolean flag ) +{ + if ( !ip ) return; + rec_put_il( T, ip->p, r, flag ); + if ( flag ) { + if ( ip->positive ) + remove_rl( T, r, &(T->vector[ip->v].cvals[ip->w].posrefs) ); + else + remove_rl( T, r, &(T->vector[ip->v].cvals[ip->w].cvrefs) ); + if ( T->vector[ip->v].cvals[ip->w].back == r ) + skipout("Ghost in the back field",SKIP); + } + put_il( T, ip ); +} + + + +/* +* In order to know when to cut-and-guess, we need to know how many +* batches of 1024 refutations have been used. So we count them! +*/ + +int batches_used( trs T ) +{ + int i; + + for ( i = 0; i <= T->job->batches; i++ ) + if ( !T->stak[i] ) return i; + skipout("Too many refutations requested",BUST); + /* That causes an exit, but just to keep the warnings quiet: */ + return i; +} + + + +/* +* The next three routines are for grabbing new chunks of memory to +* hold refutations, rls or ils. The relevant data structure is an +* array of pointers to such chunks. That is, T->stak[x] is a pointer +* to a structure which consists simply of an array of 1024 refs. +*/ + +void more_refs( trs T ) +{ + int i; + ref r; + + i = batches_used( T ); + T->stak[i] = (rbatch) malloc(sizeof(RBATCH)); + for ( r = T->stak[i]->news; r-T->stak[i]->news < 1024; r++ ) { + r->nxtr = (r == T->stak[i]->news+1023? 0: r+1); + r->cardinality = -1; + } + if ( i ) T->stak_ptr->nxtr = T->stak[i]->news; + else T->stak_ptr = T->stak[i]->news; + return; +} + + + +/* +* Getting more rls is pretty much like getting more refs. +*/ + +void more_rls( trs T ) +{ + int i; + reflist rp; + + for ( i = 0; i <= T->stakmax+T->job->batches; i++ ) + if ( !T->rl_stak[i] ) { + T->rl_stak[i] = (rlbatch) malloc(sizeof(RLBATCH)); + for ( rp = T->rl_stak[i]->news; rp-T->rl_stak[i]->news < 1024; rp++ ) + rp->nextref = (rp == T->rl_stak[i]->news+1023? 0: rp+1); + if ( i ) T->rl_free->nextref = T->rl_stak[i]->news; + else T->rl_free = T->rl_stak[i]->news; + return; + } +} + + + +/* +* And getting more ils is similar. +*/ + +void more_ils( trs T ) +{ + int i; + intlist ip; + + for ( i = 0; i <= T->stakmax; i++ ) + if ( !T->il_stak[i] ) { + T->il_stak[i] = (ilbatch) malloc(sizeof(ILBATCH)); + for ( ip = T->il_stak[i]->news; ip-T->il_stak[i]->news < 1024; ip++ ) + ip->p = (ip == T->il_stak[i]->news+1023? 0: ip+1); + if ( i ) T->il_free->p = T->il_stak[i]->news; + else T->il_free = T->il_stak[i]->news; + return; + } +} + + + +/****************************************************************** +* +* Now back to the plot. The next few functions are where a healthy +* run of any vntr program spends most of its time. +* +* First we have the routine to insert a new value v in a cell c. +*/ + +boolean invalue( trs T, cell c, int v ) +{ + reflist r; + intlist ip; + int i; + + if ( T->job->done ) return false; + if ( T->job->maxbak && + T->job->reps[NULL_BACKTRACKS].i_report + + T->job->reps[SURJ_BACKTRACKS].i_report + + T->job->reps[SOLUTIONS].i_report + + T->job->reps[BAD_TESTS].i_report >= T->job->maxbak ) { + job_done( T->job, "limit on backtracks" ); + return false; + } + c->has_value = true; + T->comvec[c->vpos] = v; + T->inorder[++T->this_cell] = c->vpos; + T->coinorder[c->vpos] = T->this_cell; + + for ( r = c->cvals[v].cvrefs; r; r = r->nextref ) + transfer_ref( T, c, r->rf ); + + for ( i = 0; i <= T->maxval; i++ ) if ( i != v ) + for ( r = c->cvals[i].posrefs; r; r = r->nextref ) + transfer_ref( T, c, r->rf ); + + for ( r = c->inject; r; r = r->nextref ) + if ( v < r->rf->cardinality ) { + FORR( r->rf, ip ) + if ( !(T->vector[ip->v].has_value) ) + blockval( T, T->vector+ip->v, v, r->rf, NULL_BACKTRACKS ); + } + + return surjok( T ); +} + + +/* +* This is just a subroutine of invalue. It decrements the * +* cardinality of a refutation. If the result is cardinality 1 * then +* either there is a value inserted in some cell other than * the +* value involved at that cell, in which case no action need * be +* taken, or the refutation now blocks the insertion of a * value. In +* the latter case, the value is marked as blocked and * if this newly +* removes it the `poss' count for its cell is * decremented. If this +* empties the possval set, we are ready to * backtrack. */ + +void transfer_ref( trs T, cell c, ref rf ) +{ + intlist ip, iq; + int j; + + if ( --(rf->cardinality) == 1 ) { + iq = 0; + FORR( rf, ip ) { + if ( !(T->vector[ip->v].has_value) ) { + if ( ip->positive ) { + for ( j = 0; j <= T->maxval; j++ ) + if ( j != ip->w ) + blockval( T, T->vector+ip->v, j, rf, + NULL_BACKTRACKS ); + } + else blockval( T, T->vector+ip->v, ip->w, rf, + NULL_BACKTRACKS ); + if ( iq ) { + iq->p = ip->p; + ip->p = rf->cellvals; + rf->cellvals = ip; + } + return; + } + else if (( ip->positive && T->comvec[ip->v] == ip->w ) + || ( !ip->positive && T->comvec[ip->v] != ip->w )) { + if ( iq ) { + iq->p = ip->p; + ip->p = rf->cellvals; + rf->cellvals = ip; + } + return; + } + iq = ip; + } + } +} + + + +/* +* The subroutine to block a value in the light of a transferred +* refutation could be left in-line, but since it is called from three +* places, it is better isolated like this. +*/ + +void blockval( trs T, cell c, int v, ref rrf, report_code RC ) +{ + c->cvals[v].blocks++; + if ( c->cvals[v].blocks == 1 ) { + c->poss--; + if ( !(c->poss) && !T->ts ) { + T->ts = true; + T->susguy = c->vpos; + if ( RC ) T->job->reps[RC].i_report++; + } + c->cvals[v].back = rrf; + } +} + + + +/* +* Injective and surjective fragments of the vector are given special +* treatment. This routine checks the surjections to ensure that every +* value is still possible for at least one of the cells. The trick of +* remembering the cell where the value was last seen and looking there +* first keeps time spent on this to something rather small, and where +* surjectiveness is important, surjok pays its way handsomely. +* +*/ + +boolean surjok( trs T ) +{ + int i, j, ls; + intlist ip; + static boolean onoff; + + if ( T->ts ) return true; + if ( onoff ) { onoff = false; return true; } + onoff = true; + for ( i = 0; T->surjection[i].valset; i++ ) + for ( j = 0; j <= T->maxval; j++ ) + if ( T->surjection[i].valset & (1 << j) ) { + ls = T->surjection[i].last_seen[j]; + if ( T->vector[ls].cvals[j].blocks || + ( T->vector[ls].has_value && T->comvec[ls] != j )) { + FOR( T->surjection[i].cellset, ip ) + if ( !(T->vector[ip->v].cvals[j].blocks) && + (!T->vector[ip->v].has_value || T->comvec[ip->v] == j)) { + T->surjection[i].last_seen[j] = ip->v; + /* Try this */ + ip->v = T->surjection[i].cellset->v; + T->surjection[i].cellset->v = T->surjection[i].last_seen[j]; + /* for efficiency */ + break; + } + if ( !ip ) { + T->job->reps[SURJ_BACKTRACKS].i_report++; + return false; + } + } + } + return true; +} + + + +/* +* The converse of putting a value in is taking one out. Why do I have to +* point that out to you? Note that these functions maintain T->inorder[x] +* (the x-th cell to get a value) and T->coinorder[x] (the y such that x +* is T->inorder[y]). These arrays are set to -1 where undefined. +*/ + +void outvalue( trs T, cell c ) +{ + intlist ip; + reflist r; + int i; + + for ( r = c->cvals[T->comvec[c->vpos]].cvrefs; r; r = r->nextref ) + untransfer_ref( T, c, r->rf ); + + for ( i = 0; i <= T->maxval; i++ ) if ( i != T->comvec[c->vpos] ) + for ( r = c->cvals[i].posrefs; r; r = r->nextref ) + untransfer_ref( T, c, r->rf ); + + for ( r = c->inject; r; r = r->nextref ) + if ( T->comvec[c->vpos] < r->rf->cardinality ) { + FORR( r->rf, ip ) + if ( !(T->vector[ip->v].has_value) ) + unblockval( T, T->vector+ip->v, T->comvec[c->vpos], r->rf ); + } + + if ( c->vpos == T->forkpoint ) { + c->cvals[T->comvec[c->vpos]].blocks++; + if ( c->cvals[T->comvec[c->vpos]].blocks == 1 ) + if ( --c->poss == 1 ) T->forkpoint = -1; + } + c->has_value = false; + T->comvec[c->vpos] = 0; + T->inorder[T->this_cell--] = -1; + T->coinorder[c->vpos] = -1; + if ( c->vpos > T->unfixt ) T->unfixt = c->vpos; + if ( T->duds && T->cut_flag != RECORDED ) delete_duds( T ); +} + + +/* +* The converse of transfer_ref. This unblocks any blocked cell after +* incrementing the cardinality of the refutation. +*/ + +void untransfer_ref(trs T, cell c, ref rf) +{ + int i; + intlist ip; + + rf->cardinality++; + if ( rf->cardinality == 2 && !T->vector[rf->cellvals->v].has_value ) { + ip = rf->cellvals; + if ( ip->positive ) { + for ( i = 0; i <= T->maxval; i++ ) if ( i != ip->w ) + unblockval( T, T->vector+ip->v, i, rf ); + } + else unblockval( T, T->vector+ip->v, ip->w, rf ); + } + + if ( rf->notop ) { + FORR( rf, ip ) + if ( ip->v != c->vpos && T->vector[ip->v].has_value ) + break; + if ( !ip ) { + mark_delete( T, rf ); + T->job->reps[TOPLESS].i_report++; + } + } +} + + + +/* +* Unblockval is the converse of blockval: re-transfer a refutation by +* deleting the block and adjusting the poss and back fields as necessary. +*/ + +void unblockval( trs T, cell c, int v, ref rrf ) +{ + if ( !(--c->cvals[v].blocks) ) c->poss++; + if ( c->cvals[v].back == rrf ) c->cvals[v].back = 0; +} + + + + +/* +* Refutations are deleted when they are no longer meaningful. Those +* to be deleted are marked for death while other things are going on +* and later executed all together. +*/ + +void mark_delete( trs T, ref r ) +{ + reflist rr; + + rr = get_rl( T ); + rr->rf = r; + rr->nextref = T->duds; + T->duds = rr; +} + + +/* +* This empties the list of duds created by calls to mark_delete. +*/ + +void delete_duds( trs T ) +{ + delete_a_dud( T, T->duds ); + T->duds = 0; +} + + +/* +* And this is the recursive work routine for delete_duds. +*/ + +void delete_a_dud( trs T, reflist r ) +{ + if ( !r ) return; + delete_a_dud( T, r->nextref ); + put_ref ( T, r->rf, true ); + put_rl( T, r ); +} + + + + + +/* +* When a new refutation has been produced, it is sent to setref to +* have any unnecessary parts removed and generally to be smartened up +* for its entry into the database. First any cells which have +* absolutely only one * possible value are not really involved in the +* refutation and can be * removed. Secondly, if the refutation +* involves the two or more most * significant cells (not including +* the absolutely fixed ones) then all * but the least significant of +* these can be deleted, since when the search * backtracks far enough +* to change that cell, this refutation can never * again be +* active. In such a case it is marked as topless so that outvalue * +* knows to delete it when its useful life is over. Finally, any +* refutation * which is unreasonably large occupies a lot of the +* database and yields * little information, so we can pretend it is a +* small refutation until such * time as this pretence is no longer +* safe, then throw it away. We do this * by treating it as though it +* involved all of the cells more significant * than a chosen one and +* applying the "tops off" strategy above. +*/ + +void setref( trs T ) +{ + int size_before, size_after, i, j; + int latest, del; + + if ( !T->newref ) { + T->novect = true; + return; + } + size_before = size_after = T->newref->cardinality; + for ( latest = 0; latest < T->job->vlength; latest++ ) + if ( T->inorder[latest] < 0 || + T->vector[T->inorder[latest]].poss > 1 ) break; + else if ( T->vector[T->inorder[latest]].used ) { + T->vector[T->inorder[latest]].used = false; + size_after--; + } + if ( size_after < size_before ) reduceref( T ); + if ( !T->newref->cardinality ) { + T->novect = true; + return; + } + size_before = size_after; + + for ( del = -1; + T->job->topsoff && + latest < T->job->vlength && + T->inorder[latest] >= 0 && + T->vector[T->inorder[latest]].used; + latest++ ) { + if ( del >= 0 ) { + T->vector[T->inorder[del]].used = false; + size_after--; + } + del = latest; + } + if ( size_after < size_before ) { + reduceref( T ); + T->newref->notop = true; + } + + i = size_before; j = size_after; + if ( i >= RRA ) i = RRA-1; + if ( j >= RRB ) j = RRB-1; + T->job->ref_record[i][j]++; + size_before = size_after = T->newref->cardinality; + + while ( size_after > T->job->maxref ) { + if ( T->inorder[latest] >= 0 && T->vector[T->inorder[latest]].used ) { + if ( del >= 0 ) { + T->vector[T->inorder[del]].used = false; + size_after--; + } + del = latest; + } + latest++; + } + if ( size_after < size_before ) { + reduceref ( T ); + T->newref->notop = true; + } +} + + + +/* +* This is the routine to take unused cell-value pairs out of a refutation. +* It comes in two stages: one to take care of an unused starter pair and +* one for the other cases. +*/ + +void reduceref( trs T ) +{ + intlist ip, iq; + + if ( !T->newref->cellvals ) return; + while ( !T->vector[T->newref->cellvals->v].used ) { + ip = T->newref->cellvals->p; + put_il( T, T->newref->cellvals ); + T->newref->cellvals = ip; + T->newref->cardinality--; + if ( !ip ) { + T->novect = true; + return; + } + } + + FORR( T->newref, ip ) + while ( ip->p && !(T->vector[ip->p->v].used) ) { + iq = ip->p->p; + put_il( T, ip->p ); + ip->p = iq; + T->newref->cardinality--; + } +} + + + +/* +* Adding a refutation to the database is reasonably straightforward. +* The call to this comes when backtracking has reached the point where +* the least significant cell involved has just lost its value. The other +* cells involved still have their values. +*/ + +void addref( int x, trs T ) +{ + intlist ip; + reflist rp; + int i; + int ipv, ipw; + boolean ipb; + + if ( !T->newref ) skipout("Null refutation",NULL_REF); + if ( T->newref->cardinality == 1 && T->newref->notop ) { + T->vector[x].used = false; + put_ref( T, T->newref, true ); + T->newref = 0; + return; + } + if ( !(T->newref->cardinality) ) { + T->novect = true; + return; + } + T->job->reps[REFSIZE].i_report++; + + FORR( T->newref, ip ) + if ( !ip->positive && T->vector[ip->v].cvals[ip->w].blocks ) { + FORR( T->newref, ip ) + T->vector[ip->v].used = false; + put_ref( T, T->newref, true ); + T->newref = 0; + return; + } + + FORR( T->newref, ip ) { + T->vector[ip->v].used = false; + rp = get_rl( T ); + rp->rf = T->newref; + if ( ip->positive ) { + rp->nextref = T->vector[ip->v].cvals[ip->w].posrefs; + T->vector[ip->v].cvals[ip->w].posrefs = rp; + } + else { + rp->nextref = T->vector[ip->v].cvals[ip->w].cvrefs; + T->vector[ip->v].cvals[ip->w].cvrefs = rp; + } + if ( ip->v == x ) { + if ( ip->positive ) { + for ( i = 0; i <= T->maxval; i++ ) if ( i != ip->w ) + blockval( T, T->vector+x, i, rp->rf, 0 ); + } + else blockval( T, T->vector+x, ip->w, rp->rf, 0 ); + ipv = ip->v; ipw = ip->w; ipb = ip->positive; + ip->v = T->newref->cellvals->v; + ip->w = T->newref->cellvals->w; + ip->positive = T->newref->cellvals->positive; + T->newref->cellvals->v = ipv; + T->newref->cellvals->w = ipw; + T->newref->cellvals->positive = ipb; + } + else T->newref->cardinality--; + } + if ( T->newref->cardinality != 1 ) + skipout("Refutation cardinality not correctly set",SKIP); + for ( i = 0; i < T->job->vlength; i++ ) + if ( T->vector[i].used ) skipout("Used not reset",SKIP); + T->newref = 0; +} + + + +void print_ref( ref r ) +{ + intlist ip; + + if ( !r ) return; + FORR(r,ip) printf(" %d:%d%c ",ip->v, ip->w, ip->positive? '*': ' '); + if ( r->notop ) printf(" +"); +} + + +void print_sus( trs T, cell c ) +{ + intlist ip; + int i; + + puts(""); + for ( i = 0; i < T->maxval; i++ ) { + printf(" %d:%d",c->vpos,i); + if ( c->cvals[i].back ) + FORR(c->cvals[i].back,ip) if ( ip->v == c->vpos ) + printf("%c ",ip->positive? '*': ' '); + if ( c->cvals[i].back ) + FORR(c->cvals[i].back,ip) if ( ip->v != c->vpos ) + printf("%d:%d%c ", ip->v, ip->w, ip->positive? '*': ' '); + puts(""); + } +} + + + +void totsus( trs T ) +{ + int i, addval; + cell c; + intlist ip, jp, kp=0; + boolean pos_used[V_LENGTH]; + + if ( T->job->Nosecs ) { + if ( T->vector[T->susguy].poss ) T->ts = false; + return; + } + T->ts = false; + c = T->vector + T->susguy; + T->susguy = 0; + T->newref = get_ref( T ); + for (i=0; imaxval; i++ ) if ( c->cvals[i].back ) { + if ( c->cvals[i].back->notop ) T->newref->notop = true; + } + + for ( i = 0; i <= T->maxval; i++ ) if ( c->cvals[i].back ) + FORR( c->cvals[i].back, ip ) + if ( ip->v != c->vpos ) { + if ( ip->w < 0 ) { /* The case of an injection */ + if ( T->vector[ip->v].has_value + && T->comvec[ip->v] == i ) + addval = i; + else addval = -1; + } + else addval = ip->w; + if ( ip->positive ) pos_used[ip->v] = true; + if ( (pos_used[ip->v] || !T->vector[ip->v].used) + && addval >= 0 ) { + jp = get_il( T, ip->positive, ip->v, addval, 0 ); + if ( !T->newref->cellvals ) + T->newref->cellvals = jp; + else kp->p = jp; + kp = jp; + T->vector[jp->v].used = true; + T->newref->cardinality++; + } + } + make_coherent( T ); + + if ( T->newref->notop ) { + ip = T->newref->cellvals; + FOR( ip->p, jp ) + if ( T->coinorder[jp->v] < T->coinorder[ip->v] ) + T->vector[jp->v].used = false; + else { + T->vector[ip->v].used = false; + ip = jp; + } + reduceref( T ); + } + else setref( T ); +} + + + + + + +/* +* At present, the program is not set up to deal with two positive +* cell-value pairs in a refutation which both pertain to the same +* cell. That is, we cannot represent a disjunction saying that cell +* c has "either value v1 or value v2". The following subroutine of +* totsus checks for such difficulties and if they are present +* substitutes for the offending set of pairs the negative one that +* the cell should not have its current value. +*/ + +void make_coherent( trs T ) +{ + intlist ip, iq, ir; + + FORR(T->newref,ip) if ( ip->positive ) { + while ( T->newref->cellvals != ip + && T->newref->cellvals->v == ip->v ) { + ir = T->newref->cellvals->p; + put_il( T, T->newref->cellvals ); + T->newref->cellvals = ir; + T->newref->cardinality--; + ip->w = -1; + } + for ( iq = T->newref->cellvals; iq != ip && iq->p != ip; iq = iq->p ) + while ( iq->p != ip && iq->p->v == ip->v ) { + ir = iq->p; + iq->p = ir->p; + put_il( T, ir ); + T->newref->cardinality--; + ip->w = -1; + } + for ( iq = ip; iq && iq->p; iq = iq->p ) + while ( iq->p && iq->p->v == ip->v ) { + if ( iq->p->w != ip->w ) ip->w = -1; + ir = iq->p; + iq->p = ir->p; + put_il( T, ir ); + T->newref->cardinality--; + } + if ( ip->w < 0 ) { + ip->positive = false; + ip->w = T->comvec[ip->v]; + } + } +} + + + + + +void new_small_ref( int cells[], int vals[], boolean valency[], trs T ) +{ + ref the_ref; + reflist rp, rrp, *tvc; + int i, j, k, m; + int refcard, maxrefcard; + boolean b; + + /* Cludge at this stage: don't allow refutations with more than + one positive member. This restriction can be removed once the + basic program is running OK. */ + + for ( i = 0; cells[i] >= 0; i++ ) if ( valency[i] ) + for ( j = i+1; cells[j] >= 0; j++ ) if ( valency[j] ) + if ( cells[i] != cells[j] || vals[i] != vals[j] ) + skipout("Refutation clause is non-Horn",SKIP); + + /* Check for subsumption by a one-refutation + and remove any fixed cell-value pairs. Then + skip out of the search if the refutation is null */ + + for ( maxrefcard = 0; cells[maxrefcard] >= 0; maxrefcard++ ); + for ( i = 0; cells[i] >= 0; i++ ) { + if ( valency[i] ) { + if ( T->vector[cells[i]].cvals[vals[i]].blocks ) { + for ( j = i; cells[j] >= 0; j++ ) { + cells[j] = cells[j+1]; + vals[j] = vals[j+1]; + valency[j] = valency[j+1]; + } + i--; + } + else if ( T->vector[cells[i]].poss == 1 ) return; + } + else { + if ( T->vector[cells[i]].cvals[vals[i]].blocks ) return; + if ( T->vector[cells[i]].poss == 1 ) { + for ( j = i; cells[j] >= 0; j++ ) { + cells[j] = cells[j+1]; + vals[j] = vals[j+1]; + valency[j] = valency[j+1]; + } + i--; + } + } + } + if ( !(refcard = i) ) { + T->novect = true; + return; + } + + /* Sort the refutation, removing repetitions. Remember that + refcard is likely to be about 2 or 3 (4 at most) so + the crudest sorting method is as good as anything! */ + + b = true; while (b) { + b = false; + for ( i = 0; cells[i] >= 0 && cells[i+1] >= 0; i++ ) + if ( cells[i] > cells[i+1] ) { + b = true; + j = cells[i]; k = vals[i]; m = valency[i]; + cells[i] = cells[i+1]; vals[i] = vals[i+1]; + valency[i] = valency[i+1]; + cells[i+1] = j; vals[i+1] = k; valency[i+1] = m; + } + else if ( cells[i] == cells[i+1] ) { + if ( vals[i] == vals[i+1] ) { + if ( valency[i] != valency[i+1] ) return; + for ( j = i; cells[j] >= 0; j++ ) { + cells[j] = cells[j+1]; + vals[j] = vals[j+1]; + valency[j] = valency[j+1]; + } + i--; + refcard--; + b = true; + } + else { + if ( !valency[i] && !valency[i+1] ) return; + for ( j = (valency[i]? i: i+1); cells[j] >= 0; j++ ) { + cells[j] = cells[j+1]; + vals[j] = vals[j+1]; + valency[j] = valency[j+1]; + } + i--; + refcard--; + b = true; + } + } + } + + if ( refcard == 1 ) { + if ( valency[0] ) { + for ( i = 0; i <= T->maxval; i++ ) if ( i != vals[0] ) + if ( !T->vector[cells[0]].cvals[i].blocks ) + remove_value( T, T->vector+cells[0], i ); + } + else remove_value( T, T->vector+cells[0], vals[0] ); + } + else if ( !T->cut_flag ) { + the_ref = get_ref( T ); + the_ref->cardinality = refcard; + the_ref->cellvals = get_all_ils( cells, vals, valency, 0, T ); + if ( subsumed(the_ref, T) ) { + put_ref( T, the_ref, false ); + T->job->reps[F_SUBSUMED].i_report++; + return; + } + T->job->reps[PREREFS].i_report++; + for ( i = 0; cells[i] >= 0; i++ ) { + rp = get_rl( T ); + rp->rf = the_ref; + if ( valency[i] ) + tvc = &(T->vector[cells[i]].cvals[vals[i]].posrefs); + else tvc = &(T->vector[cells[i]].cvals[vals[i]].cvrefs); + if ( !*tvc || ((*tvc)->rf->cellvals->v != cells[i]) + || the_ref->cellvals->v == cells[i] ) { + rp->nextref = *tvc; + *tvc = rp; + } + else { + for (rrp=*tvc; rrp->nextref; rrp=rrp->nextref) + if ( rrp->nextref->rf->cellvals->v != cells[i] ) + break; + rp->nextref = rrp->nextref; + rrp->nextref = rp; + } + } + if ( refcard < maxrefcard ) back_sub( T, the_ref ); + } +} + + + + +void remove_value( trs T, cell c, int x ) +{ + reflist rp; + intlist ip, iq; + int i; + + c->cvals[x].blocks++; + if ( c->cvals[x].blocks == 1 ) { + if ( !--c->poss ) T->novect = true; + } + else skipout("Nonexistent value removed by 1-refutation",SKIP); + for ( rp = c->cvals[x].cvrefs; rp; rp = rp->nextref ) { + FORR( rp->rf, ip ) if ( ip->v == c->vpos ) break; + if ( !ip ) skipout("Error in refutation",SKIP); + if ( ip->positive ) { + if ( ip == rp->rf->cellvals ) rp->rf->cellvals = ip->p; + else { + FORR( rp->rf, iq ) + if ( iq->p == ip ) break; + iq->p = ip->p; + } + put_il( T, ip ); + remove_rl( T, rp->rf, &(c->cvals[x].cvrefs) ); + rp->rf->cardinality--; + if ( rp->rf->cardinality == 1 ) { + if ( rp->rf->cellvals->positive ) { + for ( i = 0; i <= T->maxval; i++ ) + if ( i != rp->rf->cellvals->w ) + if ( !T->vector[rp->rf->cellvals->v].cvals[i].blocks ) + remove_value( T, T->vector+rp->rf->cellvals->v, i ); + } + else remove_value(T,T->vector+rp->rf->cellvals->v,rp->rf->cellvals->w); + } + else back_sub( T, rp->rf ); + } + else { + mark_delete( T, rp->rf ); + T->job->reps[B_SUBSUMED].i_report++; + T->job->reps[B1_SUBSUMED].i_report++; + T->job->reps[PREREFS].i_report--; + } + } + delete_duds(T); + T->comvec[c->vpos] &= ~(1 << x); +} + + + + +intlist get_all_ils( int cells[], int vals[], boolean valency[], int x, trs T ) +{ + if ( cells[x] < 0 ) return 0; + return get_il( T, valency[x], cells[x], vals[x], + get_all_ils(cells, vals, valency, x+1, T) ); +} + + + + +boolean subsumed( ref the_ref, trs T ) +{ + reflist rp; + intlist ip; + + FORR( the_ref, ip ) if ( ip->p ) + for ( rp = T->vector[ip->v].cvals[ip->w].cvrefs; rp; rp = rp->nextref ) { + if ( rp->rf->cellvals->v != ip->v) break; + if ( (rp->rf->cardinality < the_ref->cardinality || + (ip == the_ref->cellvals && + rp->rf->cardinality == the_ref->cardinality)) + && subsumes(rp->rf, the_ref) ) return true; + } + return false; +} + + +boolean subsumes( ref little_ref, ref big_ref ) +{ + intlist il, ib; + + ib = big_ref->cellvals; + FORR( little_ref, il ) { + while ( ib && (il->v != ib->v) ) ib = ib->p; + if ( !ib ) return false; + if ( ib->positive && !il->positive ) return false; + if ( !ib->positive && il->positive ) { + if ( il->w == ib->w ) return false; + } + else if ( il->w != ib->w ) return false; + } + return true; +} + + +void back_sub( trs T, ref the_ref ) +{ + reflist rp; + intlist ip; + ip = the_ref->cellvals; + for ( rp = T->vector[ip->v].cvals[ip->w].cvrefs; rp; rp = rp->nextref ) + if ( rp->rf->cardinality > the_ref->cardinality ) + if ( subsumes(the_ref, rp->rf) ) { + mark_delete( T, rp->rf ); + T->job->reps[B_SUBSUMED].i_report++; + T->job->reps[PREREFS].i_report--; + } + delete_duds( T ); +} + + + + +void Ref( int x, trs T ) +{ + intlist it; + + if ( T->vector[x].used ) return; + if ( !(T->newref) ) T->newref = get_ref( T ); + it = get_il( T, false, x, T->comvec[x], T->newref->cellvals ); + T->newref->cellvals = it; + T->newref->cardinality++; + T->vector[x].used = true; +} + + + +void no_ref( trs T ) +{ + T->noref = true; +} + + + +/* +* This is done when a forkpoint exists and has a value, as do zero or +* more less significant cells which next have to be outvalued. +* +* If there is currently an active cut it must be deleted, as this one +* subsumes it. Failure to do this may produce looping. +*/ + +void record_cut( trs T ) +{ + if ( T->cutptr && T->cutptr->active ) delete_cut( T ); + record_part_cut( T->comvec[T->forkpoint], T ); + T->cut_flag = RECORDED; + T->ts = false; + T->novect = true; +} + + + +/* +* Record the cuts for values from x up. +*/ + +void record_part_cut( int x, trs T ) +{ + int i; + cutt newcut; + + if ( x > T->maxval ) return; + record_part_cut( x+1, T ); + if ( T->vector[T->forkpoint].cvals[x].blocks ) return; + newcut = (cutt) malloc(sizeof(CUTT)); + newcut->lastone = T->cutptr; + T->cutptr = newcut; + + if ( x == T->comvec[T->forkpoint] ) + for ( i = 0; i <= T->job->vlength; i++ ) { + T->cutptr->comvec[i] = T->comvec[i]; + T->cutptr->inorder[i] = T->inorder[i]; + } + else { + for ( i = 0; T->inorder[i] != T->forkpoint; i++ ) { + T->cutptr->comvec[i] = T->comvec[i]; + T->cutptr->inorder[i] = T->inorder[i]; + } + do { + T->cutptr->comvec[i] = 0; + T->cutptr->inorder[i] = -1; + } + while ( ++i <= T->job->vlength ); + } + T->cutptr->forkpoint = T->forkpoint; + T->cutptr->active = false; + T->cutptr->cutval = x; +} + + + + +/* +* This is the action when the last cell in inorder to have a value +* is the forkpoint, and there is a cut being recorded. +*/ + +void complete_cut( int ex, trs T ) +{ + int i, j; + cutt cp; + + for ( cp = T->cutptr; cp && cp->forkpoint == ex; cp = cp->lastone ) { + for ( i = 0; i < T->job->vlength; i++ ) { + cp->possvals[i] = 0; + for ( j = 0; j <= T->maxval; j++ ) + if ( !T->vector[i].cvals[j].blocks ) + cp->possvals[i] |= (1 << j); + } + cp->possvals[ex] = (1 << cp->cutval); + } + T->cut_flag = NOCUT; +} + + + +boolean prepared( trs T ) +{ + int i, j; + logical u = 0; + cell c; + + T->novect = false; + if ( T->cutptr ) { + for ( i = 0; i < V_LENGTH; i++ ) + T->comvec[i] = T->cutptr->possvals[i]; + } + else { + for ( i = 0; i < SZ; i++ ) u |= (1 << i); + for ( i = 0; i < V_LENGTH; i++ ) + T->comvec[i] = (i < T->job->vlength)? u: 0; + } + + if ( T->job->Onerefs ) { + (*(T->job->Onerefs))(T->comvec,T); + T->job->reps[SETPOSSES].i_report++; + } + T->maxval = 0; + for ( i = 0; i < T->job->vlength; i++ ) { + if ( !T->comvec[i] ) { + T->novect = true; + return false; + } + for ( j = T->maxval+1; j < SZ; j++ ) + if ( T->comvec[i] & (1 << j) ) + T->maxval = j; + } + + for ( i = 0; i < T->job->vlength; i++ ) { + c = T->vector + i; + c->vpos = i; + for ( j = 0; j <= SZ; j++ ) { + c->cvals[j].vpos = i; + c->cvals[j].cvrefs = 0; + } + c->inject = 0; + } + for ( i = 0; i <= T->job->vlength; i++ ) { + T->inorder[i] = -1; + T->coinorder[i] = -1; + } + for ( i = 0; i < T->job->batches; i++ ) T->stak[i] = 0; + for ( i = 0; i < T->stakmax; i++ ) T->il_stak[i] = 0; + for ( i = 0; i < T->stakmax+T->job->batches; i++ ) T->rl_stak[i] = 0; + T->stak_ptr = 0; + T->il_free = 0; + T->rl_free = 0; + more_refs( T ); + more_ils( T ); + more_rls( T ); + for ( i = 0; i < SURJ_MAX; i++ ) { + T->surjection[i].valset = 0; + T->surjection[i].cellset = 0; + for ( j = 0; j < SZ; j++ ) + T->surjection[i].last_seen[j] = 0; + } + + setinx( T ); + + T->firstchange = -1; + T->forkpoint = -1; + T->this_cell = -1; + T->unfixt = T->job->vlength-1; + T->ts = false; + T->susguy = 0; + T->newref = 0; + T->duds = 0; + return true; +} + + + +void setinx( trs T ) +{ + int i, j; + cell c; + + for ( i = 0; i < T->job->vlength; i++ ) { + c = T->vector+i; + c->poss = 0; + for ( j = 0; j < SZ; j++ ) { + if ( T->comvec[i] & (1 << j) ) { + c->poss++; + c->cvals[j].blocks = 0; + } + else c->cvals[j].blocks = 1; + c->cvals[j].cvrefs = 0; + c->cvals[j].posrefs = 0; + c->cvals[j].back = 0; + } + c->used = false; + c->has_value = false; + } +} + + + + + +boolean pre_test( trs T ) +{ + int i, j; + + if ( !T->job->Smallrefs ) return true; + T->job->reps[DOSMALLS].i_report++; + for ( i = 0; i < T->job->vlength; i++ ) { + T->comvec[i] = 0; + for ( j = 0; j <= T->maxval; j++ ) + if ( !T->vector[i].cvals[j].blocks ) + T->comvec[i] |= (1 << j); + } + if ( !T->job->Smallrefs(T->comvec, T) ) return false; + if ( T->novect ) return false; + + T->maxval = 0; + for ( i = 0; i < T->job->vlength; i++ ) { + if ( !(T->vector[i].poss) ) { + T->novect = true; + return false; + } + for ( j = T->maxval+1; j < SZ; j++ ) + if ( T->comvec[i] & (1 << j) ) T->maxval = j; + T->comvec[i] = 0; + } + T->max_batches = batches_used(T) + T->job->extra_batches; + if ( T->max_batches >= T->job->batches ) + T->max_batches = T->job->batches - 1; + return true; +} + + + + +boolean absfixt( int x ) +{ + /* Only a stub at present */ + return false; +} + + + + + +void surjective( logical vec[], logical vset, trs T ) +{ + int i, j; + + for ( i = 0; T->surjection[i].valset; i++ ) + if ( i == SURJ_MAX ) skipout("Too many surjections",SURJES); + + T->surjection[i].valset = vset; + for ( j = 0; j < T->job->vlength; j++ ) if ( vec[j] ) + T->surjection[i].cellset = get_il(T,false,j,-1,T->surjection[i].cellset); + for ( j = 0; j < SZ; j++ ) + T->surjection[i].last_seen[j] = T->surjection[i].cellset->v; +} + + + + +void injective( logical vec[], int x, trs T ) +{ + int i; + ref r; + reflist rp; + + r = get_ref( T ); + r->cardinality = x; + r->notop = false; + + for ( i = 0; i < T->job->vlength; i++ ) if ( vec[i] ) { + r->cellvals = get_il( T, false, i, -1, r->cellvals ); + rp = get_rl( T ); + rp->rf = r; + rp->nextref = T->vector[i].inject; + T->vector[i].inject = rp; + } +} + + + +void co_priority( int x, int y, trs T ) +{ + T->priority[x] = y; +} + + + + +void job_done( trin t, char *s ) +{ + if ( t->report_breaks ) + fprintf(stderr,"\nStopped by %s\n", s); + t->done = true; +} + + + + +int timestamp() +{ + struct tms time_buffer; + + times(&time_buffer); + return time_buffer.tms_utime; +} + + +void dump_cuts(trs T) +{ + cutt ct; + + printf("\nSearching."); + if ( T->cutptr ) { + printf(" Cut stack = "); + for ( ct = T->cutptr; ct; ct = ct->lastone ) + printf(" %d.%d ", ct->forkpoint, ct->cutval ); + } + puts(""); + fflush(stdout); +} + + + +void skipout( char s[], int exitcode ) +{ + fprintf(stderr,"\n\n\n Aborting on detection of an error"); + fprintf(stderr,(*s? ":": ".")); + fprintf(stderr,"\n\n %s.\n\n", s); + exit( exitcode ); +} + + +void test_backs(trs T) +{ + int i, j; + cellvalue cv; + + for ( i = 0; i < T->job->vlength; i++ ) + for ( j = 0; j <= T->maxval; j++ ) { + cv = &(T->vector[i].cvals[j]); + /* + * if ( cv->blocks && !cv->back ) + * skipout("Block without back",SKIP); + */ + if ( cv->back && !cv->blocks ) + skipout("Back without block",SKIP); + } +} diff --git a/src/vntr.h b/src/vntr.h new file mode 100644 index 0000000..976a6cb --- /dev/null +++ b/src/vntr.h @@ -0,0 +1,343 @@ + + /**************************************************************** + * * + * FINDER 3.0 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +#include +#include + +#ifndef __APPLE__ +#include +#endif + +#include + + + +/* +* Constant definitions +*/ + +#define REF_MAX 512 +#define REF_DEFAULT 16 +#define EXTRA_DEFAULT 5 +#define MAX_REF_SIZE 8 + +#ifndef SZ +#define SZ 32 +#endif +#define V_LENGTH 1024 +/* +* #define SURJ_MAX 64 +*/ +#define SURJ_MAX 256 +#define STAK_MAX (REF_MAX*MAX_REF_SIZE) +#define RRA 50 +#define RRB 30 + +#define NO_REPORT ((unsigned)1) +#define INT_REPORT ((unsigned)2) +#define FLOAT_REPORT ((unsigned)4) +#define DO_REPORT ((unsigned)8) + +#define logical unsigned int + +#define FOR(x,y) for( y = x; y; y = y->p ) +#define FORR(x,y) FOR( x->cellvals, y ) + +/* +* Type definitions +*/ + +/* +*typedef enum { +* false, +* true +*} boolean; +*/ + +#define boolean char +#define false ((boolean)0) +#define true ((boolean)1) + +typedef enum { + NOCUT, + FOUND, + RECORDED +} cutcode; + +typedef enum { + PERFECT, + NULL_SPACE, + BIG_SPACE, + REF_LIST, + BUST, + IBUST, + NULL_REF, + SURJES, + SKIP +} exit_code; + +typedef enum { + SETPOSSES, + DOSMALLS, + SEARCHES, + SOLUTIONS, + BAD_TESTS, + SURJ_BACKTRACKS, + NULL_BACKTRACKS, + F_SUBSUMED, + B_SUBSUMED, + B1_SUBSUMED, + PREREFS, + REFSIZE, + TOPLESS, + LOC_MEMORY, + MEMORY, + BRANCHES, + PRE_TIME, + SEARCH_TIME, + MAX_REPORT +} report_code; + +typedef struct IL { + boolean positive; /* Represents positive literal */ + short int v, w; /* A couple of integers */ + struct IL *p; /* in a list */ +} INTLIST, *intlist; + + +typedef struct reftype { + int cardinality; /* Just what is says */ + boolean notop; /* Useless extension omitted */ + intlist cellvals; /* The cell-valuess involved */ + struct reftype *nxtr; /* Just for stack management */ +} REF, *ref; + + +typedef struct rf_list { /* A list of refutations */ + ref rf; + struct rf_list *nextref; +} REFLIST, *reflist; + + +typedef struct { + int blocks; /* How many times blocked */ + ref back; /* For calculating secondaries */ + reflist cvrefs; /* The refs involving this c-v */ + reflist posrefs; /* Positives involving this c-v */ + int vpos; /* Offset of its cell */ +} CELLVALUE, *cellvalue; + + +typedef struct { + short poss; /* How many possible values */ + short vpos; /* Vector position of this cell */ + reflist inject; /* Index to injective function */ + CELLVALUE cvals[SZ]; /* The cell-value structures */ + boolean used; /* Involved in new refutation */ + boolean has_value; /* This cell has a value */ +} CELL, *cell; + + +typedef struct { + logical valset; + int last_seen[SZ]; + intlist cellset; +} SURJ; + + +typedef struct { + logical report_type; + char s_report[80]; + int i_report; + float f_report; + void (*do_report)(); +} REPORT; + + +typedef struct cng { + int forkpoint; + int cutval; + logical comvec[V_LENGTH]; + int inorder[V_LENGTH]; + logical possvals[V_LENGTH]; + boolean active; + struct cng *lastone; +} CUTT, *cutt; + + +typedef struct { REF news[1024]; } RBATCH, *rbatch; +typedef struct { INTLIST news[1024]; } ILBATCH, *ilbatch; +typedef struct { REFLIST news[1024]; } RLBATCH, *rlbatch; + + +typedef struct { + boolean (*Test)(), /* The principal test function */ + (*Onerefs)(), /* Removal of impossible values */ + (*Smallrefs)(); /* Special treatment for smalls */ + int vlength, /* Local vector length */ + batches, /* Local stack size / 1000 */ + extra_batches, /* Batches after pretest */ + maxref, /* Maximum refutation size */ + maxbak, /* Maximum number of backtracks */ + verb, /* Statistics verbosity setting */ + ref_record[RRA][RRB], + branches[SZ]; + boolean Nosecs, /* Suppress secondary refs */ + topsoff, /* Remove redundant ref tops */ + report_breaks, /* Print reason for end search */ + forbidden[V_LENGTH], /* No backtracking */ + done; /* End-of-search status raised */ + REPORT reps[MAX_REPORT+1]; /* Search statistics */ +} TRIN, *trin; + + + +typedef struct { + trin job; + CELL vector[V_LENGTH]; + logical comvec[V_LENGTH]; + int inorder[V_LENGTH]; + int coinorder[V_LENGTH]; + int priority[V_LENGTH]; + int this_cell; + int susguy; + int firstchange; + int forkpoint; + int maxval; + int stakmax; + int unfixt; + int local_memory; + int max_batches; + boolean novect; + boolean ts; + boolean cut_recorded; + boolean noref; + rbatch stak[REF_MAX]; + ilbatch il_stak[STAK_MAX]; + rlbatch rl_stak[STAK_MAX + REF_MAX]; + intlist il_free; + reflist rl_free; + reflist duds; + ref newref; + ref stak_ptr; + SURJ surjection[SURJ_MAX]; + cutt cutptr; + cutcode cut_flag; +} TRS, *trs; + + +#ifndef CLK_TCK +extern long sysconf(); +#define CLK_TCK sysconf(3) +#endif + + +/* +* Global variables +*/ + +#ifdef TRFILE +#define TR_GLOB +#else +#define TR_GLOB extern +#endif + +TR_GLOB trs debugger; +TR_GLOB int begin_time, end_time; + + +/* +* Function prototypes +*/ + +void transref ( trin job ); +void init_trin ( trin tr ); +void clean_up ( trs T ); +void report_initial ( trs T ); +trs tr_initial ( trin job ); +void search ( trs T ); +int pick_cell ( trs T ); +int set_fork ( trs T, int rc ); +void delete_cut ( trs T ); +reflist get_rl ( trs T ); +void put_rl ( trs T, reflist dead ); +intlist get_il ( trs T, boolean b, int x, int y, intlist ip ); +void put_il ( trs T, intlist dead ); +int batches_used ( trs T ); +ref get_ref ( trs T ); +void put_ref ( trs T, ref dead, boolean flag ); +void rec_put_il ( trs T, intlist ip, ref r, boolean flag ); +void more_refs ( trs T ); +void more_rls ( trs T ); +void more_ils ( trs T ); +boolean invalue ( trs T, cell c, int v ); +void transfer_ref (trs T, cell c, ref rf); +void blockval ( trs T, cell c, int v, ref rrf, report_code RC ); +boolean surjok ( trs T ); +void outvalue ( trs T, cell c ); +void untransfer_ref (trs T, cell c, ref rf); +void unblockval ( trs T, cell c, int v, ref rrf ); +void mark_delete ( trs T, ref r ); +void delete_duds ( trs T ); +void delete_a_dud ( trs T, reflist r ); +void setref ( trs T ); +void reduceref ( trs T ); +void addref ( int x, trs T ); +void print_ref ( ref r ); +void print_sus ( trs T, cell c ); +void totsus ( trs T ); +void make_coherent ( trs T ); +void no_ref ( trs T ); +void new_small_ref ( int cells[], int vals[], boolean valency[], trs T ); +void remove_value ( trs T, cell c, int x ); +intlist get_all_ils ( int cells[], int vals[], boolean valency[], int x, trs T ); +boolean subsumed ( ref the_ref, trs T ); +boolean subsumes ( ref little_ref, ref big_ref ); +void back_sub ( trs T, ref the_ref ); +void Ref ( int x, trs T ); +void record_cut ( trs T ); +void record_part_cut ( int x, trs T ); +void complete_cut ( int ex, trs T ); +boolean prepared ( trs T ); +void setinx ( trs T ); +boolean pre_test ( trs T ); +boolean absfixt ( int x ); +void surjective ( logical vec[], logical vset, trs T ); +void injective ( logical vec[], int x, trs T ); +void co_priority ( int x, int y, trs T ); +void ref_size_report (); +void Report ( trin tr ); +void report_branches ( trin tr ); +void dump_refs ( int rows, int columns ); +void job_done ( trin t, char *s ); +int timestamp (); +void skipout ( char s[], int exitcode ); +void test_backs (trs T); diff --git a/src/wffs.c b/src/wffs.c new file mode 100644 index 0000000..7d90820 --- /dev/null +++ b/src/wffs.c @@ -0,0 +1,499 @@ +/* +* wffs.c V2.1 (May 1993) +* +* This contains the routines from MaGIC (other than the +* parser) which deal with formulas. +*/ + + + + /**************************************************************** + * * + * MaGIC 2.1 * + * * + * (C) 1993 Australian National University * + * * + * All rights reserved * + * * + * The information in this software is subject to change without * + * notice and should not be construed as a commitment by the * + * Australian National University. The Australian National Uni- * + * versity makes no representations about the suitability of * + * this software for any purpose. It is supplied "as is" without * + * express or implied warranty. If the software is modified in * + * a manner creating derivative copyright rights, appropriate * + * legends may be placed on the derivative work in addition to * + * that set forth above. * + * * + * Permission to use, copy, modify and distribute this software * + * and its documentation for any purpose and without fee is * + * hereby granted, provided that both the above copyright notice * + * and this permission notice appear in all copies and sup- * + * porting documentation, and that the name of the Australian * + * National University not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. * + * * + ****************************************************************/ + + +#include "MaGIC.h" + +#define oldsymbol(s) newsymbol(s,0,0) +#define found_symbol(s) (this_symbol(s)? true: false) + + + +/* +* This grabs, initialises and returns the symbol with the +* given string, inserting it in the first unoccupied symbol +* structure if need be. It is added to the end of the given +* symbol lists if these are non-null. +* +* If the symbol already exists, no change is made to the +* symbol lists, even if they are non-null. +*/ + +symb newsymbol( char *string, symb symbol_list1[], symb symbol_list2[] ) +{ + int i; + symb sy = 0; + + for ( i = 0; i < SYMBOLMAX; i++ ) + if ( !strcmp(theJob->symtable[i].s,string) ) + return (theJob->symtable + i); + + for ( i = 0; i < SYMBOLMAX; i++ ) + if ( !theJob->symtable[i].s[0] ) { + sy = theJob->symtable + i; + strcpy(sy->s, string); + sy->next = theJob->symtab; + sy->last = 0; + if ( theJob->symtab ) theJob->symtab->last = sy; + theJob->symtab = sy; + break; + } + if ( symbol_list1 ) { + for ( i = 0; symbol_list1[i]; i++ ) ; + symbol_list1[i] = sy; + symbol_list1[i+1] = 0; + } + if ( symbol_list2 ) { + for ( i = 0; symbol_list2[i]; i++ ) ; + symbol_list2[i] = sy; + symbol_list2[i+1] = 0; + } + return sy; +} + + + + + +/* +* Wff_initial initialises theJob->form etc. +* It is called from dialog.c as part of setting +* up a new job. +*/ + +void wff_initial() +{ + int i; + symb dummy_symbol; + + dummy_symbol = oldsymbol("no_connective"); + + for ( i = 0; i < FMAX; i++ ) { + theJob->form[i].lsub = theJob->form[i].rsub = 0; + if ( i == 0 ) theJob->form[i].sym = dummy_symbol; + else if ( i == 1 ) theJob->form[i].sym = oldsymbol("a"); + else if ( i == 2 ) theJob->form[i].sym = oldsymbol("b"); + else if ( i < VMAX ) theJob->form[i].sym = oldsymbol("."); + else if ( i < VMAX+4+CMAX ) theJob->form[i].sym = oldsymbol(" "); + else theJob->form[i].sym = 0; + } + theJob->form[VMAX].sym = oldsymbol("t"); + theJob->form[VMAX+1].sym = oldsymbol("f"); + theJob->form[VMAX+2].sym = oldsymbol("T"); + theJob->form[VMAX+3].sym = oldsymbol("F"); + + for ( i = 0; i < CMAX; i++ ) { + atom[0][i] = VMAX+4+CMAX+i; + theJob->form[atom[0][i]].sym = oldsymbol("a"); + atom[1][i] = VMAX+4+(2*CMAX)+i; + theJob->form[atom[1][i]].sym = oldsymbol("b"); + } +} + + + + + + + +/* +* Got_formula calls infml to read in a formula if possible. +* If the user asks for help this is given, otherwise the +* boolean return value is 1 for success, 0 for failure. +* +* The parameters x, y and yy are passed on to infml. The +* string s states what type of formula ("axiom", etc) is +* sought. +*/ + +boolean got_formula(int x, int y, int yy, char *s) +{ + for(;;) + switch( infml(x,y,yy) ) { + case -1: + return(0); + case 0: + return(1); + case 1: + if ( xdialog ) return(0); + printf("\n H)elp, R)epeat %s or N)either? ", s); + switch(readin("hnr")) { + case 'n': + return(0); + case 'h': + help(x+1); + break; + case 'r': + printf("\n Definition: "); + fflush(stdout); + } + } +} + + + + + +/* +* Infml calls for and processes a formula from stdin. The two +* parameters are: x, the destination (IN_CONC = axiom or +* conclusion, IN_PREM = premise, IN_BGUY = badguy, IN_DEFN = +* definition); y, the offset for cases other than x = IN_BGUY; yy, +* the second offset for cases x = IN_CONC and x = IN_PREM. +* +* Return value is 0 if successful in reading a formula and getting +* it parsed, 1 if an error is encountered, -1 if the null formula +* is input. +* +* The first action after input is to render the formula hygenic, +* checking for bad characters at the same time. A list of the +* symbols is compiled from the input string. By default, each +* string of characters surrounded by white space (or parentheses) +* is tried against the symbol table. If it is not found, initial +* segments are tried. +*/ + +int infml(input_case x, int y, int yy) +{ + int i, k, wf; + symb fml[SLEN*2]; + char longs[SLEN]; + + if ( x != IN_DEFN && !xdialog ) printf("\n Enter formula (or RETURN): "); + fflush(stdout); + fgets(answer,SLEN,stdin); + + k = 0; + while ( next_symbol(longs) ) + if ( !seek_symbol(longs,fml,&k) ) { + EP; + printf("\n Illegal input: unrecognised symbol %s\n\n ", longs); + return 1; + } + if ( !k ) return -1; + fml[k] = 0; + + if ( x == IN_DEFN ) { + if ( k == 1 ) { + EP; printf("\n ERROR: definition is too short!\n\n"); + return 1; + } + if ( theJob->adicity[y] ) { + for ( i = 0; i < k; i++ ) + if ( !strchr(fml[i]->s,'a') ) break; + if ( i == k ) { + EP; printf("\n ERROR: variable \"a\" does not "); + printf("occur in the definition\n\n"); + return 1; + } + } + if ( theJob->adicity[y] == 2 ) { + for ( i = 0; i < k; i++ ) + if ( !strchr(fml[i]->s,'b') ) break; + if ( i == k ) { + EP; printf("\n ERROR: variable \"b\" does not "); + printf("occur in the definition\n\n"); + return 1; + } + } + } + + wf = parse(fml); + if ( !wf ) return 1; + if ( x == IN_CONC ) + theJob->croot[y][yy] = wf; + else if ( x == IN_PREM ) + theJob->proot[y][yy] = wf; + else if ( x == IN_BGUY ) + theJob->failure = wf; + else { + fix_atoms(y,wf); + theJob->defcon[y] = wf; + } + return 0; +} + + + +/* +* Get the first space-delimited sequence of nonspace characters +* from answer[], truncate answer[] to the following portion and +* return true. If there is no such sequence, return false. +*/ + +boolean next_symbol( char longs[] ) +{ + int i, j; + + for ( i = 0; answer[i] && isspace(answer[i]); i++ ); + if ( !answer[i] ) return false; + j = i; + do { + longs[i-j] = answer[i]; + i++; + } + while ( answer[i-1] && !isspace(answer[i-1]) ); i--; + if ( answer[i] ) longs[i-j] = 0; + for ( j = 0; answer[j]; j++ ) answer[j] = answer[j+i]; + return true; +} + + + +/* +* String occurs in the current symbol table. +*/ + +symb this_symbol( char *string ) +{ + symb sy; + + for ( sy = theJob->symtab; sy; sy = sy->next ) + if ( !strcmp(sy->s,string) ) return sy; + return 0; +} + + + +/* +* The "long string" longs may contain exactly a meaningful symbol, +* in which case this is placed in fml[*k] and *k incremented. +* Otherwise, the longest meaningful initial substring is placed +* in fml[*k], *k incremented and the remainder of longs treated +* recursively. The value returned is true on success, false if no +* meaningful initial string is found. +*/ + +boolean seek_symbol( char longs[], symb fml[], int *k ) +{ + char shorts[64], *cp; + int i, j; + + if ( !*longs ) return true; + if ( found_symbol(longs) ) { + fml[(*k)++] = this_symbol(longs); + return true; + } + + if ( (cp = strchr(longs,'(')) && (j = cp - longs) ) { + for ( i = 0; i < j; i++ ) shorts[i] = longs[i]; + shorts[i] = 0; + if ( !seek_symbol(shorts, fml, k) ) return false; + for ( i = 0; longs[i]; i++ ) longs[i] = longs[i+j]; + } + else if ( (cp = strchr(longs,')')) && (j = cp-longs) ) { + for ( i = 0; i < j; i++ ) shorts[i] = longs[i]; + shorts[i] = 0; + if ( !seek_symbol(shorts, fml, k) ) return false; + for ( i = 0; longs[i]; i++ ) longs[i] = longs[i+j]; + } + + if ( strchr(")(",*longs) ) { + *shorts = *longs; + shorts[1] = 0; + fml[(*k)++] = oldsymbol(shorts); + return seek_symbol(longs+1, fml, k); + } + + strcpy( shorts, longs ); + for ( i = strlen(longs)-1; i; i-- ) { + shorts[i] = 0; + if ( found_symbol(shorts) ) { + fml[(*k)++] = this_symbol(shorts); + return seek_symbol(longs+i, fml, k); + } + } + + if ( isalpha(*longs) ) { + *shorts = *longs; + shorts[1] = 0; + fml[(*k)++] = oldsymbol(shorts); + return seek_symbol(longs+1, fml, k); + } + + return false; +} + + + + +/* +* Part of infml is to "fix the atoms". This means change the +* occurrences of 'a' and 'b' in the definition of defined +* connective #y to atom[0][y] and atom[1][y] respectively. +* The other parameter, dy, points to the subformula within +* which to search for 'a' and 'b'. Fix_atoms is recursive. +*/ + +void fix_atoms(int y, int wf) +{ + WFF *dy; + + if ( !wf ) return; + dy = theJob->form + wf; + + if ( dy->lsub ) { + if ( !strcmp(theJob->form[dy->lsub].sym->s,"a") ) + dy->lsub = atom[0][y]; + else if ( !strcmp(theJob->form[dy->lsub].sym->s,"b") ) + dy->lsub = atom[1][y]; + else fix_atoms(y,dy->lsub); + } + + if ( dy->rsub ) { + if ( !strcmp(theJob->form[dy->rsub].sym->s,"a") ) + dy->rsub = atom[0][y]; + else if ( !strcmp(theJob->form[dy->rsub].sym->s,"b") ) + dy->rsub = atom[1][y]; + else fix_atoms(y,dy->rsub); + } +} + + + + + +/* +* Outfml is a very simple deparser which writes the formula +* rooted at p, a subformula of that rooted at q, to stream f. +* +* Local variable c is set to p->sym just for brevity. +*/ + +void outformula(int p, int q, FILE *f, varmode vm) +{ + symb c; + + if ( p <= 0 ) skipout("Attempt to print nonsense formula",SKIP); + + c = theJob->form[p].sym; + + if ( symbol_listed(2,c) ) { + if ( p != q ) fprintf( f, "(" ); + outformula(theJob->form[p].lsub,q,f,vm); + fprintf( f, " %s ", c->s ); + outformula(theJob->form[p].rsub,q,f,vm); + if ( p != q ) fprintf( f, ")" ); + } + else if ( symbol_listed(1,c) ) { + fprintf( f, "%s", c->s ); + if ( strlen(c->s) > 1 ) fprintf( f, " " ); + outformula(theJob->form[p].rsub,q,f,vm); + } + else { + if ( p <= Vmax && vm == VALS ) + fprintf( f, "%d", theJob->form[p].val ); + else fprintf( f, "%s", c->s ); + } +} + + + +/* +* Is_in returns 1 (true) if c occurs in the formula *w +* and 0 (false) if it does not. +*/ + +boolean is_in(symb s, int w) +{ + if ( w <= 0 ) return false; + if ( theJob->form[w].sym == s ) return true; + if ( is_in( s, theJob->form[w].lsub )) return true; + return is_in(s, theJob->form[w].rsub); +} + + + + +/* +* It is sometimes necessary to purge the formula list of any +* appeal to a given symbol. This is done especially when a +* connective has just been deleted. +* +* The first move is to propagate the bad symbol up the tree: +* anything with a bad subformula is also bad. +*/ + +void purge( symb badsym ) +{ + int i, j, k, m; + + for ( k = 1; theJob->form[k].sym; k++ ) { + if ( theJob->form[theJob->form[k].lsub].sym == badsym ) + theJob->form[k].sym = badsym; + if ( theJob->form[theJob->form[k].rsub].sym == badsym ) + theJob->form[k].sym = badsym; + } + + j = 1; + for ( i = 1; theJob->form[i].sym; i++ ) + if ( theJob->form[i].sym == badsym ) { + if ( j < i ) j = i; + while ( theJob->form[++j].sym == badsym ) ; + if ( ! theJob->form[j].sym ) { + do { + theJob->form[i].sym = 0; + theJob->form[i].lsub = 0; + theJob->form[i].rsub = 0; + } + while ( ++i <= j ); + return; + } + theJob->form[i].sym = theJob->form[j].sym; + theJob->form[i].lsub = theJob->form[j].lsub; + theJob->form[i].rsub = theJob->form[j].rsub; + theJob->form[j].sym = badsym; + + for ( k = j+1; theJob->form[k].sym; k++ ) { + if ( theJob->form[k].lsub == j ) + theJob->form[k].lsub = i; + if ( theJob->form[k].rsub == j ) + theJob->form[k].rsub = i; + } + if ( theJob->failure == j ) theJob->failure = i; + for ( k = 0; theJob->croot[k][0]; k++ ) + for ( m = 0; m < RTMAX; m++ ) { + if ( theJob->proot[k][m] == j ) + theJob->proot[k][m] = i; + if ( theJob->croot[k][m] == j ) + theJob->croot[k][m] = i; + } + for ( k = 0; theJob->dcs[k]; k++ ) + if ( theJob->defcon[k] == j ) theJob->defcon[k] = i; + } +}